写在之前

之前我们已经通过两个例子尝试着解决过歧义性的问题:

  1. 运算符优先性

    我们通过语法分析器优先匹配靠前的规则这一准则,将乘法设置在加法规则之前,来解决这个问题。

    但问题是,这种优先性的歧义是在语法分析树遍历时产生的,如果我们在词法分析或者语法分析过程就遇到歧义的规则呢?

  2. 词法分析时的歧义

    在Keywords.g4中,我们设置了IF、WHILE等多个关键字。但在词法分析过程中,它们会与ID:[a-zA-z] 这种常见的标识符词法规则产生歧义。后来我们通过在ID规则的内嵌动作中多添加了一次判定,判定ID是否为关键字,是的话将ID类型换成对应的关键字类型,再进行文法规则匹配。

    ID:   [a-zA-Z]+ {if(keywords.containsKey(getText())){setType(keywords.get(getText()));}};

词法分析时的歧义改进版

在之前的词法分析歧义中,我们通过在词法分析器中加入map< 关键字,Token类型 >的方式,在ID的词法分析中检测有无

已存的关键字,并将该关键字的类型从ID转换成关键字Token类型。

T__0=1
T__1=2
ID=3
CHAR=4
INT=5
WS=6
BEGIN=7
END=8
IF=9
THEN=10
WHILE=11
'='=1
';'=2

我们可以看到,由于BEGIN、END、IF、THEN、WHILE是通过tokens{}的方式添加的,因此在显式的词法规则之后。而=;是在文法规则中隐式添加的,其类型排在最前面。

接下来我们将介绍如何去掉这些内嵌动作和预成员,直接通过隐式的先后顺序解决词法歧义性。

预想效果

来看一下一个让人头疼的输入吧:

if if then call call;

我们想让第一个if匹配成关键字,第二个if匹配成标识符,第一个call匹配成关键字,第二个call匹配成标识符。

语法文件

让我们来看看做了什么。

if then call 直接在文法规则stat中出现了,这意味着它们隐式地生成了token。让我们看看tokens表:

T__0=1
T__1=2
T__2=3
T__3=4
ID=5
WS=6
'if'=1
'then'=2
'call'=3
';'=4

果然它们的类型在显式词法规则之前了。接下去我们观察id,它包含了3个隐式token和1个词法规则。

id  :   'if' | 'call' | 'then' | ID ;

这意味着“if”"then"“call”从ID规则中剥离了出来,lexer会把它们单独的作为token类型来看,而且还在ID标识符token类型之前。

由于ANTLR4的分析顺序为 字符输入流->词法分析->tokens流->语法分析,这几个关键字token类型会提前被匹配。

grammar IDKeyword;prog: stat+ ;stat: 'if' expr 'then' stat| 'call' id ';'| ';';expr: id ;id  :   'if' | 'call' | 'then' | ID ;ID : [a-z]+ ;
WS : [ \r\n]+ -> skip ;

运行结果

语法分析时的歧义

接下来我们通过一个C++中简单的例子来演示如何解决语法分析时的歧义。

语法文件

我们可以发现,decl规则中的第二条分支和expr规则中的第三条分支会造成歧义:

比如输入T(i),它会被匹配成T类型的变量声明,也会被匹配成T函数调用(i作为实参)。

我们可以通过ANTL4的优先匹配靠前规则的机制,使得这种歧义总是调用decl规则。

但有时我们也可以通过自定义的方法来精准匹配。

grammar CppStat;stat:   decl ';'  {System.out.println("decl "+$decl.text);}|   expr ';'  {System.out.println("expr "+$expr.text);};decl:   ID ID           // E.g., "Point p"|   ID '(' ID ')'   // E.g., "Point (p)", same as ID ID;expr:   INT             // integer literal|   ID              // identifier|   ID '(' expr ')' // function call;ID  :   [a-zA-Z]+ ;
INT :   [0-9]+ ;
WS  :   [ \t\n\r]+ -> skip ;

自定义布尔判断动作

我们的想法很简单:在进行到decl、expr有歧义的分支之前,判断它们第一个ID的内容的实际类型。如果是我们预设的类型,就当作是变量类型,不然就当成函数名。

需要注意的几点:

  1. 由于是语法分析过程中的歧义,我们只在parser中引进member。
  2. 我们需要在具体的歧义分支中找到产生歧义的点,并在前面通过{}?的方式,引进一个布尔类型的值(或者返回布尔类型的方法)。
grammar PredCppStat;@parser::header{import java.util.*;
}@parser::members{Set<String> types=new HashSet<String>();{types.add("T");}boolean isType(){return types.contains(getCurrentToken().getText());}
}
stat:   decl ';'  {System.out.println("decl "+$decl.text);}|   expr ';'  {System.out.println("expr "+$expr.text);};decl:   ID ID           // E.g., "Point p"|   {isType()}? ID '(' ID ')'   // E.g., "Point (p)", same as ID ID;expr:   INT             // integer literal|   ID              // identifier|   {!isType()}?    ID '(' expr ')' // function call;ID  :   [a-zA-Z]+ ;
INT :   [0-9]+ ;
WS  :   [ \t\n\r]+ -> skip ;

