自动机与表达式折行语法分析
概述
在分析折行实现前, 先说说对基本的算术结构的分析.自动机基类
grammar::AutomationBase
(grammar/automation-base.h: 30) 能够处理所有没有在 Yacc 语法规则中给出的细节, 比如将因子与算符组成算式, 分析括号配对, 还有对逗号, 冒号等分隔符的处理.自动机对可以处理的项目作了基本的分类, 对应于其中各名为
pushXxx
的函数和 matchClosing
函数. 而 matchClosing
函数相当于将 pushCloseParen
, pushCloseBracket
等三种不同的结束括号合并成一个函数了, 因为大部分自动机自身不处理三种结束括号, 而将它们派发给其它的自动机处理.什么, 自动机还有很多种? 没错, 在 Stekinscript 语法模块设计中, 用来分析表达式的自动机有多种, 不同的自动机互相利用使得代码复杂度能够大大降低. 举个例子, 算术运算自动机
grammar::ArithAutomation
(grammar/expr-automations.h: 9) 本身不处理大括号配对, 但表达式里面终归是可以出现大括号裹起来的字典的; 当这种情况发生时, 算术自动机会创建一个字典识别自动机 grammar::DictAutomation
(grammar/expr-automations.h: 132), 让它顶替自己处理接下来的部分; 而这个字典自动机自己又是很懒的, 它才不会自己动手去识别用表达式描述的字典的键跟值, 怎么办呢? 与是它再新建一些算术自动机来识别表达式, 而自己只坐等逗号冒号等分隔符, 或者花括号结束.无论哪一种自动机, 都只会对特定的一些种类的
Token
感兴趣, 而基类 grammar::AutomationBase
对任何项目的实现都是一句话 (grammar/automation-base.cpp: 58-97)error::unexpectedToken(pos, image);
Token
并报错. 除了这种粗暴的对待方式, 大致上来说每个函数还有如下方式对待 Token
- 接受并记录, 比如
ArithAutomation
对象在正确的时候遇到运算符或者因子 - 接受并丢弃, 这个说法有点语死早的感觉, 例子就是上面
ArithAutomation
与DictAutomation
的故事, 在ArithAutomation
对象遇到开始的大括号时, 它会创建一个DictAutomation
对象, 然后丢弃大括号 (留着也没用) - 将自身归约并移交, 比如刚才提到,
DictAutomation
会委托ArithAutomation
为它识别键值表达式, 那么此时如果ArithAutomation
遇到一个冒号, 那么它自身不会处理, 而是首先将自己归约成一个表达式grammar::Expression
(grammar/node-base.h: 24) 对象, 并传递给它的委托者, 然后将这个冒号也一并移交给其委托者处理 - 将自身归约并丢弃, 比如
DictAutomation
遇到结束的大括号时, 它会将自己归约成一个字典grammar::Dictionary
(grammar/expr-nodes.h: 228) 对象, 完成使命, 然后弃掉结束大括号