自制编译器 青木峰郎 笔记 Ch5 基于JavaCC的解析器描述

5。1 基于EBNF语法的描述

种类 含义 e.g
终端符 token , , '=', "Var"
非终端符 语法树非叶节点 stmt(), expr()

可以省略的元素用[]括起: e.g: storage() typeref() name ["=" expr()] ";"

5.2 语法二义性和token的超前扫描

语法二义性可能由语言定义本身也可能由choice conflict产生。

Choice Conflict

如果出现了choice conflict,应该试着先提取共同部分,如果不行,那么就需要LOOKAHEAD。

LookAhead

  1. 可以使用Lookahead(storage() type() ";")这样的语法预读不确定数目的token,javacc会读取直到该规则完全匹配/不符合该规则为止。
  2. 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

直接用编程语言来描述语法,解析器生成器是单纯的程序库,无需导入额外的工具生成代码

原文地址:https://www.cnblogs.com/xuesu/p/14378307.html