ANTLR Visitor类使用C#从表达式树中获取表达式

时间:2017-05-09 15:46:51

标签: c# antlr antlr4

在为我的antlr程序编写访问者时遇到困难,表达式为

multiplyingExpression ((PLUS | MINUS) multiplyingExpression)*

我想得到表达式的左侧部分,获取运算符类型和表达式的右侧部分,请帮助。这就是我到目前为止所拥有的

private double WalkLeft(testGrammerParser.MultiplyingExpressionContext context)
        {
            return Visit(context.GetRuleContext<testGrammerParser.MultiplyingExpressionContext>(0));
        }

        private double WalkRight(testGrammerParser.MultiplyingExpressionContext context)
        {
            return Visit(context.GetRuleContext<testGrammerParser.MultiplyingExpressionContext>(1));
        }

,我得到的错误是Object reference not set to an instance of an object

提前致谢。

修改

我更喜欢这样的事情

public Integer visitMulDiv(LabeledExprParser.MulDivContext ctx) {
    int left = visit(ctx.expr(0)); // get value of left subexpression
    int right = visit(ctx.expr(1)); // get value of right subexpression
    if ( ctx.op.getType() == LabeledExprParser.MUL ) return left * right; //check operation type
    return left / right; // must be DIV
}

编辑2(语法)

这是使用

的语法
grammar testGrammer;

/*
 * Parser Rules
 */

 compileUnit
    :   expression + EOF
    ;

expression
   : multiplyingExpression ((PLUS | MINUS) multiplyingExpression)*
   ;

multiplyingExpression
   : powExpression ((TIMES | DIV) powExpression)*
   ;

powExpression
   : atom (POW atom)*
   ;

atom
   : scientific
   | variable
   | LPAREN expression RPAREN
   | func
   ;

scientific
   : number (E number)?
   ;

func
   : funcname LPAREN expression RPAREN
   ;

funcname
   : COS
   | TAN
   | SIN
   | ACOS
   | ATAN
   | ASIN
   | LOG
   | LN
   ;

number
   : MINUS? DIGIT + (POINT DIGIT +)?
   ;

variable
   : MINUS? LETTER (LETTER | DIGIT)*
   ;

COS
   : 'cos'
   ;

SIN
   : 'sin'
   ;

TAN
   : 'tan'
   ;

ACOS
   : 'acos'
   ;

ASIN
   : 'asin'
   ;

ATAN
   : 'atan'
   ;

LN
   : 'ln'
   ;

LOG
   : 'log'
   ;

LPAREN
   : '('
   ;

RPAREN
   : ')'
   ;

PLUS
   : '+'
   ;

MINUS
   : '-'
   ;

TIMES
   : '*'
   ;

DIV
   : '/'
   ;

POINT
   : '.'
   ;

E
   : 'e' | 'E'
   ;

POW
   : '^'
   ;

LETTER
   : ('a' .. 'z') | ('A' .. 'Z')
   ;

DIGIT
   : ('0' .. '9')
   ;

/*
 * Lexer Rules
 */

WS
    :[ \r\n\t] + -> channel(HIDDEN)
    ;

1 个答案:

答案 0 :(得分:3)

