Syntax-Directed Translation
error handling
编译器的两个作用:
- 检测无效的程序;
- 翻译有效的程序;
error handler的作用:
准确报告错误;
迅速从错误回复;
不影响有效部分的编译速度;
panic mode:
考虑这样的错误:(1++2)+3
panic mode的测量是:跳过,到达下一个整数,然后继续;
- error productions
分辨出已知的语法错误
error correction:尝试去找出正确的接近的程序,对tokens进行插入和删除;
Abstract Syntax Tree
编译器需要一个数据结构去代表那个程序。
考虑grammar:E->int|(E)|E+E,字符串为5+(2+3),lexical analysis之后变成int'+'('int'+'int')',然后parse tree为
然后构造AST:
强调嵌套结构,更加抽象并且没有non-terminals
Recursive Descent Parsing
也叫top-down parsing,则parse tree是从上到下,从左到右去构建。
考虑grammar:
E->T|T+E T->int | int * T | (E)
token stream为:(int),匹配5个括号
在构造过程中,按照从上到下从左到右一个一个比对,找到正确的parse tree
Recursive Descent Algorithm
首先来看一些特别的tokens:INT,OPEN,CLOSE,PLUS,TIMES,全局的next指向下一个的输入token
定义个函数来检测匹配与否:
对于给定的终止符:**bool term(TOKEN tok){return *next++==tok;}**
S中的第n个production:bool Sn(){...}
尝试所有的production:bool S(){...}
对于production:E->T: bool E1(){return T();}
对于production:E->T+E: bool E2(){return T()&&term(PLUS)&&E();}
对于所有的productions of E(带回溯的情况):
1 | bool E(){ |
- non-terminal T:
bool T1(){return term(INT);}
bool T2(){return term(INT)&&term(TIMES)&&T();}
bool T3(){return term(OPEN)&&E()&&term(CLOSE);}
1 | bool T(){ |
如何开始parser:
- 初始化next指针
- 调用E()
接下来就可以按照从上到下,从左到右进行parse,如果mismatch就进行回溯。
Left Recursion
- 考虑这样的production:S->Sa
1 | bool S1(){return S()&&term(a);} |
在这种情况下,S()进入了无限递归;
对于一个grammar,若存在P经过一次或者多次的递归,即推导出以P开头的式子,则称之为左递归。
elimination of left recursion
考虑这样的grammar:A→Aa1|Aa2……|Aan|b1|b2……|bm
可以改写成:
A→b1A’|b2A’……|bmA’
A’ →a1A’|a2A’……|anA’|ε