Простейший транслятор
Зададимся целью транслировать арифметические выражения в их аналоги, например, на языке lisp (scheme). Рассмотрим упрощенный случай, когда грамматикой допускаются только суммы чисел.
Решение с использованием visitor'а
Calc.g4
grammar Calc;
input
: expr EOF
;
expr
: l3=expr PLUS r3=expr # plus
| INT # int
;
INT : [0-9]+;
WS : [ \t\r\n]+ -> skip;
ID : [a-zA-Z_][a-zA-Z_0-9]*;
PLUS : '+';
MINUS : '-';
MULT : '*';
DIV : '/';
LPAR : '(';
RPAR : ')';
Чтобы сгенерировать код для visitor'а, запускаем antlr4 со следующими параметрами:
$ antlr4 -no-listener -visitor Calc.g4
CalculationVisitor.java
public class CalculationVisitor extends CalcBaseVisitor<Integer> {
@Override
public Integer visitPlus(CalcParser.PlusContext ctx) {
System.out.print("(+ ");
visit(ctx.expr(0));
visit(ctx.expr(1));
System.out.print(") ");
return 0;
}
@Override
public Integer visitInt(CalcParser.IntContext ctx) {
System.out.print(ctx.INT().getText() + " ");
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));
CalculationVisitor eval = new CalculationVisitor();
eval.visit(tree);
}
}
Решение с использованием listener'а
Calc.g4
grammar Calc;
input
: expr EOF
;
expr
: l3=expr PLUS r3=expr
| INT
;
INT : [0-9]+;
WS : [ \t\r\n]+ -> skip;
ID : [a-zA-Z_][a-zA-Z_0-9]*;
PLUS : '+';
MINUS : '-';
MULT : '*';
DIV : '/';
LPAR : '(';
RPAR : ')';
Запускаем antlr4:
$ antlr4 -listener Calc.g4
CalculationListener.java
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.TokenStreamRewriter;
public class CalculationListener extends CalcBaseListener {
@Override
public void enterExpr(CalcParser.ExprContext ctx) {
if (ctx.INT() != null) {
System.out.print(ctx.INT() + " ");
}
else if (ctx.PLUS() != null) {
System.out.print("(+ ");
}
}
@Override
public void exitExpr(CalcParser.ExprContext ctx) {
if (ctx.PLUS() != null)
System.out.print(") ");
}
}
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));
ParseTreeWalker walker = new ParseTreeWalker();
CalculationListener listener = new CalculationListener();
walker.walk(listener, tree);
}
}
Домашнее задание
Создать при помощи listener'а транслятор микроязыка с переменными, арифметическими выражениями и вводом-выводом в scheme.
Пример
Вход
read a
b = a - 1
display a ** b * (-a + 2 * b + b / 2 - 1)
read c
display a + c
Выход
(define a (read))
(define b (- a 1))
(display (* (expt a b) (- (+ (- a) (* 2 b) (/ b 2)) 1)))
(define c (read))
(display (+ a c))