Antlr4: 修改语法规则更接近普通BNF格式

简介: 为"圈2"语言, 修改语法定义规则接近BNF格式. Modify grammar definition of pretotype programming language quan2 to make it closer to BNF format.

经 @沈默 在上文Antlr4添加中文变量赋求值,括号,各种问题评论中指出, 语法规则描述依赖于Antlr4生成的语法分析器的默认分析方法, 比如运算符的左联系, 以及优先级处理等等. 于是将语法修改为下面(源码版本号: program-in-chinese/quan5):

表达式: 求积表达式 (('+'|'-') 求积表达式)*;

求积表达式: 最小表达式 (('*'|'/'|'×'|'÷') 最小表达式)*;

最小表达式
  : 字面量
  | '(' 表达式 ')'
  ;

字面量
  : T数
  | T变量名
  ;

这样做的结果是, Antlr4会分析生成一个多叉树. 比如 1+2-3, 生成树如下:
2018_01_14_antlr_quan2_bnf

于是在生成抽象语法树时手动转换为二叉树. 代码如下:

private 节点 构建二叉树(List<ParseTree> 子节点) {
    if (子节点.isEmpty()) {
      return null;
    } else if (子节点.size() == 1) {
      return visit(子节点.get(0));
    } else {
      ParseTree 最后运算符节点 = 子节点.get(子节点.size() - 2);
      运算符号 运算符 = ((TerminalNodeImpl)最后运算符节点).symbol.getType() == 圈5Parser.T加 ? 运算符号.加 : 运算符号.減;
      运算式节点 节点 = new 运算式节点();
      节点.运算符 = 运算符;
      节点.左子节点 = 构建二叉树(子节点.subList(0, 子节点.size() - 2));
      节点.右子节点 = visit(子节点.get(子节点.size() - 1));
      return 节点;
    }
  }

开发过程中发现一些坑(如果是小白错误请指出). 一个比较费解的是, 不能省去"字面量"规则如下:

最小表达式
  : T数
  | T变量名
  | '(' 表达式 ')'
  ;

不然生成的分析器会有编译错误:

com/中文编程/圈5/分析器/圈5Parser.java:403: error: unreachable statement
                enterOuterAlt(_localctx, 3);

Antlr4有个github库汇集了社区维护的各种语言的语法规则文件, 其中有Java8, 根据注释说明它的语法规则描述"极度"接近Java标准, 于是参考了它的实现. 其中看到这样的模式(已转成中文):

求和表达式
    :    求积表达式
    |    求和表达式 '+' 求积表达式
    |    求和表达式 '-' 求积表达式
    ;

求积表达式
    :    最小表达式
    |    求积表达式 '*' 最小表达式
    |    求积表达式 '/' 最小表达式
    ;

感觉这样会让语法树转换这一步的实现更加方便(应该可以省去多叉树转换成二叉树的那个递归算法). 在添加新功能之前, 打算尝试修改成这样.

已完成:

表达式
  : 求积表达式
  | 表达式 '+' 求积表达式
  | 表达式 '-' 求积表达式;

求积表达式
  : 最小表达式
  | 求积表达式 '*' 最小表达式
  | 求积表达式 '/' 最小表达式
  | 求积表达式 '×' 最小表达式
  | 求积表达式 '÷' 最小表达式;

的确省去了多叉树转换. 代码整理完毕(program-in-chinese/quan5). 接下去, 是条件判断还是函数定义?

补记

Antlr4自带的语法分析可视化工具, 以antlr/grammars-v4为例:

