上一篇:實現能夠解析表達式的計算器javascript
本文已經同步到公衆號「極客起源」,輸入379404開始學習!html
本文詳細講解如何用Listener方式實現一個能夠計算表達式的程序,該程序不只能夠計算表達式,也能夠識別表達式的錯誤,若是某一個表達式出錯,那麼該表達式不會輸出任何結果。java
// expr op=('*'|'/') expr # MulDiv public Integer visitMulDiv(CalcParser.MulDivContext ctx) { int left = visit(ctx.expr(0)); // 訪問MulDiv的左操做數 int right = visit(ctx.expr(1)); // 訪問MulDiv的右操做數 if ( ctx.op.getType() == CalcParser.MUL ) return left * right; return left / right; } }
@Override public void enterMulDiv(CalcParser.MulDivContext ctx) { } @Override public void exitMulDiv(CalcParser.MulDivContext ctx) { }
那麼開始處理動做和結束處理動做有什麼區別呢?若是是原子表達式(內部不包含其餘表達式的表達式),如id、數值等,這兩個事件方法沒什麼不一樣的(用哪個處理表達式均可以)。但若是是非原子表達式,就要考慮下使用enter仍是exit了。例如,下面的表達式:
3 * (20 / x * 43)
void myfun() { }
import org.antlr.v4.runtime.tree.ParseTreeListener; public interface CalcListener extends ParseTreeListener { void enterProg(CalcParser.ProgContext ctx); void exitProg(CalcParser.ProgContext ctx); void enterPrintExpr(CalcParser.PrintExprContext ctx); void exitPrintExpr(CalcParser.PrintExprContext ctx); void enterAssign(CalcParser.AssignContext ctx); void exitAssign(CalcParser.AssignContext ctx); void enterBlank(CalcParser.BlankContext ctx); void exitBlank(CalcParser.BlankContext ctx); void enterParens(CalcParser.ParensContext ctx); void exitParens(CalcParser.ParensContext ctx); void enterMulDiv(CalcParser.MulDivContext ctx); void exitMulDiv(CalcParser.MulDivContext ctx); void enterAddSub(CalcParser.AddSubContext ctx); void exitAddSub(CalcParser.AddSubContext ctx); void enterId(CalcParser.IdContext ctx); void exitId(CalcParser.IdContext ctx); void enterInt(CalcParser.IntContext ctx); void exitInt(CalcParser.IntContext ctx); }
public class MyCalcParser extends CalcBaseListener{ ... ... }
4 * 5
private Map<ParseTree,Integer> values = new HashMap<>();
public void setValue(ParseTree node, int value) { values.put(node,value); } public int getValue(ParseTree node) { try { return values.get(node); } catch (Exception e) { return 0; } }
import org.antlr.v4.runtime.tree.ParseTree; import java.util.HashMap; import java.util.Map; public class MyCalcParser extends CalcBaseListener{ private Map<ParseTree,Integer> values = new HashMap<>(); // 用於保存向上一層節點傳遞的值 Map<String, Integer> memory = new HashMap<String, Integer>(); // 用於保存變量的值 boolean error = false; // 用於標識分析的過程是否出錯 // 設置值 public void setValue(ParseTree node, int value) { values.put(node,value); } // 獲取值 public int getValue(ParseTree node) { try { return values.get(node); } catch (Exception e) { return 0; } } @Override public void enterPrintExpr(CalcParser.PrintExprContext ctx) { // 當開始處理表達式時,默認沒有錯誤 error = false; } @Override public void exitPrintExpr(CalcParser.PrintExprContext ctx) { if(!error) { // 只有在沒有錯誤的狀況下,纔會輸出表達式的值 System.out.println(getValue(ctx.expr())); } } // 必需要放在exitAssign裏 @Override public void exitAssign(CalcParser.AssignContext ctx) { String id = ctx.ID().getText(); // 獲取變量名 int value = getValue(ctx.expr()); // 獲取右側表達式的值 memory.put(id, value); // 保存變量 } // 必須在exitParens中完成 @Override public void exitParens(CalcParser.ParensContext ctx) { setValue(ctx,getValue(ctx.expr())); } // 計算乘法和除法(必須在exitMulDiv中完成) @Override public void exitMulDiv(CalcParser.MulDivContext ctx) { int left = getValue(ctx.expr(0)); // 獲取左操做數的值 int right = getValue(ctx.expr(1)); // 獲取右操做數的值 if ( ctx.op.getType() == CalcParser.MUL ) setValue(ctx,left * right); // 向上傳遞值 else setValue(ctx,left / right); // 向上傳遞值 } // 計算加法和減法(必須在exitAddSub中完成) @Override public void exitAddSub(CalcParser.AddSubContext ctx) { int left = getValue(ctx.expr(0)); // 獲取左操做數的值 int right = getValue(ctx.expr(1)); // 獲取右操做數的值 if ( ctx.op.getType() == CalcParser.ADD ) setValue(ctx,left + right); else setValue(ctx,left - right); } // 在enterId方法中也能夠 @Override public void exitId(CalcParser.IdContext ctx) { String id = ctx.ID().getText(); if ( memory.containsKey(id) ) { setValue(ctx,memory.get(id)); // 將變量的值向上傳遞 } else { // 變量不存在,輸出錯誤信息(包括行和列), System.err.println(String.format("行:%d, 列:%d, 變量<%s> 不存在!",ctx.getStart().getLine(),ctx.getStart().getCharPositionInLine() + 1, id)); error = true; } } // 處理int類型的值 @Override public void enterInt(CalcParser.IntContext ctx) { int value = Integer.valueOf(ctx.getText()); setValue(ctx, value); // 將整數值向上傳遞 } }
import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeWalker; import java.io.FileInputStream; import java.io.InputStream; public class MarvelListenerCalc { public static void main(String[] args) throws Exception { String inputFile = null; if ( args.length>0 ) { inputFile = args[0]; } else { System.out.println("語法格式:MarvelCalc inputfile"); return; } InputStream is = System.in; if ( inputFile!=null ) is = new FileInputStream(inputFile); CharStream input = CharStreams.fromStream(is); // 建立詞法分析器 CalcLexer lexer = new CalcLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); // CalcParser parser = new CalcParser(tokens); ParseTree tree = parser.prog(); MyCalcParser calc = new MyCalcParser(); ParseTreeWalker walker = new ParseTreeWalker(); // 開始遍歷AST walker.walk(calc, tree); } }
1+3 * 4 - 12 /6; x = 40; y = 13; x * y + 20 - 42/6; z = 12; 4; x + 41 * z - y;