zoukankan      html  css  js  c++  java
  • 2018-01-11 Antlr4实现数学四则运算

    中文编程知乎专栏原文地址

    基本参考https://pragprog.com/book/tpantlr2/the-definitive-antlr-4-reference 一书"Building a Calculator Using a Visitor"一节, 仅添加了数学乘除法符号的支持(×÷). 比如下面的算式:

    3×2+8÷4-2×4
    

    源码仍在program-in-chinese/quan5

    相比上一版本语法文件去除了空格定义. 需要深究的是优先级问题. 是否因为"表达式 运算符=('*'|'/'|'×'|'÷') 表达式"写在了前面才使得乘除法的优先级在语法分析时更高.

    至此, 感觉Antlr语法文件对中文命名的支持还是不错的. 唯一需要权宜之计的就是Token(词)规则必须要大写开头, 因此采用了前缀"T"):

    grammar 圈5;
    程序
     : 表达式
     ;
    
    表达式
     : 表达式 运算符=('*'|'/'|'×'|'÷') 表达式 	#乘除
       | 表达式 运算符=('+'|'-') 表达式 		#加減
       | T数					#数
     ;
    
    T数
     : [0-9]+
     ;
    T加 : '+';
    T減 : '-';
    T乘 : '*';
    T数乘: '×';
    T除 : '/';
    T数除: '÷';
    

    第一次尝试#标号的辅助功能. 一个"表达式"语法规则生成了三个Visitor方法(如下), 访问器仍比较简单. 注: 语法规则中要么所有分支都有标号, 要么都没有. 不然生成分析器时报错:

    public class 定制访问器 extends 圈5BaseVisitor<节点> {
    
      @Override
      public 节点 visit数(数Context 上下文) {
        TerminalNode 数 = 上下文.T数();
        return 数 instanceof ErrorNode ? null : new 数节点(数.getText());
      }
    
      @Override
      public 节点 visit加減(加減Context 上下文) {
        表达式节点 节点 = new 表达式节点();
        节点.运算符 = 上下文.运算符.getType() == 圈5Parser.T加 ? 运算符号.加 : 运算符号.減;
        节点.左子节点 = visit(上下文.表达式(0));
        节点.右子节点 = visit(上下文.表达式(1));
        return 节点;
      }
    
      @Override
      public 节点 visit乘除(乘除Context 上下文) {
        表达式节点 节点 = new 表达式节点();
        int 运算符 = 上下文.运算符.getType();
        节点.运算符 = (运算符 == 圈5Parser.T乘 || 运算符 == 圈5Parser.T数乘) ? 运算符号.乘 : 运算符号.除;
        节点.左子节点 = visit(上下文.表达式(0));
        节点.右子节点 = visit(上下文.表达式(1));
        return 节点;
      }
    
    }
    

    语法树中稍微复杂一点的"表达式"节点, 代码很冗余:

    public class 表达式节点 extends 节点 {
    
      public 运算符号 运算符;
    
      @Override
      public Object 求值() {
        if (运算符.equals(运算符号.加)) {
          return (int)(左子节点.求值()) + ((int)右子节点.求值());
        } else if (运算符.equals(运算符号.減)) {
          return (int)(左子节点.求值()) - ((int)右子节点.求值());
        } else if (运算符.equals(运算符号.乘)) {
          return (int)(左子节点.求值()) * ((int)右子节点.求值());
        } else if (运算符.equals(运算符号.除)) {
          return (int)(左子节点.求值()) / ((int)右子节点.求值());
        } else {
          return null;
        }
      }
    
    }
    

    已经要手动跑十个测试文件, 下面除了清理代码, 还需要加测试, 再加功能(应该是变量赋值).

  • 相关阅读:
    获取表信息(MSSQL)
    合并有数据的列
    isnull的使用方法
    查询SQLServer的启动时间
    查询数据库中有数据的表
    查询数据库中表使用的空间信息。
    SQL Server SA 密码丢失无法连接数据库怎么办?
    tensorflow 语法及 api 使用细节
    Python: PS 滤镜-- Fish lens
    中英文对照 —— 概念的图解
  • 原文地址:https://www.cnblogs.com/program-in-chinese/p/10487208.html
Copyright © 2011-2022 走看看