कैसे एक antlr4 आगंतुक लिखने के लिए

Nov 27 2020

मैं एक साधारण antlr4 व्याकरण के लिए एक आगंतुक लिखने की कोशिश कर रहा हूं - मैं पुस्तक से निम्नलिखित उदाहरण से आकर्षित कर रहा हूं:

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

जावा कोड के आधार पर, मैंने निम्नलिखित गो कोड लिखा है:

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 गलती मिली।

आपके संदर्भ के लिए, मैं मूल जावा कोड नीचे दिखा रहा हूं:

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 पर कुछ नकारात्मक टिप्पणियां कीं , और यह भी एंटीवायरल मुद्दों पर पोस्ट किया गया है। उस प्रश्न का एक उत्तर है जो अपूर्ण है और संकलन नहीं करता है। तो, क्या आगंतुक antlr4 के लक्ष्य में सभी काम करते हैं? और क्या इसके लिए एक नमूना कोड उपलब्ध है?

जवाब

1 BartKiers Nov 28 2020 at 13:16

मैंने थोड़ा सा Googled, और निम्नलिखित 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 को डाउनलोड करें, पार्सर और विजिटर को जनरेट करें और उन्हें 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

यदि आप अब निम्नलिखित गो स्क्रिप्ट चलाते हैं:

फाइल: ./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]

ध्यान दें कि मैंने कभी भी गो में कुछ भी प्रोग्राम नहीं किया है: मुझे यकीन है कि कोड एक गड़बड़ है, लेकिन हे, "यह काम करता है"।