运行结果

我们首先输入:

T(i);

语法分析器判断出来是T类型变量的声明。

再输入:

f(i);

由于f并不在我们预设的变量类型里,因此语法分析器判定它是一个函数T的调用。

ANTLR4(十三)解决歧义性总结相关推荐

  1. Spring学习之旅(二):Bean的高级装配之解决装配歧义性

    一:装配的歧义性: 发生原因:装配的接口有多个实现,例如:FirstClass,SecondClass,ThirdClass皆实现了接口SupperClass,当装配SupperCLass时就会出现歧 ...

  2. 中国人民大学张静:知识图谱融合中歧义性与异质性问题的讨论

    ⬆⬆⬆ 点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入! 2020 年 9 月 25 日,在由中国科协主办,清华大学计算机科学与技术系.AI TIME 论道承办的<2020 中国科 ...

  3. Spring实战之bean重复、指定bean的名字、消除bean的歧义性

    Spring实战之bean重复.指定bean的名字.消除bean的歧义性 自动装配的歧义性示例 解决方案 @Primary标示首选bean @Primary注解与@Component注解配合使用 @P ...

  4. spring自动装配的歧义性

    错误提示:Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bea ...

  5. Spring框架(二) ---- bean的歧义性

    自动装配bean时,如果符合条件的bean超过一个,就会出现歧义性,抛出NoUniqueBeanDefinitionException异常,有如下两种方法保证bean的唯一性: 一.使用@Primar ...

  6. Spring实战(六)自动装配的歧义性

    1.Spring进行自动装配时碰到的bean歧义性问题. 在进行自动装配时,只有仅有一个bean匹配所需结果时,才是可行的. 如果不仅仅一个bean能够匹配结果,例如一个接口有多个实现,这种歧义性会阻 ...

  7. 【计算理论】上下文无关语法 CFG ( CFG 设计示例 | CFG 歧义性 | Chomsky 范式 | 上下文无关语法 转为 Chomsky 范式 )

    文章目录 一.上下文无关语法 设计 示例 二.上下文无关语法 的歧义性 三.Chomsky 范式 四.上下文无关语法 转为 Chomsky 范式 五.上下文无关语法 转为 Chomsky 范式 示例 ...

  8. RN系列之五十三解决Android上图片圆角的终级解决方案

    RN上Android画图角有问题,主要存在几种情况 1.随机性,在页面有多图的情况下,偶现有的图需要画圆角的没有画圆角,有的不需要画圆角的确画了圆角 2.画圆角的不规则性,有时候想画10px的圆角,结 ...

  9. Spring实战(第四版)读书笔记08——处理自动装配的歧义性

    1.标示首选的bean 组件扫描方式例子: @Component @Primary public class IceCream implements Dessert {...} Java配置例子: @ ...

最新文章

  1. Java运行作业控制语言_Java安全——语言本身的设计
  2. 关于scrollTop为0以及解决方法
  3. react 逆地理 高德地图_高德地图又出逆天黑科技!全国各大城市模型直接获取...
  4. CSS学习-网页导航栏
  5. MATLAB 优化程序【profile简明用法】
  6. c++ ——第一个MFC界面
  7. 利用Tushare合成期货主力连续数据
  8. VideoEdit+ User Manual
  9. 随着员工转为远程办公,Diligent在所有董事会管理平台中提供无缝视频会议接入,确保安全的虚拟董事会议
  10. 专家:苹果手机换电池对系统速度几乎没影响
  11. Rstudio与R的绑定和更新
  12. 百万调音师—Audition多轨编辑
  13. Java 利用Graphics2D 合并图片(png格式可设置透明)
  14. python3.8与pyinstaller_pyinstaller 3.5 在python 3.8 环境下出现不兼容的问题
  15. 16g电脑内存有什么好处_电脑内存4G/8G/16G有什么区别?
  16. 计算机维修工试题及答案,计算机维修工初级工试题和参考答案
  17. vue官网学习笔记(九)组件基础
  18. java o2o_全渠道java b2b b2c o2o平台
  19. 设计模式之-适配器模式
  20. 清除系统LJ-批处理.bat 源代码

热门文章

  1. 另类的手机壁纸!壁纸颜色像变色龙一样随环境变化而变色!好喜欢!
  2. 在亚洲最HOT的地方做最IN的事
  3. 文字html广告,文字链接广告
  4. mysql 自定义函数验证证件号码
  5. Flink事件时间、水印以及迟到数据处理的个人理解
  6. Java反射——Type接口详解
  7. 解决excel导入数据存在公式的问题
  8. 硬币找零问题的动态规划实现
  9. xrdp在ylmf下的问题
  10. python中正切函数_Python tan() 函数