ANTLR可以自動地幫助你完成詞法分析和語法分析的工做, 免去了手寫去寫詞法分析器和語法分析器的麻煩java
它是基於LL(k)的, 以遞歸降低的方式進行工做.ANTLR v4還支持多種目標語言。本文用java來寫代碼。框架
總結一下:ANTRL能自動完成語法分析和詞法分析過程,並生產框架代碼,讓咱們寫相關過程的時候只須要往固定位置添加代碼便可。大大簡便了語法分析詞法分析的過程。ide
由於用IDEA,因此直接介紹在IDEA中怎麼安裝,在IDEA中安裝ANTLR4相關插件便可。而後MAVEN引用下函數
``` <dependency> <groupId>org.antlr</groupId> <artifactId>antlr4</artifactId> <version>4.5.2</version> </dependency> ```
ANTLR4有專門的語法來構建整個過程測試
grammar Expr; 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 ; MUL : '*' ; // assigns token name to '*' used above in grammar DIV : '/' ; ADD : '+' ; SUB : '-' ; ID : [a-zA-Z]+ ; INT : [0-9]+ ; NEWLINE:'\r'? '\n' ; WS : [ \t]+ -> skip;
相關語法很簡單, 總體來講一個原則,遞歸降低。 即定義一個表達式(如expr),能夠循環調用直接也能夠調用其餘表達式,可是最終確定會有一個最核心的表達式不能再繼續往下調用了。this
以上代碼在真正執行的時候會生成一棵抽象語法樹,選擇「prog」而後->"Test Rule prog", 輸入測試數據「(1 + 2)+3-4*5」,而後咱們會就能夠看到一棵語法樹了。插件
整個語法文件的目的是爲了讓antlr生產相關的java代碼。 咱們先設置下生成visitor, 而後,他會生成以下幾個文件:code
ExprLexer 是詞法分析器, ExprParser是語法分析器。 一個語言的解析過程通常過程是 詞法分析-->語法分析。這是ANTLR4爲咱們生成的框架代碼, 而咱們惟一要作的是本身實現一個Vistor,通常從ExprBaseVistor繼承便可。blog
ANTLR 會爲ExprBaseVistor 從定義的symoble文件如「#printExpr, #assign」 ,自動生成相應的仍是,而後就實現這些仍是就能夠實現咱們的功能了。 如:繼承
@Override public Integer visitAssign(ExprParser.AssignContext ctx) { String id = ctx.ID().getText(); Integer value = visit(ctx.expr()); this.memory.put(id, value); return value; } @Override public Integer visitInt(ExprParser.IntContext ctx) { return Integer.valueOf(ctx.INT().getText()); } @Override public Integer visitMulDiv(ExprParser.MulDivContext ctx) { Integer left = visit(ctx.expr(0)); Integer right = visit(ctx.expr(1)); if (ctx.op.getType() == ExprParser.MUL){ return left * right; }else{ return left / right; } }
解釋下Context的應用, Context 能夠經過 expr(i) 取上下文的子內容。
而後就能夠用以下方式是使用了:
public static void main(String [] args) throws IOException { ANTLRInputStream inputStream = new ANTLRInputStream("1 + 2 + 3 * 4+ 6 / 2"); ExprLexer lexer = new ExprLexer(inputStream); CommonTokenStream tokenStream = new CommonTokenStream(lexer); ExprParser parser = new ExprParser(tokenStream); ParseTree parseTree = parser.prog(); EvalVisitor visitor = new EvalVisitor(); Integer rtn = visitor.visit(parseTree); System.out.println("#result#"+rtn.toString()); }
運行一下,能夠獲得正確的結果了。
好神奇。 咱們來分析下整個過程是如何實現的。
首先Antlr4會根據相關的語法文件生成ExprParser類,其內容是由 其語法內容決定的。如上的語法中有三個表達式:prog,stat,expr,因此就生成了三個函數:
public final ProgContext prog() throws RecognitionException { ProgContext _localctx = new ProgContext(_ctx, getState()); enterRule(_localctx, 0, RULE_prog); try { enterOuterAlt(_localctx, 1); { setState(6); stat(); } } catch (RecognitionException re) { _localctx.exception = re; _errHandler.reportError(this, re); _errHandler.recover(this, re); } finally { exitRule(); } return _localctx; }
public final StatContext stat() throws RecognitionException { StatContext _localctx = new StatContext(_ctx, getState()); enterRule(_localctx, 2, RULE_stat); ...
stat過程是真正的語法分析過程, 他會把相應的token填上不一樣的StatContext.
整個語法解析的過程就是 prop -> stat ->expr。
在語法文件中有MUL,DIV 等幾個關鍵字, Antlr會自動識別其是否有子項調用若是沒有則這樣定義:
public static final int T__0=1, T__1=2, T__2=3, MUL=4, DIV=5, ADD=6, SUB=7, ID=8, INT=9, NEWLINE=10, WS=11; public static final int RULE_prog = 0, RULE_stat = 1, RULE_expr = 2;
有了parser, 下一個疑問就是parser如何和咱們寫的visitor聯繫起來的。 這就要藉助於一個很是重要的概念:Context.
由於語法文件中有8個symbol ,因此會對於生成不一樣的Context.
最終返回出去:
ParseTree parseTree = parser.prog(); EvalVisitor visitor = new EvalVisitor(); Integer rtn = visitor.visit(parseTree);
一個典型的Context是這樣實現的:
public static class IntContext extends ExprContext { public TerminalNode INT() { return getToken(ExprParser.INT, 0); } public IntContext(ExprContext ctx) { copyFrom(ctx); } @Override public <T> T accept(ParseTreeVisitor<? extends T> visitor) { if ( visitor instanceof ExprVisitor ) return ((ExprVisitor<? extends T>)visitor).visitInt(this); else return visitor.visitChildren(this); } }
特別關注 accept 的實現。
看下 visitor的實現
public T visit(ParseTree tree) { return tree.accept(this); }
典型的visitor模式的實現。 以上這個流程是:
Antlr4 屏蔽了語法分析和詞法分析的細節。大大簡化了開發的工做量。 並且使用簡單方便。對比 Boost.Spirit,簡直一個是自動擋的汽車,一個是飛機。