ANTLR4 返回值及传参的几种方式

  • 写在之前
  • 标签的Visitor
    • 语法文件
    • 自定义Visitor
    • 运行结果
  • 自定义的栈
    • 自定义Listener
    • 运行结果
  • ParseTreeProperty
    • 自定义Listener
    • 运行结果
  • 对比

写在之前

我们已经熟知了ANTLR4的Listener以及Visitor模式。

虽然我们用java来编写应用程序,但是我们想将语法文件和应用文件解耦合出来,因此暂时不用内嵌动作

接下来我们会介绍三种方式:带标签的Visitor模式(java自带函数栈)、自定义的、ANTLR4辅助数据结构ParseTreeProperty

标签的Visitor

这种模式我们之前介绍过,会省略中间的介绍。

语法文件

三种方法对应的语法文件都是统一的,后面不会重复列举。

类似于一个只会乘法和加法的计算器。

grammar LExpr;s:   e;e:   e MULT e    #Mult|   e ADD e     #Add|   INT         #Int;  CLEAR :   '!' ;
MULT :   '*' ; // assigns token name to '*' used above in grammar
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
ID  :   [a-zA-Z]+ ;      // match identifiers
INT :   [0-9]+ ;         // match integers
NEWLINE:'\r'? '\n' ;     // return newlines to parser (is end-statement signal)
WS  :   [ \t]+ -> skip ; // toss out whitespace

自定义Visitor

可以看到,EvalVisitor继承了泛型LExprBaseVisitor,类型为Integer。

这导致了后续visit的函数返回值类型都是Int,这个举措有好有坏。好处是我们终于可以设置每个树结点的返回值了,坏处是返回值类型被

同一,而且没办法传参

还有一个问题是,非叶子结点如果需要访问子结点,必须要显式地要用visit。

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;public class TestEvalVisitor{public static class EvalVisitor extends LExprBaseVisitor<Integer>{public Integer visitMult(LExprParser.MultContext ctx){return visit(ctx.e(0))*visit(ctx.e(1));}public Integer visitAdd(LExprParser.AddContext ctx){return visit(ctx.e(0))+visit(ctx.e(1));}public Integer visitInt(LExprParser.IntContext ctx){return Integer.valueOf(ctx.INT().getText());}}public static void main(String[] args) throws Exception{...EvalVisitor evalVisitor = new EvalVisitor();int result = evalVisitor.visit(tree);System.out.println("visitor result = "+result);}
}

运行结果

输入1+2*3

自定义的栈

我们采用Listener模式,在自定义Listener中取创建这个Stack。

自定义Listener

我们设置了一个类型为Integer的Stack,各个监听函数都可以传参,但是我们需要先对整个语法树的遍历过程足够的了解。

以1+23为例子,+和都是向右结合的运算符,因此我们从栈取出来时,第一个参数应该给right,第二个参数应该给left,看起来无伤大

雅,但还是小心为上。

使用Stack的缺点也十分明显:不够gracious,而且需要有非常清晰的逻辑顺序。但它确实有效。

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;public class TestLEvaluator{public static class Evaluator extends LExprBaseListener{Stack<Integer> stack=new Stack<Integer>();@Override public void exitMult(LExprParser.MultContext ctx) {int right=stack.pop();int left=stack.pop();stack.push(right*left);}@Override public void exitAdd(LExprParser.AddContext ctx) {int right=stack.pop();int left=stack.pop();stack.push(right+left);}@Override public void exitInt(LExprParser.IntContext ctx) {stack.push( Integer.valueOf(ctx.INT().getText()) );}}public static void main(String[] args) throws Exception{...ParseTreeWalker walker=new ParseTreeWalker();Evaluator eval=new Evaluator();walker.walk(eval,tree);System.out.println("stack result = "+eval.stack.pop());}}

运行结果

输入1+2*3:

ParseTreeProperty

这个方法的思路是,将每个结点需要返回的值临时保存起来(map)。ANTLR4自带了这个一个辅助的map,也就是ParseTreeProperty

实际上是一个Map<TreeNode,T>

自定义Listener

可以看到为了方便使用这个map,我们创建了getValue和setValue方便使用它。

好处自然是兼具了优雅以及高效,问题在于会过多的侵占内存。

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;public class TestLEvaluatorWithProps {public static class EvaluatorWithProps extends LExprBaseListener{ParseTreeProperty<Integer> values=new ParseTreeProperty<Integer>();@Override public void exitS(LExprParser.SContext ctx) {setValue(ctx,getValue(ctx.e()));}@Override public void exitMult(LExprParser.MultContext ctx) {setValue(ctx,getValue(ctx.e(0))*getValue(ctx.e(1)));}@Override public void exitAdd(LExprParser.AddContext ctx) {setValue(ctx,getValue(ctx.e(0))+getValue(ctx.e(1)));}@Override public void exitInt(LExprParser.IntContext ctx) {setValue(ctx,Integer.valueOf(ctx.INT().getText()));}public void setValue(ParseTree node,int value){values.put(node,value);}public int getValue(ParseTree node){return values.get(node);}}public static void main(String[] args) throws Exception{...ParseTreeWalker walker=new ParseTreeWalker();EvaluatorWithProps evalProp=new EvaluatorWithProps();walker.walk(evalProp,tree);System.out.println("properties result = " +evalProp.getValue(tree));}}

