如何构建JavaScript ANTLR访问者

Sim*_*mon 5 javascript antlr4

我实际上在JavaScript中实现了一个antlr-visitor有一个问题.我已经创建了一个语法.但是,我没有找到任何文档,示例或教程.不知何故,只返回多维数组.在那里发生了类似的问题:https://github.com/antlr/antlr4/issues/1995 不幸的是,在这个讨论中没有解决方案.在Java中,我已经有一个完成的访问者,因此只想将其转换为JS.所以我更愿意,如果有一个没有听众的解决方案.在此先感谢您的帮助

编辑:这是访问者,语法和起始工具的代码.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


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
    ;
Run Code Online (Sandbox Code Playgroud)


"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;
Run Code Online (Sandbox Code Playgroud)

此时我不知道如何继续并为访问者实现这些方法.

sep*_*p2k 2

在 JavaScript 中visitChildren返回一个包含访问子项结果的数组。因此,如果一个节点有两个子节点,则 thenvisitChildren(node)将返回[visit(node.child1), visit(node.child2)](而在 Java 中,您只能得到 的结果visit(node.child2),并且 的结果visit(node.child1)将被丢弃)。如果它只有一个子元素,您将得到一个仅包含一个元素的数组(而在 Java 中,您将只获得不包含数组的元素)。

在您的代码中,您到处调用visitChildren,因此这就是您最终得到嵌套数组的原因。如果您不需要数组,则要么不调用它visitChildren,要么调用它,解压数组然后返回其他内容。例如,这里有两种方法可以实现visitPlus返回加法结果(如果这是目标):

// 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
};
Run Code Online (Sandbox Code Playgroud)

当然,要使其起作用,所有其他访问者方法也需要更改为返回数字。如果您希望访问者执行除计算表达式之外的其他操作,则需要相应地调整代码。

PS:您可以简单地通过使用 ANTLR4 的优先级处理规则来简化语法,该规则允许您按优先级降序列出中缀运算符,除非您指定 ,否则运算符是左关联的<assoc=right>。这样您就可以将所有表达式规则合并为一个规则,如下所示:

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
    ;
Run Code Online (Sandbox Code Playgroud)