一、ANTLR入门

1、概念

  • ANTLR是一款强大的语法分析器生成工具,可用于读取、处理、执行和翻译结构化的文本或二进制文件。其主要由Java编写的。

    通过ANTLR可以解析代码的grammar并且生成另一种语言的parser,例如Java,c++,python等。

2、安装

(1)Java-jdk1.7(or more high)
  • 下载地址:👉

  • 环境配置:

    • JAVA_HOME ,值为jdk目录

      C:\Program Files\Java\jdk-17.0.1
      
    • CLASSPATH

      .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;
      
    • Path

      %JAVA_HOME%\bin
      %JAVA_HOME%\jre\bin
      
    • 测试,进入cmd,查询java 和javac

(2)Antlr4
  • 下载地址:👉

    (这里声明,由于antlr是java写的,因此antlr即Java的一个jar包)

  • 环境配置:

    • CLASSPATH,加入Antlr的jar文件的路径以及名字,格式例如:

      .;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;
      D:\OfficeSoftware\Antlr\antlr-4.9.2-complete.jar;
      
    • 在antlr的jar包目录下创建两个文件,antlr4.bat,内容如下:

      java org.antlr.v4.Tool %*
      
    • grun.bat,内容如下:

      java org.antlr.v4.gui.TestRig %*
      
    • 添加antlr的jar包所在目录地址到环境变量Path中

      在这里插入图片描述

    • 测试,进入cmd,分别查询antlr命令和grun命令(有输出便成功)

(3)Idea或者VScode安装插件(ANTLR4)

3、使用

(1)简单入门级语法:

实例

//语法文件以grammer开头
//Algorithm与文件名相同
grammar Algorithm ;

//init文法名  
//EOF  结束标志
init: expr* EOF;

expr: expr ('*'|'/') expr
    | expr ('+'|'-') expr
    | INT
    | '(' expr ')' ;

INT: [0-9]+ ;
WS: [ \t\r\n] ->skip; //表示跳过匹配中出现的空格,制表,回车,换行符等。
grammar xx;

文法1:
文法2:
文法...

//词法规则
词法1:
词法2:
...
  • 文件名和grammar关键字后跟的语法名应该一致。
  • 文法规则和词法规则可以同时存在一个文件中,但文法小写开头,词法大写开头。
  • 我们在构建一个语法文件时,一般是先考虑文法,再考虑词法。就像是我们造句,先会考虑句子的结构,再往里面填词。但是实际语法分析的过程是:输入流先经过词法分析器生成匹配的词法符号流,词法符号流经过语法分析器生成匹配的语法结构。
(2)通配符
  • | 表示或
  • *表示出现0次或多次
  • ?表示出现0次或1次
  • +表示出现1次或多次
  • ~表示取反
(3)编译及运行(Vscode版)
  • 编写.g4文件

  • 打开终端进入到.g4文件路径下在这里插入图片描述

  • 编译.g4文件生成Java代码文件

    antlr4 File.g4  #其中File.g4为自己创建的.g4文件
    
  • 编译生成的Java文件

    javac *.java
    
  • 使用TestRig进行调试

    #手动标准输入
    grun File file -tokens  #File为.g4文件名,file为该文件下的语法名
    #文本输入
    grun File file input.txt -tokens 
    #查看可视化语法树
    grun File file -gui
    

    扩展:

    -tokens 打印词法符号流

    -tree 以 LISP 格式打印出语法分析树

    -gui 在对话框中以可视化方式显示语法分析树

    -ps file.ps 以 PostScipt 格式生成可视化语法分析树,然后将其存储与 file.ps

    -encoding encodingname 若当前的区域设定无法正确读取输入,使用这个选项指定测试

    组件输入文件的编码。

    -trace 打印规则的名字以及进入和离开规则时的词法符号

    -diagnostics 开启解析过程中的调试信息输出。通常仅在一些汉奸情况下才使用它产生信

    息,例如输入的文本有歧义。

    -SLL 使用另外一种更快但是功能稍弱的解析策略

二、基础设计语法:

