# antlr4_learn **Repository Path**: jsqf/antlr4_learn ## Basic Information - **Project Name**: antlr4_learn - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-11-08 - **Last Updated**: 2021-12-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # grun ## 选项 -tokens 打印出词法符号流 -tree 以LIST格式打印出语法分析树 -gui 可视化方式显示语法分析树 -ps file.ps 以PostScript格式生成可视化语法分析树,然后保存与file.ps -encoding language 指定文件编码 -trace 打印规则的名称以及进入和离开更改规则的词法符号 -diagnostics 开启调试log -SSL 使用另外一种更快但是功能稍弱的解析策略 # antlr4 -no-visitor 不生成 vistor 代码 -visitor 生成 vistor 代码 -listener 生成 listener 代码 -no-listener 不生成 listener 代码 ## Vistor vistorStat(StatContext) vistorAssiggn(AssignContext) vistorExpr(ExprContext) vistorTerminal(TerminalNode) ## Listener 如何获取 一段 词法符号,参考 character_4/4.3 ``` mport org.antlr.v4.runtime.TokenStream; public class ExtreaInterfaceListener extends JavaBaseListener{ JavaParser parse; public ExtreaInterfaceListener(JavaParser javaParse){ this.parse = javaParse; } //监听 类 定义的 匹配 @Override public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { System.out.println(); System.out.println("interface I" + ctx.Identifier() + " {"); } //监听 类 定义的 匹配 @Override public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { System.out.println("}"); } //监听 对方法 定义的 匹配 @Override public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) { //需要从 语法分析器 获取 词法符号 TokenStream tokens = parse.getTokenStream(); String type = "void"; //返回类型 if(ctx.type() != null){ type = tokens.getText(ctx.type()); } String args = tokens.getText(ctx.formalParameters()); System.out.println("\t" + type + " " + ctx.Identifier() + args + ";"); } @Override public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) { //需要从 语法分析器 获取 词法符号 String tokens = parse.getTokenStream().getText(ctx); System.out.println(tokens); } } ``` ## 使用 antlr 产生的代码 ### 获取节点的 child 数量 ctx.getChildCount() ### 获取一些 常量 Expr1Parser.MULT 在 Parser 里面 ### 获取 常量对应变量的值 ctx.op.getType() ### 获取某个 child 的值 ctx.getChild(0) ### 当出现 e MULTI e 的时候 e(0) e(1) ### 获取 叶子节点的值 ctx.getINT().getText() INT 就是 词法了 # g4 编写 ## 可以给 备选分支 加上 标签 这样的话,就会为 备选分支 产生一个新的方法,更便于控制 当我们 使用 antlr4 -no-listener -visitor LabeledExpr.g4 之后, 可以看到 : LabeledExprVisitor 接口里面 生成了 这些 标签的 方法 LabeledExprBaseVisitor 是antlr4 自动 生成的 上面 LabeledExprVisitor 接口的默认实现类,可以供我们使用 所以可以 继承 LabeledExprBaseVisitor,然后重写 里面的方法 完成自己的业务逻辑。 可以得出,自己需要重写的方法和 自己定义对的 标签数量 是差不多的,除了一些 空白的方法。 # 书籍源代码 https://github.com/GaoGian/ANTLR-4-Resource-Code.git 或者 参考 book_source_code 文件夹下的代码 # 章节解析 ## character_1 ### 1.2 简单匹配 hello [a-z]+ 查看和了解 antlr4 自动产生的代码 ``` antlr4 Hello.g4 javac *.java grun Hello r -tokens 这个 r 指的是 上面的 r 语法,当然你也可以 换为 ID -tokens 表示 打印全部的 词法符号列表 也可以使用 -tree 打印语法分析树 也可以 -gui hello part Ctrl-D ``` ## character_3 ### 3.1 实现了一个 可以嵌套的 整形数组初始化 语法规则 编写 Test.java 实现可以在 自己的 业务代码里面 使用 antlr4 解析结果,例如打印语法解析树 ``` // javac ArrayInit*.java Test.java // java Test //{1,2} //Ctrl-D ``` ### 3.4 实现 3.1 的 可以嵌套的 整形数组初始化 可以转化为 16进制的 Unicode 编码,我们继承了默认生成的ArrayInitBaseListener, 重写自己感兴趣的方法完成自己的业务逻辑。最后编写 Translate.java 实现调用。 ## character_4 ### 4.1 实现一个简单的 + - * / 的 支持变量的语法规则 编写 ExprJoyRide.java 实现解析结果 树形展示 ### 4.1_1 4.1 的规则 是 语法和词法 规则都在一个文件里面, 这里把词法和语法规则 拆成 2个g4文件,在 语法文件里面 import 词法即可。 ### 4.2 上面的章节都是 解析过程,这个章节 实现的计算过程,并且在 g4 文件里面 加入了 标签,这样的话 antlr4 会为我们自动 产生 对应 标签的方法,供我们重写,实现业务逻辑。 这里我们使用 antlr4 的 visit 模式, 继承了 生成的 LabeledExprBaseVisitor 类,实现 自己感兴趣的方法,完成开发。 ``` antlr4 -no-listener -visitor LabeledExpr.g4 javac Clc.java LabeledExpr*.java java Clc t.expr ``` 需要注意的编程方法: 如果需要计算子节点的值,则需要 使用 visit(ctx.语法名称()) 获取 词法的值 使用 ctx.词法名称().getText() 如果一个 便签里面 存在 多个一样的 语法 ,则类似 数组的取法 visit(ctx.语法名称(0)) visit(ctx.语法名称(1)) 如果获取 定义的常量则 存在于 生成的 XParse 类里面,获取这个常量op的方法则是 ctx.op.getType() 最后,自己实现了 clear 语法,并且在 自己继承的 vistor 里面 重写了 clear 逻辑。 ### 4.3 完成 提取 java 类 为 接口的 语法规则 继承 listener 重写 感兴趣的方法 实现了 自己的 业务。 ### 4.4 定制语法分析过程 我们可以将代码片段 嵌入到 g4 语法中 #### 4.4.1 在语法中 嵌入任意动作 在 g4语法中 嵌套 代码 ,解析 TAB 分割的 文本,实现 抽取 某一列的 值 #### 4.4.2 使用语义判定改变语法分析过程 根据 第一个 数字,抽取 这个数字后面的 这个数字个数的 数据 作为 一组,一次类推 ``` antlr4 -no-listener Data.g4 javac Data*.java grun Data file -tree t.data grun Data file -gui t.data ``` ### 4.5 词法分析特性 #### 4.5.1 孤岛语法-处理相同文件中的不同格式 就是 一种语言 里面 又使用了 其他的 语言。 这里 只是 使用了 词法分析 ``` antlr4 XmlLexer.g4 javac Xml*.java grun Xml tokens -tokens t.xml ``` #### 4.5.2 重写输入流 修改 java 代码 并且插入 java.io.Serializable 这里有一个新特性,隐藏通道 ``` COMMENT : '/*' .*? '*/' -> channel(HIDDEN) // match anything between /* and */ ; WS : [ \r\t\u000C\n]+ -> channel(HIDDEN) ; LINE_COMMENT : '//' ~[\r\n]* '\r'? '\n' -> channel(HIDDEN) ; ``` 这样的话,词法分析器 处理之后, 仍然保留这些 词法符号,只是 不会 提供给 语法分析器。 ## character_5 设计语法 ### antlr 常见的 语言模式 1. 序列模式 csv antlr g4: ``` file : (row '\n')* ; row : field (, field)* ; field : INT ; INT : [0-9]+; ``` 2. 选择模式 csv antlr g4: ``` file : (row '\n')* ; row : field (, field)* ; field : INT | STRING ; INT : [0-9]+; STRING : [a-z]+; ``` 3. 词法符号依赖模式 ``` vector : '[' INT+ "]"; //[1], [1 2], [1 2 3] ... expr : expr '(' exprList? ')' //类似于 f(), f(x), f(x, y) | expr '[' expr ']' //类似于 a[b[c]] a[i] a[i][j] ``` 4. 嵌套模式 ``` stat : 'while' '(' expr ')' stat | '{' stat '}' ... ; expr : ID '[' expr ']' //a[1] a[b[1]] a[2*b[1]] | '(' expr ')' //(1) (a[1]) (2*a[1]) | INT // 1 999 ; INT : [0-9]+ ; ``` ### 处理优先级、左递归和结合性 1. 在 antlr 语法中,备选分支 越靠前面,优先权越高,所以 + - * / 规则里面,可以把 * / 的备选分支 放在前面 就解决了 优先权问题 ``` expr : expr ('*' | '/') expr | expr ('+' | '-') expr | INT | ID | '(' expr ')' ; ``` 2. antlr 的 结合性 都是 从左向右 结合,但是可以 使用 assoc = right 手动指定 从右到左 结合性 ``` expr : expr '^' expr //运算符是右结合的 2^(3^4) | INT ; ``` 最终,但是主要 还是不能处理 间接左递归。 ``` expr : expr '^' expr //运算符是右结合的 2^(3^4) | expr ('*' | '/') expr | expr ('+' | '-') expr | INT ``` ## character_6 探索真实的语法世界 ### 解析CSV文件 可以识别 标题行,可以识别 空行 ### 解析 JSON ### 解析 DOT 语言 ### 解析 Cymbol 语言 antlr4 Cymbol.g4 javac Cymbol*.java grun Cymbol file -gui < t.cymbol ### 解析 R语言 ## character_7 将语法和程序的逻辑代码解耦 监听器可以对特定的规则的进入和退出事件做出响应。 访问器可以控制语法分析树的遍历过程。 ### 从内嵌动作到监听器的演进 7.1 内嵌代码只能继承 PropertyFileParser 类,复写 自己定义的方法,实现自己的业务逻辑。 ### 使用语法分析数监听器编写程序 7.2 ### 使用访问器编写程序 7.3 antlr4 -visitor PropertyFile.g4 ### 标记备选分支以获取精确的事件方法 7.4 ### 在事件方法中共享信息 7.5 #### 使用访问器遍历语法分析树 7.5 #### 使用栈来模拟返回值 监听器的方法都是 void 类型的,没有办法返回结果,只能将局部结果保存在 成员变量中。 7.5_1 #### 标注语法分析树 通过规则参数和返回值为节点添加字段 7.5_2 ## character_8 构建真实的语言类应用程序 ### 8.1 加载csv数据 ### 8.2 将 json 翻译为 xml ### 8.3 生成调用图 ### 8.4 验证程序中符号的使用 ## character_9 错误报告和恢复 ### 9.1 错误处理入门