ANTLR4(六) 返回值 传参
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:
对比
- Visitor with tags:优雅。但无法传参,只能返回单一类型。
- Stack:高效。但不优雅,需要严谨逻辑。
- Map:优雅,方便,可以传参。内存耗用过大。
ANTLR4(六) 返回值 传参相关推荐
- fread读结构体返回值是0无错误_嵌入式C编程之错误处理(附代码例子)!
原标题:嵌入式C编程之错误处理(附代码例子)! 作者: clover-toeic 前言 本文主要总结嵌入式系统C语言编程中,主要的错误处理方式.文中涉及的代码运行环境如下: 一.错误概念 错误分类 从 ...
- c语言返回值作用,c语言的返回值是什么意思啊?
c语言的返回值是什么意思啊? 关注:204 答案:6 手机版 解决时间 2021-01-17 03:28 提问者怪咖 2021-01-16 11:08 例如下题 求三个整型参数的最大值函数 int ...
- 函数的初识;函数的返回值;函数的传参
函数的初识: 避免重复代码,增强可读性. 函数是以功能为导向的. def 函数名(形参): 函数体 函数名() 实参 def 关键字 函数名(与变量命名规则一样) 1.由数字字母下划线组成 2.不能由 ...
- java中注解动态传参_SpringMVC之注解、传参、返回值及拦截器
1. 注解式开发之annotation-driven解释 (1) mvc注解驱动在哪个文件中配置? Springmvc.xml (2) 配置mvc注解驱动使用哪个标签? 2. 注解式开发之视图解析器 ...
- 【php7扩展开发四】函数的参数 ,引用传参 ,返回值
函数参数解析 之前我们定义的函数没有接收任何参数,那么扩展定义的内部函数如何读取参数呢?用户自定义函数在编译时会为每个参数创建一个zend_arg_info结构,这个结构用来记录参数的名称.是否引用传 ...
- java doget 返回json_HttpClient调用doGet、doPost、JSON传参及获得返回值
调用 doPost:map传参 Map map = new HashMap<>(); map.put("test","test"); String ...
- SSM8==纯注解SSM项目:实现单表CRUD、事务、自定义异常和统一异常处理、RESTFUL风格接口、统一返回值格式(状态码、内容、消息)、JSON传参、axios、vue.js、elementUI
环境:IDEA2021+JDK8+MAVEN3.8+TOMCAT7插件 前端:axios.vue.js.elementUI 后端:见POM.XML相关依赖,主要有数据库MySQL5.7 ,数据源Dru ...
- shell传参python脚本和获取返回值的探索方式
一.python脚本 1.情况1:正常return 文件名test1.py #无参数有返回值的函数 def Have_return():return 55if __name__ == '__main_ ...
- Shell函数(函数定义、函数变量、函数调用、函数传参、函数返回值、获取函数返回值)
分享知识 传递快乐 1.函数定义 linux shell 可以用户定义函数,然后在shell脚本中可以随便调用.Shell 函数定义的语法格式如下: [function] funname [()]{函 ...
最新文章
- 3dmax导出fbx时如何带贴图_houdini | 第一章 第三节 贴图与顶点动画
- 院士:国内科研目前内卷太严重,勿简单“抬轿子”
- ThinkPad L412 安装Mac 10.7.2 显卡驱动安装成功
- cgdb基本用法总结
- lucene,基于QueryParser的搜索
- 手动脱Mole Box壳实战总结
- 大连开发区取暖费能微信支付吗_下半年教资报考人数增加,那到底能不能异地报考呢?...
- 2018-2019-2 网络对抗技术 20165301 Exp2 后门原理与实践
- 网上购物商城 html+css+MVC+sql server+idea编辑器实现。
- 确保客户端可以接收到服务端的异常serviceDebug includeExceptionDetailInFaults=true
- Julia : win下cmd和repl中执行.jl程序
- 一个java文件可以有多个类嘛?
- Ardence BXP 3.5 - 4.1 PNP 方法
- python爬虫100个入门项目
- mysql字符集校对_MySQL字符集与校对
- PDF文件提取单独页面
- python数据分析与应用pdf_看了Python在金融行业中的应用,大数据分析实在太重要了!...
- 善于计划,善于总结,善于归纳
- 咏南中间件跨平台解决方案
- 职业生涯规划需要考虑的三大要点
热门文章
- 通过WireGuard搭建隧道实现内网穿透
- jboss jpa 配置使用
- 关键字的首字母拼音和全拼搜索
- onenote 无法使用个人账号登陆_lol手游无法使用此区域账号登陆怎么办 无法使用此区域账号登陆处理方法[多图] -手游问答...
- 各种计算机知识和技术大全
- [冀信2021-pwn] vip
- 什么是库函数、寄存器?如何新建一个库函数的工程模板?(第四天,有检讨)
- ARM编程模式和7种模式
- 堆优化器(Heap-Based Optimizer, HBO)
- 通用的结构化数据流通工具