1、序列:

  • //定义一个文件规则
    grammar File;
    
    //语法规则定义
    //file文法规则中可以有多个句子(sentence),并且用 . 作为终止符
    file: (sentence '.')* ;
    
    //一个句子(sentence)中包含多个分句,用 , 作为分句的分隔符
    sentence: clause (',' clause)* ; 
    
    //一个分句(clause)中包含多个单词,并用 空格 作为单词的分隔符
    clause: words (' ' words)*;
    
    //单词中可能是字母组成的字符串,也可以是数字
    words: STRING
        |  NUMBER
        ;
    
    //词法规则定义
    //定义字符串规则,由小写字母(a-z)和大写字母(A-Z)组成
    STRING: [a-zA-Z]+;
    //定义数字规则
    NUMBER: [1-9][0-9]+   	//可以是整数
        |   [0]			  	//也可以是0
        |   [0-9]+[.][0-9]+	//也可以是浮点数
        ;
    
    WS: [ \t\r\n] ->skip ;  //跳过其中多余的空格,制表,回车,换行符。
    

    输入文件:

    I am a boy,and i like play basketball.
    This is an integer number of 690.
    There is a float number of 5.678. 
    

    编译结果:在这里插入图片描述

2、选择和嵌套:

  • /*
        选择和嵌套语法规则
     */
    
    grammar OptAndNest ;
    
    stem: expr* ;
    
    expr: ID '[' expr ']'  //选择数组 a[1] 或者嵌套 a[b[1]] 或者 a[b[1]] 
        | '(' expr ')'     //选择带括号的表达式 (1),(a[1]),(a[1])等
        | INT              //选择一个整数  1  32375
        ;
    
    ID: [a-z]+;             //定义标识符
    INT: [1-9][0-9]*        //定义整形
        | [0]
        ;
    
    WS: [ \t\r\n] ->skip ;
    

    输入文件:

    a[1] a[b[1]] (a[2]) (2) 213
    

    编译结果:

    在这里插入图片描述

3、优先和结合:

  • antlr的优先级通常为从上至下,选择分支排在上面的优先级高,并且一般为左向右结合

    /*
        antlr的优先级 结合性  以及左递归
    
     */
    
    grammar Priority ;
    
    init: expr* EOF;
    // 从左向右结合
    expr: expr ('*'|'/') expr 
        | expr ('+'|'-') expr
        | '(' expr ')' 
        | INT
        ;
    
    INT: [1-9][0-9]*
        | [0]
        ;
    WS: [ \t\r\n] ->skip ;
    

    输入文件:

    2 + 3 * 6
    2 * 3 * 6
    2 + 3 + 5
    

    效果展示:(可以从图中看出一般都是左向右结合,即优先级从左到右)

    在这里插入图片描述

  • 因此想要从右向左则需要设置命名 <assoc=right>

    //从右向左结合
    expr: <assoc=right> expr ('*'|'/') expr 
        | <assoc=right> expr ('+'|'-') expr
        | '(' expr ')' 
        | INT
        ;
    

    效果展示:

    在这里插入图片描述

  • 左递归:

    • 首先,什么叫做左递归呢? 一个左递归的语法通常有这样的形式 : A-> Aa .而自顶向下的语法分析是无法处理左递归语法的。为什么呢?无论是递归分析还是预测分析或者是LL文法分析,在碰到左递归这种语法时都会陷入死循环当中。如果我们用递归分析,那么在分析A这个非终结符号的时候就会调用functionA,functionA将A分解成A,a,然后在我们再次碰到A的时候又会调用functionA,这样便形成了无限递归。如果我们用非递归的LL文法分析,那么在我们将把A->Aa无限次地压入到栈中,即每次弹出A都会压入Aa。所以我们必须采取手段消除左递归,下面给出标准方法。

4、核心语法标记:

三、ANTLR常见错误

1、134
  • ERROR(134):  symbol char conflicts with generated code in target language or runtime
    

    原因&解决:.g4文件中文法或者词法命名与其关键字冲突,需更换命名。

2、153
  • ERROR(153):  rule row contains a closure with at least one alternative that can match an empty string
    

    原因&解决:

3、146
  • warning(146):  non-fragment lexer rule STRING can match the empty string
    

    原因&解决:.g4文件中可能会有空字符串,即需要将部分*改为+

4、119
  • # 或者 出现
    error(119): The following sets of rules are mutually left-recursive [expr]
    

    原因&解决:.g4文件中不能识别终止,主要由于首个文法 ’:‘左右都有相同标识符,例如:

    expr: expr+; // 可能会造成无限循环递归
    
5、169
  • error(169):  rule expr is left recursive but doesn't conform to a pattern ANTLR can handle
    

    原因&解决:左右两边都是相同的标识符,而且没有其他标识符可选择。如:

    expr: expr;
    

    本文章学习并参考antlr权威指南所撰写,部分图片来自于该文章


版权声明:本文为lzh623015455原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/lzh623015455/article/details/122716934