Wie schreibe ich einen antlr4 Besucher
Ich versuche, einen Besucher für eine einfache antlr4-Grammatik zu schreiben - ich adaptiere aus dem folgenden Beispiel aus dem Buch:
* directory tour
* example: LabeledExpr.g4, EvalVisitor.java, Calc.java
Basierend auf dem Java-Code habe ich den folgenden Go-Code geschrieben:
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)
}
Ich zeige einen der Besucher oben, die anderen Besucher würden ähnlich geschrieben. Ich erhalte einige Kompilierungsfehler, wie in den obigen Kommentaren gezeigt. Wie behebe ich diesen Fehler?
Es scheint auch einen Fehler in der obersten Ebene des Besuchers zu geben, da ich beim Auskommentieren der Zeile "links + rechts" einen SIGSEGV-Fehler erhalten habe.
Als Referenz zeige ich unten den ursprünglichen Java-Code:
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
}
Auch die Grammatik ist wie folgt:
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
HINWEIS: Ich habe nach einem Beispiel-Besuchercode gesucht, aber bei 54992660 einige negative Kommentare erhalten , die auch zu Antlr-Problemen veröffentlicht werden. Diese Frage hat eine unvollständige und nicht kompilierbare Antwort. Arbeiten Besucher überhaupt im Go-Ziel von antlr4? Und gibt es dafür einen Beispielcode?
Antworten
Ich habe ein bisschen gegoogelt und den folgenden Go-Besucher zusammen gehackt:
Datei: ./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())
}
Datei: ./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];
Laden Sie zuerst die ANTLR 4.9-JAR herunter, generieren Sie die Parser- und Besucher-Go-Dateien und verschieben Sie sie in den antlr4demo
Ordner:
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
Wenn Sie jetzt das folgende Go-Skript ausführen:
Datei: ./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)
}
Sie sehen die Ausgabe:
$ go run main.go
1000 25/5 (1 + 2) * -3.14159265 = -9.424777950000001
All results: map[0:1000 1:5 2:-9.424777950000001]
Beachten Sie, dass ich in Go noch nie etwas programmiert habe: Ich bin sicher, dass der Code ein Chaos ist, aber hey, "es funktioniert".