运行结果

还是输入1+2*3:

对比

  1. Visitor with tags:优雅。但无法传参,只能返回单一类型。
  2. Stack:高效。但不优雅,需要严谨逻辑。
  3. Map:优雅,方便,可以传参。内存耗用过大。

ANTLR4(六) 返回值 传参相关推荐

  1. fread读结构体返回值是0无错误_嵌入式C编程之错误处理(附代码例子)!

    原标题:嵌入式C编程之错误处理(附代码例子)! 作者: clover-toeic 前言 本文主要总结嵌入式系统C语言编程中,主要的错误处理方式.文中涉及的代码运行环境如下: 一.错误概念 错误分类 从 ...

  2. c语言返回值作用,c语言的返回值是什么意思啊?

    c语言的返回值是什么意思啊? 关注:204  答案:6  手机版 解决时间 2021-01-17 03:28 提问者怪咖 2021-01-16 11:08 例如下题 求三个整型参数的最大值函数 int ...

  3. 函数的初识;函数的返回值;函数的传参

    函数的初识: 避免重复代码,增强可读性. 函数是以功能为导向的. def 函数名(形参): 函数体 函数名() 实参 def 关键字 函数名(与变量命名规则一样) 1.由数字字母下划线组成 2.不能由 ...

  4. java中注解动态传参_SpringMVC之注解、传参、返回值及拦截器

    1. 注解式开发之annotation-driven解释 (1) mvc注解驱动在哪个文件中配置? Springmvc.xml (2) 配置mvc注解驱动使用哪个标签? 2. 注解式开发之视图解析器 ...

  5. 【php7扩展开发四】函数的参数 ,引用传参 ,返回值

    函数参数解析 之前我们定义的函数没有接收任何参数,那么扩展定义的内部函数如何读取参数呢?用户自定义函数在编译时会为每个参数创建一个zend_arg_info结构,这个结构用来记录参数的名称.是否引用传 ...

  6. java doget 返回json_HttpClient调用doGet、doPost、JSON传参及获得返回值

    调用 doPost:map传参 Map map = new HashMap<>(); map.put("test","test"); String ...

  7. SSM8==纯注解SSM项目:实现单表CRUD、事务、自定义异常和统一异常处理、RESTFUL风格接口、统一返回值格式(状态码、内容、消息)、JSON传参、axios、vue.js、elementUI

    环境:IDEA2021+JDK8+MAVEN3.8+TOMCAT7插件 前端:axios.vue.js.elementUI 后端:见POM.XML相关依赖,主要有数据库MySQL5.7 ,数据源Dru ...

  8. shell传参python脚本和获取返回值的探索方式

    一.python脚本 1.情况1:正常return 文件名test1.py #无参数有返回值的函数 def Have_return():return 55if __name__ == '__main_ ...

  9. Shell函数(函数定义、函数变量、函数调用、函数传参、函数返回值、获取函数返回值)

    分享知识 传递快乐 1.函数定义 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.Shell 函数定义的语法格式如下: [function] funname [()]{函 ...

最新文章

  1. 3dmax导出fbx时如何带贴图_houdini | 第一章 第三节 贴图与顶点动画
  2. 院士:国内科研目前内卷太严重,勿简单“抬轿子”
  3. ThinkPad L412 安装Mac 10.7.2 显卡驱动安装成功
  4. cgdb基本用法总结
  5. lucene,基于QueryParser的搜索
  6. 手动脱Mole Box壳实战总结
  7. 大连开发区取暖费能微信支付吗_下半年教资报考人数增加,那到底能不能异地报考呢?...
  8. 2018-2019-2 网络对抗技术 20165301 Exp2 后门原理与实践
  9. 网上购物商城 html+css+MVC+sql server+idea编辑器实现。
  10. 确保客户端可以接收到服务端的异常serviceDebug includeExceptionDetailInFaults=true
  11. Julia : win下cmd和repl中执行.jl程序
  12. 一个java文件可以有多个类嘛?
  13. Ardence BXP 3.5 - 4.1 PNP 方法
  14. python爬虫100个入门项目
  15. mysql字符集校对_MySQL字符集与校对
  16. PDF文件提取单独页面
  17. python数据分析与应用pdf_看了Python在金融行业中的应用,大数据分析实在太重要了!...
  18. 善于计划,善于总结,善于归纳
  19. 咏南中间件跨平台解决方案
  20. 职业生涯规划需要考虑的三大要点

热门文章

  1. 通过WireGuard搭建隧道实现内网穿透
  2. jboss jpa 配置使用
  3. 关键字的首字母拼音和全拼搜索
  4. onenote 无法使用个人账号登陆_lol手游无法使用此区域账号登陆怎么办 无法使用此区域账号登陆处理方法[多图] -手游问答...
  5. 各种计算机知识和技术大全
  6. [冀信2021-pwn] vip
  7. 什么是库函数、寄存器?如何新建一个库函数的工程模板?(第四天,有检讨)
  8. ARM编程模式和7种模式
  9. 堆优化器(Heap-Based Optimizer, HBO)
  10. 通用的结构化数据流通工具