使用Antlr4实现计算器[Python]

     2016年07月03日       江南兔子哥       非专业笔记->Python       antlr4 

语法和词法

编辑语法文件 Calculator.g4 如下

grammar Calculator;

expr : expr op=('*'|'/') expr     # MULDiv
     | expr op=('+'|'-') expr     # AddSub
     | INT                        # int
     | '(' expr ')'               # parens
     | op=('-'|'+') INT           # prehead1
     | op=('-'|'+') '(' expr ')'  # prehead2
     ;

INT  : [0-9]+;
MUL  : '*' ;
DIV  : '/' ;
ADD  : '+' ;
SUB  : '-' ;

WS   : [ \t\r\n]+ -> skip;

使用Antlr4 生成解析器框架

antlr4 -visitor -Dlanguage=Python2 Calculator.g4

修改Vistor框架代码,实现计算器的解析和计算

  • 修改 CalculatorVisitor.py文件
# Generated from Calculator.g4 by ANTLR 4.5.3
from antlr4 import *
from CalculatorLexer import CalculatorLexer

class CalculatorVisitor(ParseTreeVisitor) :
    # | INT                     # int
    def visitInt(self, ctx):
        return int(ctx.getText())

    # | expr op=('+'|'-') expr  # AddSub
    def visitAddSub(self, ctx):
        left = self.visit(ctx.expr(0))
        right = self.visit(ctx.expr(1))
        if ctx.op.type == CalculatorLexer.ADD:
            return left + right
        else:
            return left - right

    # | expr op=('*'|'/') expr  # MULDiv
    def visitMULDiv(self, ctx):
        left = self.visit(ctx.expr(0))
        right = self.visit(ctx.expr(1))
        if ctx.op.type == CalculatorLexer.MUL:
            return left * right
        else:
            return left / right

    # | op=('-'|'+') INT       # prehead1
    def visitPrehead1(self, ctx):
        value = int(ctx.INT().getText())
        if ctx.op.type == CalculatorLexer.ADD:
            return value
        else:
            return -(value)

    # | op=('-'|'+') '(' expr ')'      # prehead2
    def visitPrehead2(self, ctx):
        value = self.visit(ctx.expr())
        if ctx.op.type == CalculatorLexer.ADD:
            return self.visit(ctx.expr())
        else:
            return -self.visit(ctx.expr())

    # | '(' expr ')'            # parens
    def visitParens(self, ctx):
        return self.visit(ctx.expr())

编写主函数,调用解析器

  • 编写test.py如下:
import sys

from antlr4 import *
from CalculatorParser import CalculatorParser
from CalculatorLexer import CalculatorLexer
from CalculatorListener import CalculatorListener
from CalculatorVisitor import CalculatorVisitor


def main(argv):
    input = FileStream(argv[1])
    lexer = CalculatorLexer(input)
    stream = CommonTokenStream(lexer)
    parser = CalculatorParser(stream)
    tree = parser.expr()
    v = CalculatorVisitor()
    print v.visit(tree)

if __name__ == '__main__':
    main(sys.argv)
  • 做个测试
Case 1:

cat "7+ 3 * (10 / (12 / (3 + 1) - 1))" > cal.txt
python main.py  cal.txt
输出结果:22

使用bc计算
cat cal.txt | bc
输出结果: 22


Case 2:

cat "-7+ 3 * (10 / (12 / (3 + 1) - 1))" > cal.txt
python main.py  cal.txt
输出结果:8

使用bc计算
cat cal.txt | bc
输出结果: 8

Case 3:
cat "-(7+ 3 * (10 / (12 / (3 + 1) - 1)))" > cal.txt
python main.py  cal.txt
输出结果:-22

使用bc计算
cat cal.txt | bc
输出结果: -22