Warm tip: This article is reproduced from serverfault.com, please click

go-如何编写antlr4访问者

(go - How to write a antlr4 visitor)

发布于 2020-11-27 14:23:35

我正在尝试为一个简单的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)
}

我在上面显示一个访客,其他访客的写法也类似。我收到一些编译错误,如上面的注释所示。如何解决这个错误?

访问者的顶层调用中似乎也有一个错误,因为当我注释掉“ left + right”这一行时,出现了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上我遇到了一些负面评论,并且该问题也发布在antlr问题上。该问题的答案不完整,无法编译。那么,访客在antlr4的Go目标中是否工作?并有一个示例代码可用于此吗?

Questioner
R71
Viewed
22
Bart Kiers 2020-11-29 19:27:08

我用Google搜索了一下,并一起入侵了以下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())
}

文件:./ Expression.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,生成解析器和visitor 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中编写过任何程序:我确定代码是一团糟,但是,“它可以工作”。