我的代码需要多少抽象才能进行适当的测试



我目前正在开发一个语言解释器,更具体地说是该语言的数据模型,我的数据模型应该能够理解表达式并解释它们,例如:"cos(a+4(";应该被解释为变量a加4中值的余弦,我将保留这个例子来解释它的工作原理。

我选择将表达式解释分解为多个步骤:

  1. 从表达式创建令牌列表,例如:cos(a + 4)变为[ "cos", "(", "a", "+", "4" ]
  2. 将数学运算符转换为函数,例如:[ "cos", "(", "a", "+", "4" ]变为[ "cos", "(", "sum", "(", "a", ",", "4", ")" , ")" ]
  3. 使用以下规则从该令牌列表创建解析树:如果左括号向下,如果右括号向上,如果逗号向右,则将当前令牌写入当前节点,例如:[ "cos", "(", "sum", "(", "a", ",", "4", ")", ")" ]将成为此树:
cos

sum
/ 
a   4
  1. 使用解释树的递归函数

一切都很好,但我希望我的代码经过良好的测试,我想知道应该如何分离程序的类。

现在我把这个逻辑分为两个类,一个解析器执行前三个步骤,另一个解释器执行最后一个步骤。

我是否应该将我的解析器分解为一个标记器、一个运算符转换器和一个树生成器,以便将每个功能很好地分离,并对这些类中的每个进行独立测试,而不是让一个包含3个独立函数的解析器类作为解析器进行测试?

这取决于实际的代码和您的个人偏好。将解析器拆分为几个阶段或模块确实可以使测试和维护更容易。模块化在多大程度上对你有意义,除了你之外,任何人都无法回答。如果您选择了模块的细粒度,结果发现它太粗或太细粒度,您仍然可以稍后进行拆分或连接。

我目前正在开发一个语言解释器。

然后你应该读一些书,比如龙之书,Queinnec的小片段Lisp、Fowler的Domain Specific Languages和Pitrat的《人造人》、《有意识机器的良心》以及Parr的《最终ANTLR 4参考》和《垃圾收集手册》。

读过几本书后,可以考虑使用GNUbison和ANTLR等语法分析器生成工具。还可以从开源软件的源代码中寻找灵感,如ninja、fish、RefPerSys、FLTK、Bismon静态分析器、Frama-C静态分析器。

有关RefPerSys项目的问题,请通过电子邮件basile@starynkevitch.net与我联系

有关Bismon或Frama-C的问题,请通过电子邮件basile.starynkevitch@cea.fr与我联系

还可以考虑阅读ACM SIGPLAN会议记录。

使用递归函数来解释树

有时,该函数可能是尾部递归的(像GCC这样的优秀优化C++编译器可以将其编译为尾部调用(。另一种方法是至少用书面英语指定一些字节码(就像Ocaml解释器一样(,并将您的语言翻译成该字节码,然后编写您的字节码解释器。

一旦你有了一些有缺陷的解释器,为它编写测试用例就是一件工作。研究GNUbash的源代码以获得灵感。

最新更新