ANTLR学习(三)antlr的功能
文章目录
- 算数表达式语言
- 匹配算数表达式的语言
- 语法
- 语法导入
- 处理错误输入
- 使用访问者模式构建一个计算器
- 利用监听器构建一个翻译器
- 如何将动作直接嵌入语法文件
- 在语法中嵌入任意动作
- 使用语义判定改变语法分析过程
- 词法分析特性
- 孤岛语法
- 重写输入流
- 将词法符号送入不同通道
算数表达式语言
匹配算数表达式的语言
我们的表达式语言组成的程序就是一系列语句,每个语句都由换行符终止,一个语句可以是一个表达式、一个赋值语句或者是一个空行。
语法
//Expr.g4
grammar Expr;/** The start rule; begin parsing here. */
prog: stat+ ; stat: expr NEWLINE | ID '=' expr NEWLINE | NEWLINE ;expr: expr ('*'|'/') expr | expr ('+'|'-') expr | INT | ID | '(' expr ')' ;ID : [a-zA-Z]+ ; // match identifiers <label id="code.tour.expr.3"/>
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (is end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
- 语法分析器的规则以小写字母开头
- 词法分析器的规则以大写字母开头
- 使用
|
来分割同一个语言规则的若干备选分支,使用圆括号吧一些符号组成子规则,('*' | '/')
匹配一个乘法符号或除法符号
antlr4的最重要功能之一就是它在大部分情况下能够处理左递归规则,详细参考左递归
使用上面的Expr.g4
文件做一个测试
antlr4 Expr.g4
javac Expr*.java
grun Expr prog -gui t.expr
运行结果
相应源码也可以在https://pragprog.com/titles/tpantlr2/the-definitive-antlr-4-reference/上下载
//ExprJoyRide.java
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class ExprJoyRide {public static void main(String[] args) throws Exception {String inputFile = null; if ( args.length>0 ) inputFile = args[0];InputStream is = System.in;if ( inputFile!=null ) is = new FileInputStream(inputFile);ANTLRInputStream input = new ANTLRInputStream(is); ExprLexer lexer = new ExprLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); ExprParser parser = new ExprParser(tokens); ParseTree tree = parser.prog(); // 从parse规则开始进行语法分析System.out.println(tree.toStringTree(parser)); //以文本形式打印树}
}
javac ExprJoyRide.java Expr*.java
java ExprJoyRide t.expr
运行结果
语法导入
//CommonLexerRules.g4
lexer grammar CommonLexerRules; // note "lexer grammar"ID : [a-zA-Z]+ ; // match identifiers
INT : [0-9]+ ; // match integers
NEWLINE:'\r'? '\n' ; // return newlines to parser (end-statement signal)
WS : [ \t]+ -> skip ; // toss out whitespace
//LabeledExpr.g4
grammar LabeledExpr; // rename to distinguish from Expr.g4prog: stat+ ;stat: expr NEWLINE # printExpr| ID '=' expr NEWLINE # assign| NEWLINE # blank;expr: expr op=('*'|'/') expr # MulDiv| expr op=('+'|'-') expr # AddSub| INT # int| ID # id| '(' expr ')' # parens;MUL : '*' ; // 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
能不能将CommonLexerRules.g4中的词法规则重构进LabeledExpr.g4呢?
答案是可以的
grammar LabeledExpr; // rename to distinguish from Expr.g4
import CommonLexerRules;//添加的代码prog: stat+ ;stat: expr NEWLINE # printExpr| ID '=' expr NEWLINE # assign| NEWLINE # blank;expr: expr op=('*'|'/') expr # MulDiv| expr op=('+'|'-') expr # AddSub| INT # int| ID # id| '(' expr ')' # parens;
测试
处理错误输入
可以看到,语法分析器报错并从错误中恢复
语法分析器会在错误节点标红
使用访问者模式构建一个计算器
//LabeledExpr.g4
grammar LabeledExpr; // rename to distinguish from Expr.g4prog: stat+ ;stat: expr NEWLINE # printExpr| ID '=' expr NEWLINE # assign| NEWLINE # blank;expr: expr op=('*'|'/') expr # MulDiv| expr op=('+'|'-') expr # AddSub| INT # int| ID # id| '(' expr ')' # parens;MUL : '*' ; // 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
//Calc.java
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;import java.io.FileInputStream;
import java.io.InputStream;public class Calc {public static void main(String[] args) throws Exception {String inputFile = null;if ( args.length>0 ) inputFile = args[0];InputStream is = System.in;if ( inputFile!=null ) is = new FileInputStream(inputFile);ANTLRInputStream input = new ANTLRInputStream(is);LabeledExprLexer lexer = new LabeledExprLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);LabeledExprParser parser = new LabeledExprParser(tokens);ParseTree tree = parser.prog(); // parseEvalVisitor eval = new EvalVisitor();eval.visit(tree);}
}
命令行执行
antlr4 -no-listener -visitor LabeledExpr.g4
import java.util.HashMap;
import java.util.Map;public class EvalVisitor extends LabeledExprBaseVisitor<Integer> {/** "memory" for our calculator; variable/value pairs go here */Map<String, Integer> memory = new HashMap<String, Integer>();/** ID '=' expr NEWLINE */@Overridepublic Integer visitAssign(LabeledExprParser.AssignContext ctx) {String id = ctx.ID().getText(); // id is left-hand side of '='int value = visit(ctx.expr()); // compute value of expression on rightmemory.put(id, value); // store it in our memoryreturn value;}/** expr NEWLINE */@Overridepublic Integer visitPrintExpr(LabeledExprParser.PrintExprContext ctx) {Integer value = visit(ctx.expr()); // evaluate the expr childSystem.out.println(value); // print the resultreturn 0; // return dummy value}/** INT */@Overridepublic Integer visitInt(LabeledExprParser.IntContext ctx) {return Integer.valueOf(ctx.INT().getText());}/** ID */@Overridepublic Integer visitId(LabeledExprParser.IdContext ctx) {String id = ctx.ID().getText();if ( memory.containsKey(id) ) return memory.get(id);return 0;}/** expr op=('*'|'/') expr */@Overridepublic Integer visitMulDiv(LabeledExprParser.MulDivContext ctx) {int left = visit(ctx.expr(0)); // get value of left subexpressionint right = visit(ctx.expr(1)); // get value of right subexpressionif ( ctx.op.getType() == LabeledExprParser.MUL ) return left * right;return left / right; // must be DIV}/** expr op=('+'|'-') expr */@Overridepublic Integer visitAddSub(LabeledExprParser.AddSubContext ctx) {int left = visit(ctx.expr(0)); // get value of left subexpressionint right = visit(ctx.expr(1)); // get value of right subexpressionif ( ctx.op.getType() == LabeledExprParser.ADD ) return left + right;return left - right; // must be SUB}/** '(' expr ')' */@Overridepublic Integer visitParens(LabeledExprParser.ParensContext ctx) {return visit(ctx.expr()); // return child expr's value}
}
使用t.expr作为输入
javac Calc.java LabeledExpr*.java
type t.expr
无需再语法文件中插入Java代码编写的动作,语法文件独立于程序,具有编程语言中立性。
利用监听器构建一个翻译器
读取目标:
//Demo.java
import java.util.List;
import java.util.Map;
public class Demo {void f(int x, String y) { }int[ ] g(/*no args*/) { return null; }List<Map<String, Integer>>[] h() { return null; }
}
使用其中的方法签名生成一个接口,保留其中的全部空白字符和注释
想要得到:
interface IDemo {void f(int x,String y);int[ ] g(/*no args*/);List<Map<String, Integer>>[] h();
}
//ExtractInterfaceTool.java
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.*;import java.io.FileInputStream;
import java.io.InputStream;public class ExtractInterfaceTool {public static void main(String[] args) throws Exception {String inputFile = null;if ( args.length>0 ) inputFile = args[0];InputStream is = System.in;if ( inputFile!=null ) {is = new FileInputStream(inputFile);}ANTLRInputStream input = new ANTLRInputStream(is);JavaLexer lexer = new JavaLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);JavaParser parser = new JavaParser(tokens);ParseTree tree = parser.compilationUnit(); // parseParseTreeWalker walker = new ParseTreeWalker(); // create standard walkerExtractInterfaceListener extractor = new ExtractInterfaceListener(parser);walker.walk(extractor, tree); // initiate walk of tree with listener}
}
操作
antlr4 Java.g4
java org.antlr.v4.Tool Java.g4
javac Java*.java Extract*.java
java ExtractInterfaceTool Demo.java
结果:
成功
如何将动作直接嵌入语法文件
在语法中嵌入任意动作
//t.rows文件
parrt Terence Parr 101
tombu Tom Burns 020
bke Kevin Edgar 008
//Rows.g4
grammar Rows;@parser::members { // add members to generated RowsParserint col;public RowsParser(TokenStream input, int col) { // custom constructorthis(input);this.col = col;}
}file: (row NL)+ ;row
locals [int i=0]: ( STUFF{$i++;if ( $i == col ) System.out.println($STUFF.text);})+;TAB : '\t' -> skip ; // match but don't pass to the parser
NL : '\r'? '\n' ; // match and pass to the parser
STUFF: ~[\t\r\n]+ ; // match any chars except tab, newline
//Col.java
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.Token;import java.io.FileInputStream;
import java.io.InputStream;public class Col {public static void main(String[] args) throws Exception {ANTLRInputStream input = new ANTLRInputStream(System.in);RowsLexer lexer = new RowsLexer(input);CommonTokenStream tokens = new CommonTokenStream(lexer);int col = Integer.valueOf(args[0]);RowsParser parser = new RowsParser(tokens, col); // pass column number!parser.setBuildParseTree(false); // don't waste time bulding a treeparser.file(); // parse}
}
操作与结果
使用语义判定改变语法分析过程
//t.data
2 9 10 3 1 2 3
- 2 ➡ 匹配9,10
- 3 ➡ 匹配1,2,3
//Data.g4
grammar Data;file : group+ ;group: INT sequence[$INT.int] ;sequence[int n]
locals [int i = 1;]: ( {$i<=$n}? INT {$i++;} )* // match n integers;INT : [0-9]+ ; // match integers
WS : [ \t\n\r]+ -> skip ; // toss out all whitespace
操作与结果
词法分析特性
孤岛语法
处理相同文件中的不同格式,将模板语言表达式之外的文本按照不同的方式进行处理
例子:
//XMLLexer.g4
lexer grammar XMLLexer;// Default "mode": Everything OUTSIDE of a tag
OPEN : '<' -> pushMode(INSIDE) ;
COMMENT : '<!--' .*? '-->' -> skip ;
EntityRef : '&' [a-z]+ ';' ;
TEXT : ~('<'|'&')+ ; // match any 16 bit char minus < and &// ----------------- Everything INSIDE of a tag ---------------------
mode INSIDE;CLOSE : '>' -> popMode ; // back to default mode
SLASH_CLOSE : '/>' -> popMode ;
EQUALS : '=' ;
STRING : '"' .*? '"' ;
SlashName : '/' Name ;
Name : ALPHA (ALPHA|DIGIT)* ;
S : [ \t\r\n] -> skip ;fragment
ALPHA : [a-zA-Z] ;fragment
DIGIT : [0-9] ;
<tools><tool name="ANTLR">A parser generator</tool>
</tools>
构建与测试
重写输入流
antlr4 Java.g4
javac InsertSerialID*.java Java*.java
java InsertSerialID Demo.java
将词法符号送入不同通道
对于大多数语法,语法分析器是可以忽略空白字符与注释,但这样同样意味着程序中将完全无法访问空白字符和注释,那么如何保留空白字符和注释呢,方法是将这些词法符号送入另外一个通道,语法分析器只处理一个通道,我们就将希望保留的符号送入其他通道,这样就可以实现对空白字符和注释的保留。
Java语法
COMMENT: '/*' .*? '*/' -> channel(HIDDEN) // match anything between /* and */;
WS : [ \r\t\u000C\n]+ -> channel(HIDDEN);
ANTLR学习(三)antlr的功能相关推荐
- 【技术综述】图像与CNN发家简史,集齐深度学习三巨头
文章首发于微信公众号<有三AI> [技术综述]图像与CNN发家简史,集齐深度学习三巨头 没有一个经典的发现会是突然之间横空出世,它总是需要一些积淀. 提起卷积神经网络,我们总会从LeNet ...
- spring security 学习三-rememberMe
spring security 学习三-rememberMe 功能:登录时的"记住我"功能 原理: rememberMeAuthenticationFilter在security过 ...
- PyTorch框架学习三——张量操作
PyTorch框架学习三--张量操作 一.拼接 1.torch.cat() 2.torch.stack() 二.切分 1.torch.chunk() 2.torch.split() 三.索引 1.to ...
- 大数据基础学习三:Ubuntu下安装VMware Tools超详细步骤及需要注意的问题(以ubuntu-18.04.3、Mware Workstation 15.1.0 Pro为例)
大数据基础学习三:Ubuntu下安装VMware Tools超详细步骤及需要注意的问题 (以ubuntu-18.04.3.Mware Workstation 15.1.0 Pro for Window ...
- Docker学习三:Docker 数据管理
前言 本次学习来自于datawhale组队学习: 教程地址为: https://github.com/datawhalechina/team-learning-program/tree/master/ ...
- 深度学习三(PyTorch物体检测实战)
深度学习三(PyTorch物体检测实战) 文章目录 深度学习三(PyTorch物体检测实战) 1.网络骨架:Backbone 1.1.神经网络基本组成 1.1.1.卷积层 1.1.2.激活函数层 1. ...
- 深度学习三巨头共获 2018 年图灵奖(经典重温)!
整理 | 琥珀 出品 | AI科技大本营(ID:rgznai100) 2019 年 3 月 27 日,ACM 宣布,深度学习三位大牛 Yoshua Bengio.Yann LeCun.Geoffrey ...
- 深度学习三十年创新路
深度学习三十年创新路 编者注:深度学习火了,从任何意义上,大家谈论它的热衷程度,都超乎想象.但是,似乎很少有人提出不同的声音,说深度学习的火热,有可能是过度的繁荣,乃至不理性的盲从.而这次,有不同的想 ...
- python学习三-基础语法
python学习三-基础语法(2019-12-24日晚) 1.源码文件 Python源码文件名通常采用小写的方式,常见的扩展名有: py:基本的源码扩展名. pyw:是另一种源码扩展名,跟py唯一的区 ...
- 深度学习三巨头之Yann LeCun(杨立昆)简介
在人工智能研究领域,Yann LeCun.Geoffrey Hinton 和 Yoshua Bengio一直被公认为深度学习三巨头. Yann LeCun,自称中文名"杨立昆",计 ...
最新文章
- 路易斯安那州立大学计算机科学,西北路易斯安那州立大学
- 【MAVEN】如何在Eclipse中创建MAVEN项目
- 第七十一期:管理 | 技术Leader:选OKR还是KPI?
- loadrunner学习理论之一
- WebLogic清理缓存
- Linux调试工具strace和gdb常用命令小结-转
- 文本分类实战--从TFIDF到深度学习CNN系列效果对比(附代码)
- oracle 获取多个序列值,一次性获取多个oracle序列值问题
- ai背景合成_视频素材不好找!图片也能生成视频啦,AI剪辑助力原创短视频创作...
- 配有傲腾内存的电脑如何安装系统
- My Fifty-First Page - 组合总和 - By Nicolas
- POJ 3295: Tautology
- usb转并口支持linux,USB转并口;USB转真并口(支持仿真调试加密狗等);USB TO LPT
- Linux:ftrace: 为什么有些函数没有在available_filter_functions
- 新代系统反向间隙参数_如何调试新代系统SYNTEC参数
- XB文件开发详解(上报证监会文件)_入门系列
- Matlab画图彩色变黑白
- 一碗牛肉面的成本是多少钱?
- 免费不限速跨平台文件传输神器—文件疯巢
- 如何下载国家标准分幅影像地图
热门文章
- 群、环、域基础与例子
- GotW#63 狂乱的代码
- 【去后厂村开游戏厅吧】基于pp-tinypose的体感贪吃蛇游戏
- c盘内存不足怎么清理(如何清理电脑c盘空间)
- 铁路信号基础知识——信号部分
- SDN的两种方式分析
- Android开发入门到实战精通 完整全套开发教程送给你
- 解决PCL报错: Assertion `point_representation_->isValid (point) “Invalid (NaN, Inf) point coordinates
- Mac宝藏软件推荐(笔者也在用)(二)
- 前后端微信小程序开发