Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to build a JavaScript ANTLR visitor

I actually have a problem with the implementation of an antlr-visitor in JavaScript. I have already created a grammar. However, I found no documentation, example or tutorial. Somehow only multi-dimensional arrays are returned. A similar problem occured there: https://github.com/antlr/antlr4/issues/1995 Unfortunately there is no solution in this discussion. In Java I have already a finished visitor and therefore just want to convert it to JS. So I would prefer, if there is a solution without a listener. Thanks in advance for any help

EDIT: Here is the code for the visitor, the grammar and the startingtool.

const antlr4 = require('antlr4');
const grammarLexer = require('./SimpleGrammarLexer');
const grammarParser = require('./SimpleGrammarParser');
const extendGrammarVisitor = require('./ExtendGrammarVisitor.js');

export class SimpleGrammar {
    public static parseCode(formula: string) {
        const inputStream = new antlr4.InputStream(formula);
        const lexer = new grammarLexer.SimpleGrammarLexer(inputStream);
        const commonTokenStream = new antlr4.CommonTokenStream(lexer);
        const parser = new grammarParser.SimpleGrammarParser(commonTokenStream);
        const visitor = new extendGrammarVisitor.ExtendGrammarVisitor();
        const parseTree = parser.r();
        visitor.visitR(parseTree);
    }
}


grammar SimpleGrammar;
r: input;
INT    : [0-9]+;
DOUBLE : [0-9]+'.'[0-9]+;
PI     : 'pi';
E      : 'e';
POW    : '^';
NL     : '\n';
WS     : [ \t\r]+ -> skip;
ID     : [a-zA-Z_][a-zA-Z_0-9]*;

PLUS  : '+';
EQUAL : '=';
MINUS : '-';
MULT  : '*';
DIV   : '/';
LPAR  : '(';
RPAR  : ')';

input
    : setVar NL input     # ToSetVar
    | plusOrMinus NL? EOF # Calculate
    ;

setVar
    : ID EQUAL plusOrMinus # SetVariable
    ;


plusOrMinus 
    : plusOrMinus PLUS multOrDiv  # Plus
    | plusOrMinus MINUS multOrDiv # Minus
    | multOrDiv                   # ToMultOrDiv
    ;

multOrDiv
    : multOrDiv MULT pow # Multiplication
    | multOrDiv DIV pow  # Division
    | pow                # ToPow
    ;

pow
    : unaryMinus (POW pow)? # Power
    ;

unaryMinus
    : MINUS unaryMinus # ChangeSign
    | atom             # ToAtom
    ;

atom
    : PI                    # ConstantPI
    | E                     # ConstantE
    | DOUBLE                # Double
    | INT                   # Int
    | ID                    # Variable
    | LPAR plusOrMinus RPAR # Braces
    ;


"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var simpleGrammarVisitor = require('./SimpleGrammarVisitor.js');
var ExtendGrammarVisitor = (function (_super) {
    __extends(ExtendGrammarVisitor, _super);
    function ExtendGrammarVisitor() {
        _super.apply(this, arguments);
    }
    ExtendGrammarVisitor.prototype.visitR = function(ctx) {
        return this.visitChildren(ctx);
    };
    ExtendGrammarVisitor.prototype.visitToSetVar = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitCalculate = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitSetVariable = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitToMultOrDiv = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitPlus = function (ctx) {
        var example = this.visit(ctx.plusorminus(0)); // ???
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitMinus = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitMultiplication = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitDivision = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitToPow = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitPower = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitChangeSign = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitToAtom = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitConstantPI = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitConstantE = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitDouble = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitInt = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitVariable = function (ctx) {
        return this.visitChildren(ctx);
    };

    ExtendGrammarVisitor.prototype.visitBraces = function (ctx) {
        return this.visitChildren(ctx);
    };
    return ExtendGrammarVisitor;
}(simpleGrammarVisitor.SimpleGrammarVisitor));
exports.ExtendGrammarVisitor = ExtendGrammarVisitor;

At this point I don't know how to continue and implement the methods for the visitor.

like image 240
Simon Avatar asked Feb 21 '19 21:02

Simon


People also ask

What is visitor in Antlr?

ANTLR Visitors The difference between listener and visitor mechanisms is listener methods are called by the ANTLR-provided walker object, whereas visitor methods must walk their children with explicit visit calls. Forgetting to invoke visit() on a node's children means those subtrees don't get visited.

Is Antlr a compiler?

In computer-based language recognition, ANTLR (pronounced antler), or ANother Tool for Language Recognition, is a parser generator that uses LL(*) for parsing. ANTLR is the successor to the Purdue Compiler Construction Tool Set (PCCTS), first developed in 1989, and is under active development.

How does an Antlr work?

ANTLR (ANother Tool for Language Recognition) is a tool for processing structured text. It does this by giving us access to language processing primitives like lexers, grammars, and parsers as well as the runtime to process text against them. It's often used to build tools and frameworks.


1 Answers

In JavaScript visitChildren returns an array containing the result of visiting the children. So if a node has two children for example, then visitChildren(node) will return [visit(node.child1), visit(node.child2)] (whereas in Java you'd only get the result of visit(node.child2) and the result of visit(node.child1) would be discarded). If it has only one child, you get an array containing only one element (whereas in Java, you'd just get the element without the array).

In your code you're calling visitChildren everywhere, so that's why you end up with nested arrays. If you don't want arrays, you either shouldn't call visitChildren or call it, unpack the array and then return something else. For example here's two ways how one might implement visitPlus to return the result of the addition (if that's the goal):

// Using visitChildren and unpacking
ExtendGrammarVisitor.prototype.visitPlus = function (ctx) {
    let [lhs, rhs] = this.visitChildren(ctx);
    return lhs+rhs;
};

// Or without visitChildren
ExtendGrammarVisitor.prototype.visitPlus = function (ctx) {
    return this.visit(ctx.plusOrMinus()) + this.visit(ctx.multOrDiv());
    // If you added labels to the grammar, you could write the above more readably
    // using labels like `lhs` and `rhs` instead of `plusOrMinus` and `multOrDiv`,
    // so the reader doesn't have to remember which one is the right and which one
    // the left operand
};

Of course, for this to work, all the other visitor methods also need to be changed to return numbers. And if you want your visitor to do something other than evaluating the expression, you need to adjust the code accordingly.

PS: You can simplify your grammar simply by making use of ANTLR4's precedence handling rules, which allow you to list infix operators in descending order of precedence with the operators being left-associative unless you specify <assoc=right>. That way you can consolidate all of your expression rules into a single one like this:

expr
    : PI                                     # ConstantPI
    | E                                      # ConstantE
    | DOUBLE                                 # Double
    | INT                                    # Int
    | ID                                     # Variable
    | LPAR expr RPAR                         # Braces
    | MINUS expr                             # ChangeSign
    | <assoc=right> lhs=expr op=POW rhs=expr # InfixExpression
    | lhs=expr op=(MULT|DIV) rhs=expr        # InfixExpression
    | lhs=expr op=(PLUS|MINUS) rhs=expr      # InfixExpression
    ;
like image 154
sepp2k Avatar answered Sep 21 '22 00:09

sepp2k