Калькуляторы
На этой паре рассматриваются два варианта написания собственного калькулятора средствами antlr4.
Вариант 1
Первый вариант с небольшими изменениями будет работать и в antlr3.
Calc.g4
grammar Calc;
input
: expr EOF { System.out.println("result = " + $expr.value); }
;
expr returns [int value]
: l1=expr MULT r1=expr { $value = $l1.value * $r1.value; }
| l2=expr DIV r2=expr { $value = $l2.value / $r2.value; }
| l3=expr PLUS r3=expr { $value = $l3.value + $r3.value; }
| l4=expr MINUS r4=expr { $value = $l4.value - $r4.value; }
| MINUS e1=expr { $value = -$e1.value; }
| INT { $value = Integer.parseInt($INT.text); }
| LPAR e2=expr RPAR { $value = $e2.value; }
;
INT : [0-9]+;
WS : [ \t\r\n]+ -> skip;
ID : [a-zA-Z_][a-zA-Z_0-9]*;
PLUS : '+';
MINUS : '-';
MULT : '*';
DIV : '/';
LPAR : '(';
RPAR : ')';
main.java
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class main {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
CalcLexer lexer = new CalcLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalcParser parser = new CalcParser(tokens);
ParseTree tree = parser.input();
System.out.println(tree.toStringTree(parser));
}
}
Вариант 2
Это новый, появившийся только в antrl4, подход. Основное его достоинство заключается в разделении грамматики и кода, что осуществляется при помощи аннотирования грамматики и создания визитора.
Calc.g4
grammar Calc;
input
: expr EOF # print
;
expr
: expr PLUS expr # plus
| INT # int
;
INT : [0-9]+;
WS : [ \t\r\n]+ -> skip;
PLUS : '+';
Просим anltr4 сгенерировать исходный код для визитора:
$ antlr4 -no-listener -visitor Calc.g4
CalculatorVisitor.java
public class CalculatorVisitor extends firstBaseVisitor<Integer> {
@Override
public Integer visitPlus(firstParser.PlusContext ctx) {
int left = visit(ctx.expr(0));
int right = visit(ctx.expr(1));
return left + right;
}
@Override
public Integer visitInt(firstParser.IntContext ctx) {
return Integer.parseInt(ctx.INT().getText());
}
@Override
public Integer visitPrint(firstParser.PrintContext ctx) {
System.out.println(visit(ctx.expr()));
return 0;
}
}
main.java
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
public class main {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRInputStream(System.in);
CalcLexer lexer = new CalcLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CalcParser parser = new CalcParser(tokens);
ParseTree tree = parser.input();
System.out.println(tree.toStringTree(parser));
CalculatorVisitor calcVisitor = new CalculatorVisitor();
int result = calcVisitor.visit(tree);
}
}
Компилируем все:
$ javac *.java
Домашнее задание
Расширить пример с калькулятором, построенном на патерне визитора, дополнив его полным арсеналом арифметических операций и возможностью объявления и использования переменных.