代码之家  ›  专栏  ›  技术社区  ›  R71

如何写一个antlr4访问者

  •  0
  • R71  · 技术社区  · 4 年前

    我试图为一个简单的antlr4语法写一个访问者——我改编自书中的以下示例:

    * directory tour
    * example: LabeledExpr.g4, EvalVisitor.java, Calc.java
    

    基于java代码,我编写了以下go代码:

    package main
    import (
        "os"
        "./parser"
        "github.com/antlr/antlr4/runtime/Go/antlr"
    )
    
    type evalVisitor struct {
        *parser.BaseLabeledExprVisitor
    }
    
    func (v *evalVisitor) VisitAddSub(c *parser.AddSubContext) int {
        left := v.Visit(c.Expr(0))
        right := v.Visit(c.Expr(1))
        if(c.GetOp().GetTokenType() == parser.LabeledExprParserADD) {
            return left + right //error: invalid operation: left + right (operator + not defined on interface)
        } else {
            return  left - right
        }
    }
    
    func main() {
        input, _ := antlr.NewFileStream(os.Args[1])
        lexer := parser.NewLabeledExprLexer(input)
        stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
        p := parser.NewLabeledExprParser(stream)
        tree := p.Prog()
        var visitor evalVisitor
        visitor.Visit(tree)
    }
    

    我展示的是上面的一个访客,其他访客的书写方式也类似。我遇到了一些编译错误,如上面的注释所示。如何修复此错误?

    访问者的顶层调用似乎也有错误,因为当我注释掉“左+右”行时,我遇到了SIGSEGV错误。

    为了您的参考,我在下面显示了原始的java代码:

    public Integer visitAddSub(LabeledExprParser.AddSubContext 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.ADD ) return left + right;
        return left - right; // must be SUB
    }
    

    另外,语法是这样的:

    grammar LabeledExpr;
    prog:   stat+ ;
    
    stat:   expr NEWLINE                # printExpr
        |   ID '=' expr NEWLINE         # assign
        |   NEWLINE                     # blank
        ;
    
    expr:   expr op=('*'|'/') expr      # MulDiv
        |   expr op=('+'|'-') expr      # AddSub
        |   INT                         # int
        |   ID                          # id
        |   '(' expr ')'                # parens
        ;
    
    MUL :   '*' ; // assigns token name to '*' used above in grammar
    DIV :   '/' ;
    ADD :   '+' ;
    SUB :   '-' ;
    ID  :   [a-zA-Z]+ ;      // match identifiers
    INT :   [0-9]+ ;         // match integers
    NEWLINE:'\r'? '\n' ;     // return newlines to parser (is end-statement signal)
    WS  :   [ \t]+ -> skip ; // toss out whitespace
    

    注意:我四处搜索了一个示例访问者代码,但在以下位置遇到了一些负面评论 54992660 ,这也发布在antlrissues上。这个问题的答案不完整,无法编译。那么,访问者在antlr4的Go目标中工作吗?有可用的示例代码吗?

    0 回复  |  直到 4 年前
        1
  •  5
  •   Bart Kiers    4 年前

    我在谷歌上搜索了一下,然后一起黑掉了以下Go访问者:

    文件:/antlr4demo/eval_visitor.go

    package antlr4demo
    
    import (
        "strconv"
    
        "github.com/antlr/antlr4/runtime/Go/antlr"
    )
    
    type EvalVisitor struct {
        BaseExpressionVisitor
        Results map[int]float64
    }
    
    func (v *EvalVisitor) Visit(tree antlr.ParseTree) float64 {
        switch val := tree.(type) {
        case *ParseContext:
            return v.VisitParse(val)
        case *MultDivExprContext:
            return v.VisitMultDivExpr(val)
        case *NumberExprContext:
            return v.VisitNumberExpr(val)
        case *PlusSubExprContext:
            return v.VisitPlusSubExpr(val)
        case *NestedExprContext:
            return v.VisitNestedExpr(val)
        case *UnaryExprContext:
            return v.VisitUnaryExpr(val)
        default:
            panic("Unknown context")
        }
    }
    
    func (v *EvalVisitor) VisitParse(ctx *ParseContext) float64 {
        for index, expr := range ctx.expr_list {
            v.Results[index] = v.Visit(expr)
        }
        return v.Results[len(v.Results)-1]
    }
    
    func (v *EvalVisitor) VisitMultDivExpr(ctx *MultDivExprContext) float64 {
        lhs := v.Visit(ctx.lhs)
        rhs := v.Visit(ctx.rhs)
    
        if ctx.op.GetTokenType() == ExpressionLexerMULT {
            return lhs * rhs
        } else {
            return lhs / rhs
        }
    }
    
    func (v *EvalVisitor) VisitPlusSubExpr(ctx *PlusSubExprContext) float64 {
        lhs := v.Visit(ctx.lhs)
        rhs := v.Visit(ctx.rhs)
    
        if ctx.op.GetTokenType() == ExpressionLexerPLUS {
            return lhs + rhs
        } else {
            return lhs - rhs
        }
    }
    
    func (v *EvalVisitor) VisitNumberExpr(ctx *NumberExprContext) float64 {
        val, _ := strconv.ParseFloat(ctx.NUMBER().GetText(), 10)
        return val
    }
    
    func (v *EvalVisitor) VisitNestedExpr(ctx *NestedExprContext) float64 {
        return v.Visit(ctx.Expr())
    }
    
    func (v *EvalVisitor) VisitUnaryExpr(ctx *UnaryExprContext) float64 {
        return -v.Visit(ctx.Expr())
    }
    

    文件:/表达式g4

    grammar Expression;
    
    parse
     : expr_list+=expr+ EOF
     ;
    
    expr
     : '(' expr ')'                        #NestedExpr
     | SUB expr                            #UnaryExpr
     | lhs=expr op=( MULT | DIV ) rhs=expr #MultDivExpr
     | lhs=expr op=( PLUS | SUB ) rhs=expr #PlusSubExpr
     | NUMBER                              #NumberExpr
     ;
    
    MULT : '*';
    DIV  : '/';
    PLUS : '+';
    SUB  : '-';
    
    NUMBER
     : ( D* '.' )? D+
     ;
    
    SPACES
     : [ \t\r\n] -> skip
     ;
    
    fragment D : [0-9];
    

    首先下载ANTLR 4.9 JAR,生成解析器和访问者Go文件,并将它们移动到 antlr4demo 文件夹:

    wget https://www.antlr.org/download/antlr-4.9-complete.jar
    java -cp antlr-4.9-complete.jar org.antlr.v4.Tool -Dlanguage=Go -o antlr4demo -package antlr4demo -visitor -no-listener Expression.g4
    

    如果现在运行以下Go脚本:

    文件:/main.go

    package main
    
    import (
        "fmt"
    
        "./antlr4demo"
        "github.com/antlr/antlr4/runtime/Go/antlr"
    )
    
    func main() {
        expression := "1000 25/5 (1 + 2) * -3.14159265"
        input := antlr.NewInputStream(expression)
        lexer := antlr4demo.NewExpressionLexer(input)
        stream := antlr.NewCommonTokenStream(lexer, 0)
        parser := antlr4demo.NewExpressionParser(stream)
        parser.BuildParseTrees = true
        tree := parser.Parse()
    
        visitor := antlr4demo.EvalVisitor{
            Results: make(map[int]float64),
        }
    
        var result = visitor.Visit(tree)
    
        fmt.Println(expression, "=", result)
        fmt.Println("All results: ", visitor.Results)
    }
    

    您将看到输出:

    $ go run main.go
    1000 25/5 (1 + 2) * -3.14159265 = -9.424777950000001
    All results:  map[0:1000 1:5 2:-9.424777950000001]
    

    请注意,我从未在Go中编写过任何程序:我确信代码很混乱,但是嘿,“它有效”。

        2
  •  0
  •   Hans-J. Schmid    1 年前

    而不是。。。

    func (v *EvalVisitor) Visit(tree antlr.ParseTree) float64 {
        switch val := tree.(type) {
        case *ParseContext:
            return v.VisitParse(val)
        case *MultDivExprContext:
            return v.VisitMultDivExpr(val)
        case *NumberExprContext:
            return v.VisitNumberExpr(val)
        case *PlusSubExprContext:
            return v.VisitPlusSubExpr(val)
        case *NestedExprContext:
            return v.VisitNestedExpr(val)
        case *UnaryExprContext:
            return v.VisitUnaryExpr(val)
        default:
            panic("Unknown context")
        }
    }
    

    …你只是写:

    func (v *EvalVisitor) Visit(tree antlr.ParseTree) float64 {
        return tree.Accept(v)
    }