手写编译器
谈谈尾递归在变编译器中的实现
这一篇主要讲述生产式到java代码的生成过程;
何为生产式
expr -> expr + term {print('+')}
| expr - term {print('-')}
| term
term -> 0 {print('0')}
|1 {print('1')}
|2 {print('2')}
...
|9 {print('9')}
expr : 表示no ternimal (非终结符号,可以继续解析)
term : 表示no ternimal (非终结符号,可以继续解析)
0,1 … 9 : 数字(terminal 终结符号)
+ - : 操作符(terminal 终结符号)
| : 或者的意思,表示当前非终结符号可以解析的可能性
就expr 解析来说 有3种解析路劲,但是有的解析路径有可以又包含expr , 又可以继续解析,这是一个递归过程;
关于生产式的大致讲到这,总体概念是这样,想要更多细节还得深入。
生产式的解析
生产式的解析需要借助抽象语法树(Abstract and concrete tree)。比如9-5+2是符合上述生产式的,其抽象语法树
+
/ \
- 2
/ \
9 5
我们知道上述的生生产式解析其实有左递归问题的(LR),可以就看成是 A代表expr。则 A->Aa|Aß|y 就是一个左递归产常见的例子,和上面的生产式类似;
消除该左递归之后可得到(消除方法见另一篇手写编译器-消除左递归)
expr -> term rest
rest -> + term {print('-')} rest
| - term {print('-')} rest
| £
term -> 0 {print('0')}
|1 {print('1')}
|2 {print('2')}
...
|9 {print('9')}
该解析树用代码java伪代码实现
void expr(){
term();reset();
}
void rest(){
if(lookahead == '+'){
match('+');term();print('+');rest();