CC S01E02

Oct. 16, 2013, 11:58 p.m.

Калькуляторы

На этой паре рассматриваются два варианта написания собственного калькулятора средствами 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

Домашнее задание

Расширить пример с калькулятором, построенном на патерне визитора, дополнив его полным арсеналом арифметических операций и возможностью объявления и использования переменных.

comments powered by Disqus