构建词法分析器/扫描器时,如何使用面向对象的设计范例?

时间:2018-07-06 17:20:57

标签: oop architecture smalltalk lexer

我正在尝试设计一个小型正则表达式引擎(在Smalltalk中),以帮助增强我的面向对象技能。

我已经用其他语言(和Smalltalk本机语言)扩展了一些正则表达式引擎,它们似乎都具有一个单一的“ Lexer”或“ Scanner”类。这使我感到困惑,因为我将词法分析器视为一个单独的函数,该函数应将模式作为输入(可能还有定义语法类型的“语法”对象),并返回一个标记流。我在弄清楚该接口将具有哪些附加功能以及该对象需要保留哪些附加状态时遇到了麻烦。

如何将其分解为面向对象的设计? 我应该补充一点,当我阅读源代码时,似乎会看到很多东西:在其名称末尾添加了动词+“ er”的类。像“清洁代码”和“代码完成”之类的书讲授的“正确的面向对象设计”似乎背道而驰。

2 个答案:

答案 0 :(得分:3)

这是一个非常广泛的问题,因此我们只能给您同样广泛的答案。这是我的。

在考虑面向对象时,我们试图在数据和行为之间建立明确的区分。一般来说,对象既具有行为又具有独立于数据的能力。

这些基本指导原则并不总是使设计乍看上去看起来很自然。原因是有时我们倾向于将与某些数据相关的行为附加到完全相同的数据上。这可能会隐藏应该具有这种行为的实际对象。

这种现象的典型情况是算法。我们有一些数据:算法输入和一些行为:算法输出,并认为该行为应附加到数据上。因此,我们努力将其实现为所述数据的功能

但是,在大多数情况下,这种简单的方法是有问题的。例如,许多算法可能会产生多个输出。以两个多项式的除法为例,其中输出是商和余数。即使该算法产生单个输出,也可能会发生这样的情况:我们想问它花了多长时间,甚至告诉它取消执行并停止自身,等等。

由于这些原因(以及其他同类原因),始终建议将算法视为对象而不是函数。此类对象的实例变量通常会引用

email

将算法作为对象来实现将有助于添加以下功能:

  1. 使用不同的输入重用同一实例
  2. 有一个存放输出的地方
  3. 向用户提供反馈
  4. 告诉算法取消并停止执行(通过将其状态设置为url := "https://cdn.recast.ai/newsletter/city-01.png" httpimg.Register(pdf, url, "") pdf.Image(url, 15, 15, 267, 0, false, "", 0, "")
  5. 计算迭代次数并注册其他内容以进行调试

如您所见,作为对象的算法比作为函数的算法要丰富得多。这并不意味着您将不得不放弃功能性方法。只需对算法进行验证,然后让您的客户对象提供一个将使用该算法并返回与该上下文相关的结果的函数。

答案 1 :(得分:3)

从名称Lexer / Scanner / Parser / Compiler / ...可以明显看出,我们正在对函数进行正则化。但是请注意,在Smalltalk中,没有任何功能。只是对象和消息。

想到的显而易见的对象当然是

  • 无论什么节点组成您要生成的树状结构,
  • 如果您想拥有一个中间词法分析器,请使用令牌
  • 语法本身(更高级)

但是,字符流到树状结构的转换将在哪里发生?

在这种情况下,Leandro的回答反映了一些常见的做法。可以针对错误处理,交互式展示和错误纠正等方面对算法进行优化,但是可能会导致或多或少的整体式实现必须明确地处理自动机的状态,而这并没有那么吸引人功能语言爱好者...

首先尝试一下,但是一旦取得足够的进展,我强烈建议您学习PetitParser,在这里https://www.lukas-renggli.ch/blog/petitparser-1进行介绍。您将看到语法本身,或者更确切地说是构成语法的节点,可以充当解析器,从而分解为基本,简单,优雅和可组合的对象。