JAVACC使用总结(四):LOOKAHEAD解决语法选择冲突的利刃

语法选择冲突

在解释什么事语法选择冲突之前,先看一个语文相关的不太恰当的例子,这个例子对理解语法选择冲突有帮助。

特别是2,3的,他们的前3字都是一样的,但是他们的含义是不同的,2是“为” “了解” ,3是“为了” “解决”。对于人的思维来说能够快速的分辨出其中的区别,但是对计算机来说并不简单。如果能明白上面的例子,那么差不多就能理解javacc中的语法选择冲突大概是怎么一会事了。

现在就看一下javacc出现了语法选择冲突的例子。

void  parse():
{

 }
{
   assignExp()
   | functionExp()

}

void assignExp():
{
   Token t;
   double v;
}
{
   t=<ID> <EQUALS> v=calc()
   {
      //TODO 实现赋值表达式的动作
      System.out.println(t.image+"="+v);
   }

}

void functionExp():
{
  Token t;
}
{
  <ID> <LPAREN> (<ID>
                    (<COMMA> <ID>)*
                    )?
       <RPAREN>
}

在对这个jj文件执行javacc命令时候,会抛出这样的提示信息

Warning: Choice conflict involving two expansions at
         line 66, column 4 and line 67, column 6 respectively.
         A common prefix is: <ID>
         Consider using a lookahead of 2 for earlier expansion.

这提示就是告诉你,有两个选择冲突,因为他们的都是以<ID>token开头的,以及他们在语法文件的位置信息,并建议你用lookahead去处理这个冲突。也就说当javacc解析到<ID>时,有两条路可以走,但是这条路的路标和路口太相似了,javacc不知道该选着哪条路。

说白了就是javacc默认是生成LL(1) 解析器,为了保持解析性能,javacc不会自动的去处理这种需要向前探索才能确定语法分支的事情,但是它提供了lookahead让用户主动指定要如何向前探索功能,通过此功能,javacc就可以处理上面的语法选择冲突。

LOOKAHEAD

顾名思义就是展望,前瞻探索的含义。

当消费token的语法指针P在token1的位置时碰到了lookahead命令,就会派出L指针前去探查后面的token是否符号lookaheand的定义,如果符合就选择该语法分支,不符合就选择别的分支。

lookahead的构造方式如下

LOOKAHEAD ( amount, expansion, { boolean_expression } )

使用时必须至少存在三个条件中的一个。如果存在多个,则用逗号分隔。 

amount  表示要展望的token数量,当expansion存在时,而没有明确指定amount  时默认是2147483647,其他情况,如果没有在options里指定默认的lookahead的amount数量则默认是0。

expansion 表示要展望的匹配的规则表达式,默认是当前分支要扩展的规则。

boolean_expression 布尔表达式,默认是true。

例子1:

//还是以解决上面的冲突为例子
void  parse():
{

 }
{
   LOOKAHEAD(2)
   assignExp()
   | functionExp()

}

通过在第一个分支上加上了lookahead(2)解决了语法选择冲突,这个lookahead的含义是,先去展望2个token,看看这两个token是否跟  <ID> "=" 匹配,此时它等价与

LOOKAHEAD(2,<ID> <EQUALS>)

 例子2:

void TypeDeclaration() :
{}
{
  LOOKAHEAD(ClassDeclaration())
  ClassDeclaration()
|
  InterfaceDeclaration()
}

 这个lookahead的含义是,先去展望token,直到展望的token完全匹配ClassDeclaration的定义并且展望的token数小于2147483647。其实不建议这样使用lookahead,这样的效率是比较低下,应该尽可能的完善lookahead的匹配条件,例如可以改成这样

void TypeDeclaration() :
{}
{
  LOOKAHEAD(10, ( "abstract" | "final" | "public" )* "class" )
  ClassDeclaration()
|
  InterfaceDeclaration()
}

  例子3:

void BC() :
{}
{
  "b"
  [ LOOKAHEAD( { getToken(1).kind == C && getToken(2).kind != C } )
    <C:"c">
  ]
}

其含义,当消费token到“b”时,先去执行一下布尔表达式,看看是否符号布尔表达式条件,符号就往下走对应的语法分支。这个 布尔表达式是先判断一下下一个(getToken(1).kind)==c并且下下个(getToken(2).kind)!= c。

 上一篇:JAVACC使用总结(三):通过四则运算解析,初探语法分析_IT不码农的博客-CSDN博客

 下一篇: JAVACC使用总结(五):语法进阶-集合函数递归脚步_IT不码农的博客-CSDN博客

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值