我终于做到了,这是完整的访客类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExpressionParser
{
    class testVisitor: testGrammerBaseVisitor<double>
    {
        public override double VisitCompileUnit(testGrammerParser.CompileUnitContext context)
        {
            return Visit(context.expression(0));
        }

        public override double VisitExpression(testGrammerParser.ExpressionContext context)
        {
            var left = context.multiplyingExpression(0);
            var right = context.multiplyingExpression(1);

            if (right != null)
            {
                if (context.PLUS(0) == null)
                {
                    return Visit(left) - Visit(right);
                }

                return Visit(left) + Visit(right);
            }

            return Visit(left);
        }

        public override double VisitMultiplyingExpression(testGrammerParser.MultiplyingExpressionContext context)
        {
            var left = context.powExpression(0);
            var right = context.powExpression(1);

            if (right != null)
            {
                if (context.DIV(0) == null)
                {
                    return Visit(left) * Visit(right);
                }

                return Visit(left) / Visit(right);
            }

            return Visit(left);
        }

        public override double VisitPowExpression(testGrammerParser.PowExpressionContext context)
        {
            var left = context.atom(0);
            var right = context.atom(1);

            if (right != null)
            {
                return Math.Pow(Visit(left), Visit(right));
            }

            return Visit(left);
        }

        public override double VisitAtom(testGrammerParser.AtomContext context)
        {
            if (context.scientific() != null)
            {
                return Visit(context.scientific());
            }

            if (context.variable() != null)
            {
                return Visit(context.variable());
            }

            if (context.expression() != null) //need to check this out
            {
                return Visit(context.expression());
            }

            return Visit(context.func());
        }

        public override double VisitScientific(testGrammerParser.ScientificContext context)
        {
            var left = context.number(0);
            var right = context.number(1);

            if (right != null)
            {
                return Visit(left) * Math.E * Visit(right);
            }

            return Visit(left);
        }

        public override double VisitFunc(testGrammerParser.FuncContext context)
        {
            var type = context.funcname().GetText();

            switch (type)
            {
                case "cos":
                    return Math.Cos(Visit(context.expression()));

                case "sin":
                    return Math.Sin(Visit(context.expression()));

                case "tan":
                    return Math.Tan(Visit(context.expression()));

                case "acos":
                    return Math.Acos(Visit(context.expression()));

                case "asin":
                    return Math.Asin(Visit(context.expression()));

                case "atan":
                    return Math.Atan(Visit(context.expression()));

                case "ln":
                    return Math.Log(Visit(context.expression()));

                case "log":
                    return Math.Log(Visit(context.expression()));
            }

            return Visit(context.expression());
        }

        public override double VisitNumber(testGrammerParser.NumberContext context)
        {
            var left = context.DIGIT(0);
            var right = context.DIGIT(1);
            int minus = 1;

            if (context.MINUS() != null)
            {
                minus = -1;
            }

            if (right != null)
            {
                return (minus * Visit(left)) + Visit(right);
            }

            return minus * Visit(left);
        }

        public override double VisitVariable(testGrammerParser.VariableContext context)
        {
            return base.VisitVariable(context); // yet to implement this
        }

        public override double VisitTerminal(Antlr4.Runtime.Tree.ITerminalNode node)
        {
            return double.Parse(node.GetText());
        }

    }
}

这是它的语法

grammar testGrammer;

/*
 * Parser Rules
 */

 compileUnit
    :   expression + EOF
    ;

expression
   : multiplyingExpression ((PLUS | MINUS) multiplyingExpression)*
   ;

multiplyingExpression
   : powExpression ((TIMES | DIV) powExpression)*
   ;

powExpression
   : atom (POW atom)*
   ;

atom
   : scientific
   | variable
   | LPAREN expression RPAREN
   | func
   ;

scientific
   : number (E number)?
   ;

func
   : funcname LPAREN expression RPAREN
   ;

funcname
   : COS
   | TAN
   | SIN
   | ACOS
   | ATAN
   | ASIN
   | LOG
   | LN
   ;

number
   : MINUS? DIGIT + (POINT DIGIT +)?
   ;

variable
   : MINUS? LETTER (LETTER | DIGIT)*
   ;

COS
   : 'cos'
   ;

SIN
   : 'sin'
   ;

TAN
   : 'tan'
   ;

ACOS
   : 'acos'
   ;

ASIN
   : 'asin'
   ;

ATAN
   : 'atan'
   ;

LN
   : 'ln'
   ;

LOG
   : 'log'
   ;

LPAREN
   : '('
   ;

RPAREN
   : ')'
   ;

PLUS
   : '+'
   ;

MINUS
   : '-'
   ;

TIMES
   : '*'
   ;

DIV
   : '/'
   ;

POINT
   : '.'
   ;

E
   : 'e' | 'E'
   ;

POW
   : '^'
   ;

LETTER
   : ('a' .. 'z') | ('A' .. 'Z')
   ;

DIGIT
   : ('0' .. '9')
   ;

/*
 * Lexer Rules
 */

WS
    :[ \r\n\t] + -> channel(HIDDEN)
    ;

我用来测试它的主要类是

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Antlr4.Runtime;
using Antlr4.Runtime.Tree;
using System.Windows.Forms;


namespace ExpressionParser
{
    class Program
    {
        static void Main(string[] args)
        {
            String input = "cos(3*3+3)";

            ITokenSource lexer = new testGrammerLexer(new AntlrInputStream(input));
            ITokenStream tokens = new CommonTokenStream(lexer);
            testGrammerParser parser = new testGrammerParser(tokens);
            parser.AddErrorListener(new ThrowExceptionErrorListener());
            parser.BuildParseTree = true;
            IParseTree tree;

            try
            {
                tree = parser.compileUnit();
                var visitor = new testVisitor();
                var results = visitor.Visit(tree);

                MessageBox.Show(results + "");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}

#NailedIt