Comment écrire un visiteur antlr4
J'essaie d'écrire un visiteur pour une grammaire antlr4 simple - je m'adapte à partir de l'exemple suivant du livre:
* directory tour
* example: LabeledExpr.g4, EvalVisitor.java, Calc.java
Sur la base du code java, j'ai écrit le code go suivant:
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)
}
Je montre l'un des visiteurs ci-dessus, les autres visiteurs seraient écrits de la même manière. Je reçois des erreurs de compilation comme indiqué dans les commentaires ci-dessus. Comment corriger cette erreur?
Il semble également y avoir une erreur dans l'appel de niveau supérieur du visiteur, car lorsque j'ai commenté la ligne "gauche + droite", j'ai eu une erreur SIGSEGV.
Pour votre référence, je montre ci-dessous le code java d'origine:
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
}
En outre, la grammaire est comme ceci:
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
REMARQUE: j'ai cherché un exemple de code de visiteur, mais j'ai rencontré des commentaires négatifs au 54992660 , et cela est également publié sur les problèmes antlr. Cette question a une réponse incomplète et ne se compile pas. Alors, est-ce que les visiteurs travaillent du tout dans la cible Go d'antlr4? Et existe-t-il un exemple de code disponible pour cela?
Réponses
J'ai un peu cherché sur Google et piraté ensemble le visiteur Go suivant:
Fichier: ./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())
}
Fichier: ./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];
Commencez par télécharger le fichier JAR ANTLR 4.9, générez l'analyseur et les fichiers Go des visiteurs et déplacez-les dans le antlr4demo
dossier:
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
Si vous exécutez maintenant le script Go suivant:
Fichier: ./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)
}
vous verrez la sortie:
$ go run main.go
1000 25/5 (1 + 2) * -3.14159265 = -9.424777950000001
All results: map[0:1000 1:5 2:-9.424777950000001]
Notez que je n'ai jamais rien programmé en Go: je suis sûr que le code est en désordre, mais bon, "ça marche".