采用访问器的计算器
这次我们想要达成这样的效果:一行一行地输入表达式,每次回车就输出运算结果。
而不是和原有语法一样,输入整个文件进行语法树分析再输出。
我们分开各个部分讲解。
@header:我们设定打包文件在tools文件夹,并且输入jar文件。@parser::members:指内嵌到语法分析中的成员。memory和eval本来是在自定义访问器中的。需要注意op的分支是词法符号MUL、DIV、ADD、SUB。e returns [int v]:a、op、b等作为标记,它们可以标记到词法符号以及语法分析过程中的context上,e会被重新打包成这样一个EContext: 需要注意的是op,它通过.type返回的就是MUL等词法符号。而INT.int实际上就是把text的类型(String)通过int返回。 grammar Expr; @header{ package tools; import java.util.*; } @parser::members{ Map<String,Integer> memory=new HashMap<String,Integer>(); int eval(int left,int op,int right){ switch(op){ case MUL:return left*right; case DIV:return left/right; case ADD:return left+right; case SUB:return left-right; } return 0; } } //prog: stat+ ; stat: e NEWLINE { System.out.println($e.v);} | ID '=' e NEWLINE { memory.put($ID.text,$e.v); } | NEWLINE ; e returns [int v] : a=e op=('*'|'/') b=e { $v=eval($a.v,$op.type,$b.v); } | a=e op=('+'|'-') b=e { $v=eval($a.v,$op.type,$b.v); } | INT { $v=$INT.int; } | ID { String id=$ID.text; $v=memory.containsKey(id)?memory.get(id):0; } | '(' e ')' { $v=$e.v; } ; MUL : '*' ; DIV : '/' ; ADD : '+' ; SUB : '-' ; 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输入部分还是不变。
我们设置一个足够大的缓冲区BufferedReader,在实例化的时候创建InputStream的Reader并赋给br。
先创建语法分析器,不生成树。
只要缓冲区还能读得到内容,就进行这样一个循环:
ANTLRInputStream->ExprLexer->CommonTokenStream->Parser。
需要注意的是,由于流里的数据一行一行在增加,每次lexer需要通过行号去读取一行。
package tools; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CommonTokenStream; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; 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); } BufferedReader br=new BufferedReader(new InputStreamReader(is)); String expr=br.readLine(); int line=1; ExprParser parser=new ExprParser(null); parser.setBuildParseTree(false); while(expr!=null) { ANTLRInputStream input=new ANTLRInputStream(expr+'\n'); ExprLexer lexer=new ExprLexer(input); lexer.setLine(line); lexer.setCharPositionInLine(0); CommonTokenStream tokens=new CommonTokenStream(lexer); parser.setInputStream(tokens); parser.stat(); expr=br.readLine(); line++; } } }输入:
antlr4 tools/Expr.g4
javac -d . tools/*.java
java tools.Calc
注意第二句,-d指明当前类层次根目录,.也就是当前目录,tools/*java为编译文件。
输出:
