目前我们的解析器已经初具规模了。我们已经实现了数字和字符串的解析,实现了空白符和注释的跳过。在这篇文章中,我们继续添加 语句 的解析。
语句是表示某种操作的独立代码单元。在很多编程语言中,语句以分号结束,我们也采用这种方式。
程序代码由多条语句组成。所以如代码清单 1 所示,我们修改程序的文法定义,现在程序由 StatementList 组成。
如代码清单 2 所示,StatementList 的文法定义是左递归形式的,不断用“StatementList Statement”替换“StatementList”,可以看到 StatementList 就是由多条 Statement 组成的。
理论化的左递归转右递归,看着过于繁杂。实际中的概念非常简单。
如代码清单 3 所示,目前语句只包含表达式语句。
如代码清单 4 所示,表达式语句的文法是表达式,后接分号。所以我们此时需要“吃掉”一个分号并移进到下一个 token。
如代码清单 5 所示,目前表达式就是之前实现的字面量。
现在分号已经需要具有 token 含义了,所以如代码清单 5 所示,我们添加分号 token 解析(第 14 行)。
增加测试用例
我们引入 TDD(Test-Driven Development)开发模式,即测试驱动开发。在此之前,我们需要把目前已经实现了的功能,补上测试用例。
如代码清单 7 所示,我们指定字面量的测试用例。
顺带学 JavaScript:
这边导出的是箭头函数,基本语法为 (parameters) => { // 函数体 };。如果只有一个参数,可以省略参数周围的括号。
箭头函数可以按 C++ 的 lambda 函数理解。此处传入的 test 参数,可以当成函数指针理解。
代码清单 8 是本节语句解析的测试用例。
最后,我们来看如何使用测试用例。如代码清单 9 所示,我们首先定义需要传递的“基础”测试函数 test,它解析 input 字符串,将得到的 ast 和 expected 进行比较,如果不一致就代表测试失败。
我们将多个测试函数导出,存放在 tests 数组,并用 forEach 方法依次遍历调用。