5。1 基于EBNF语法的描述
种类 | 含义 | e.g |
---|---|---|
终端符 | token | |
非终端符 | 语法树非叶节点 | stmt(), expr() |
可以省略的元素用[]括起: e.g: storage() typeref() name ["=" expr()] ";"
5.2 语法二义性和token的超前扫描
语法二义性可能由语言定义本身也可能由choice conflict产生。
Choice Conflict
如果出现了choice conflict,应该试着先提取共同部分,如果不行,那么就需要LOOKAHEAD。
LookAhead
- 可以使用Lookahead(storage() type()
";")这样的语法预读不确定数目的token,javacc会读取直到该规则完全匹配/不符合该规则为止。 - javacc不会再对加了lookahead的地方做任何检查,所以需要编写者确保lookahead正确
省略与冲突
如果是语义本身的二义性,例如悬空else
if(x) if(y){f();}else{g();}
有时也可以采用LOOKAHEAD来解决。
比如我们规定else属于最内侧的if。
那么对于规则
if_stmt(): {}
{
<IF> "(" expr() ")" stmt() {<ELSE> stmt();}
}
还是会在是否省略上产生矛盾。
使用LOOKAHEAD改为:
if_stmt(): {}
{
<IF> "(" expr() ")" stmt() {LOOKAHEAD(1) <ELSE> stmt();}
}
重复和冲突
下列规则会导致可变长参数"," "..."不被解析,因为每次javacc都只读一个token,所以会优先匹配[",", type()]并发现无法匹配。
param_decls(): {}
{
type() ("," type())* ["," "..."]
}
使用LOOKAHEAD后:
param_decls(): {}
{
type() (LOOKAHEAD(2) "," type())* ["," "..."]
}
Packrat
支持无限的超前扫描,无需区分扫描器和解析器,语法无二义性
parser combinator
直接用编程语言来描述语法,解析器生成器是单纯的程序库,无需导入额外的工具生成代码