×

使用antlr4, 用ts/js还原protobuf生成的java代码为pb (二)

96
饥渴的斑马
2017.03.22 16:33* 字数 258

目录:

现在开始写一个简单公式计算器的parser.

expr.g4

grammar Expr    ;

prog: stat+     ;
stat: expr NEWLINE              # printExpr
    | ID '=' expr NEWLINE       # assign
    | NEWLINE                   # empty
    ;
expr: expr op=(MUL|DIV) expr    # MulDiv
    | expr op=(ADD|SUB) expr    # AddSub
    | INT                       # int
    | ID                        # id
    | '(' expr ')'              # parentheses
    ;
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 ;

#号后面的标签必不可少, 这是用来方便visitor来处理的, 具体可以参阅calculator-visitor

这里我们使用常见的visitor模式来处理(另一种是用listener), 使用命令如下

antlr4ts -visitor ./src/antlr/Expr.g4      

CustomVisitor代码如下

import {ExprVisitor} from "./antlr/ExprVisitor";
import {
    AddSubContext,
    AssignContext,
    ExprParser,
    IdContext,
    IntContext,
    MulDivContext,
    ParenthesesContext,
    PrintExprContext
} from "./antlr/ExprParser";
import {AbstractParseTreeVisitor} from "antlr4ts/tree";
export class CustomVisitor extends AbstractParseTreeVisitor<number> implements ExprVisitor<number> {
    /** "memory" for our calculator; variable/value pairs go here */
    memory: Map<string, number> = new Map<string, number>();

    /** ID '=' expr */
    public visitAssign(ctx: AssignContext): number {
        let id = ctx.ID().text;  // id is left-hand side of '='
        let value = this.visit(ctx.expr());   // compute value of expression on right
        this.memory.set(id, value);           // store it in our memory
        return value;
    }

    /** expr */
    public visitPrintExpr(ctx: PrintExprContext) {
        return this.visit(ctx.expr());                          // evaluate the expr child
    }

    /** INT */
    public visitInt(ctx: IntContext) {
        return parseInt(ctx.INT().text);
    }

    /** ID */
    public visitId(ctx: IdContext) {
        let id = ctx.ID().text;
        if (this.memory.has(id)) {
            let value = this.memory.get(id);
            console.log(`this is a assign expr, the [${id}] value is [${value}]`)
            return value;
        }
        return 0;
    }

    /** expr op=('*'|'/') expr */
    public visitMulDiv(ctx: MulDivContext) {
        let left = this.visit(ctx.expr(0));  // get value of left subexpression
        let right = this.visit(ctx.expr(1)); // get value of right subexpression
        if (ctx._op.type == ExprParser.MUL) return left * right;
        return left / right; // must be DIV
    }

    /** expr op=('+'|'-') expr */
    public  visitAddSub(ctx: AddSubContext) {
        let left = this.visit(ctx.expr(0));  // get value of left subexpression
        let right = this.visit(ctx.expr(1)); // get value of right subexpression
        if (ctx._op.type == ExprParser.ADD) return left + right;
        return left - right; // must be SUB
    }

    /** '(' expr ')' */
    public visitParentheses(ctx: ParenthesesContext) {
        return this.visit(ctx.expr()); // return child expr's value
    }

    protected defaultResult(): number {
        return undefined;
    }

}

测试代码如下

import {ANTLRInputStream, CommonTokenStream} from "antlr4ts";
import {ExprLexer} from "./antlr/ExprLexer";
import {ExprParser, ProgContext} from "./antlr/ExprParser";
import {CustomVisitor} from "./CustomVisitor";

// Create the lexer and parser
let inputStream = new ANTLRInputStream("1+2+3\n");
let lexer = new ExprLexer(inputStream);
let tokenStream = new CommonTokenStream(lexer);
let parser = new ExprParser(tokenStream);

// Parse the input, where `compilationUnit` is whatever entry point you defined
let result: ProgContext = parser.prog();

// console.log(result);
let value = (new CustomVisitor()).visit(result);

console.log(value);

由于代码中可能包含赋值表达式, 所以对于赋值表达式在代码里作了打印操作.

现改变let inputStream = new ANTLRInputStream("1+2+3\n");中的1+2+3\n 为其他计算表达式试试看结果对不对

下一章将开始解释java代码

antlr
Web note ad 1