samedi 26 février 2022

Is Antlr4 Visitor really a visitor?

I've been learning how to make an AST with Antlr4's visitor and after reading Terrance Parr's book, and multiple forums on the topic of AST generation specifically with Antlr visitors, it seems that the standard approach for doing this involves overriding the Antlr generated visit methods like this (From The Definitive Antlr 4 Reference).

public static class EvalVisitor extends LExprBaseVisitor<Integer> {
    public Integer visitMult(LExprParser.MultContext ctx) {
        return visit(ctx.e(0)) * visit(ctx.e(1));
    }
    public Integer visitAdd(LExprParser.AddContext ctx) {
        return visit(ctx.e(0)) + visit(ctx.e(1));
    }
    public Integer visitInt(LExprParser.IntContext ctx) {
        return Integer.valueOf(ctx.INT().getText());
    }
}

In this book, and many threads, typically the way to approach overriding the base visitor is to call visit() within the visitor itself. Seemingly, the visitor itself governs the traversal of the parse tree.

However, when I look anywhere else about how the visitor pattern is typically implemented, the visit method almost never calls visit() within itself. Usually the accept method is used within a node or element to govern the traversal of a tree structure and calls accept on the node's children to do so, while the visitor mainly handles what operations occur for that particular visit, on that particular tree's node.

Wikipedia Example, but reduced for most important parts

public class ExpressionPrintingVisitor
{
    public void PrintAddition(Addition addition)
    {
        double leftValue = addition.Left.GetValue();
        double rightValue = addition.Right.GetValue();
        var sum = addition.GetValue();
        Console.WriteLine("{0} + {1} = {2}", leftValue, rightValue, sum);
    }
}

public class Addition : Expression
{
    public Expression Left { get; set; }
    public Expression Right { get; set; }

    public Addition(Expression left, Expression right)
    {
        Left = left;
        Right = right;
    }
    
    public override void Accept(ExpressionPrintingVisitor v)
    {
        Left.Accept(v);
        Right.Accept(v);
        v.PrintAddition(this);
    }
    
    public override double GetValue()
    {
        return Left.GetValue() + Right.GetValue();    
    }
}

In this particular example, PrintAddition() is the "visit()" method being called by accept to perform the operation.

Am I misunderstanding the visitor pattern? Is the antlr4 visitor not a standard visitor? Or is more happening under the hood of the Antlr visitor that I'm not understanding. To me, the simplified description of the visitor pattern implementation is using an "accept" method on a node's children to traverse a tree, while calling the visitor to perform operations on the children of that node.

I appreciate any help on the topic, and apologize if anything is unclear.

Aucun commentaire:

Enregistrer un commentaire