$ alias grun='java -cp "{PATH_TO_antlr-4.7-complete.jar}:$CLASSPATH" org.antlr.v4.runtime.misc.TestRig'
$ java -cp "{PATH_TO_antlr-4.7-complete.jar}:$CLASSPATH" org.antlr.v4.Tool -visitor  -no-listener Java8.g4 
$ javac -cp "{PATH_TO_antlr-4.7-complete.jar}:$CLASSPATH" Java8*.java
$ grun Java8 expression -tree <--- 将输入字符串进行语法解析, 生成树结构
Warning: TestRig moved to org.antlr.v4.gui.TestRig; calling automatically
a>1
(expression (assignmentExpression (conditionalExpression (conditionalOrExpression (conditionalAndExpression (inclusiveOrExpression (exclusiveOrExpression (andExpression (equalityExpression (relationalExpression (relationalExpression (shiftExpression (additiveExpression (multiplicativeExpression (unaryExpression (unaryExpressionNotPlusMinus (postfixExpression (expressionName a)))))))) > (shiftExpression (additiveExpression (multiplicativeExpression (unaryExpression (unaryExpressionNotPlusMinus (postfixExpression (primary (primaryNoNewArray_lfno_primary (literal 1)))))))))))))))))))
$ grun Java8 expression -gui <--- 图形化
Warning: TestRig moved to org.antlr.v4.gui.TestRig; calling automatically
2>1
^D

2018_01_15_antlr_quan2_bnf_test

2018-01-15

相关文章
|
9月前
|
Python
Python将字符串拆成单字的函数代码设计
要想将字符串拆成单字,在Python中有一个非常简单的方法,一行代码就可以搞定了。那就是将字符串转换成列表list即可。这个过程可以使用内置的list()函数。不过,如果是要将英文的拆分为单个单词,那么这种方法就行不通了,因为该函数会将英文单词逐个拆分为字母,如果是这种情况,
97 2
|
8月前
|
Windows
VBA的直接或间接调用的语法格式
VBA|过程或方法内部的直接或间接调用与相对怪异的语法格式
|
9月前
|
IDE 开发工具 C++
|
9月前
02-python的基础语法-标识符/运算符/字符串拓展/字符串的拼接/字符串格式化/字符串精度控制/字符串格式化方式2/对表达式进行格式化/练习题/数据输入-input语句
02-python的基础语法-标识符/运算符/字符串拓展/字符串的拼接/字符串格式化/字符串精度控制/字符串格式化方式2/对表达式进行格式化/练习题/数据输入-input语句
|
9月前
|
存储 编译器
Makefile结构与其常见书写格式
Makefile结构与其常见书写格式
60 0
|
9月前
|
Java 程序员 C#
C# 介绍、应用领域、入门、语法、输出和注释详解
C#(发音为“C-Sharp”)是一种由 Microsoft 创建的面向对象的编程语言,运行在 .NET Framework 上。源于 C 家族,与流行的语言如 C++ 和 Java 相近。首个版本发布于 2002 年,而最新版本,C# 12,于 2023 年 11 月发布
213 0
|
编译器 C++
C++ 基础篇c++注释语法解析
程序的注释是解释性语句,您可以在 C++ 代码中包含注释,这将提高源代码的可读性。所有的编程语言都允许某种形式的注释。
|
芯片 异构计算
Verilog语法之测试文件
要测试我们设计的模块功能是否正常,最直接的办法就是烧写到FPGA芯片中进行验证,但是这种方式往往结果并不直观,且出现问题后也不容易定位。为提高工作效率,我们可通过电脑仿真的方式进行功能验证,待仿真通过后,再烧写到FPGA中,这样可以快速排除电路中存在的绝大多数bug。在电脑上进行仿真,除了我们设计的功能模块之外,还需要另一模块——testbench,用于产生被测模块所需的激励信号。由于testbench只是用于电脑端的仿真,而不需要最终综合成电路,因此其写法更加灵活,可以使用verilog语法中的一些不可综合的语句,如initial、#、$display、$readmemb、forever等。
279 0
|
存储 程序员 C语言
(第16列)C语言:计算输入的一行字符串中有多少个单词。顺便补充“gets函数”的用法。
(第16列)C语言:计算输入的一行字符串中有多少个单词。顺便补充“gets函数”的用法。
(第16列)C语言:计算输入的一行字符串中有多少个单词。顺便补充“gets函数”的用法。
|
Python
408.有效单词缩写 Python指针思路分析与细节解析!
408.有效单词缩写 Python指针思路分析与细节解析!
164 0