Antlr4 访问者函数将结果包装在数组中(更准确地说是在接受函数中)

问题描述 投票:0回答:1

我正在使用目标语言 JavaScript 编写 antlr4 解析器,并且我需要使用访问者从我的树中获取自定义输出(一个数组,其他数组级别上的每个树级别)。但是我在从访问函数返回值时遇到了一些麻烦,当我尝试从每个访问函数返回值时,我得到的结果包装在数组中(我不需要这个)。我尝试调试我的代码,但是当创建此数组时,我无法真正看到antlr4库中的代码部分(可能当accept函数返回一个值时,accept在visitChildren方法中调用)。我有默认操作,它们作为链工作,直到值与具体操作匹配(表达式 -> 关系 -> 添加 -> mult -> 等)。

visitor.js:

visitFormula(ctx) {
    let result = [];

    for (let i = 1; i < ctx.getChildCount() - 1; i++) {
      let part = this.visit(ctx.getChild(i));
      result.push(part);
    }

    console.log("formula " + JSON.stringify(result));

    return result;
  }

  // Visit a parse tree produced by MyGrammarParser#expo.
  visitExpo(ctx) {
    let result = this.visitChildren(ctx);
    console.log("expo " + JSON.stringify(result));
    return result;
  }

  // Visit a parse tree produced by MyGrammarParser#unary.ex
  visitUnary(ctx) {
    let result = this.visitChildren(ctx);
    console.log("unary " + JSON.stringify(result));
    return result;
  }



  // Visit a parse tree produced by MyGrammarParser#mult.
  visitMult(ctx) {
    let result = this.visitChildren(ctx);
    console.log("mult " + JSON.stringify(result));
    return result;
  }

  // Visit a parse tree produced by MyGrammarParser#add.
  visitAdd(ctx) {
    let result = this.visitChildren(ctx);
    console.log("add " + JSON.stringify(result));
    return result;
  }

  visitTerm(ctx) {
    if (this.isTerminalNode(ctx)) {
        console.log("term " + JSON.stringify(ctx.getText()));
        return ctx.getText();
    }
    let result = this.visitChildren(ctx);
    console.log("term " + JSON.stringify(result));

    return result;
  }

  visitFunction(ctx) {
    let result = [ctx.FUNCTION_NAME().getText()];

    const expressionList = ctx.expression();

    for (let i = 0; i < expressionList.length; i++) {
      console.log("FUNC ARGS", expressionList[i].getText());
      let part = this.visit(expressionList[i]);
      result.push(part);
    }
    console.log("function " + JSON.stringify(result));
    return result;
  }

解析器.g4:

formula : ST expression EOF;

expo: unary (CARRET unary)*;

unary : (ADD | negation)* term;

negation : SUB;

mult : expo ((MUL | DIV) expo)*;

add : mult ((ADD | SUB) mult)*;

relation
    :   add (RELATION_SYMBOL add)*
    ;

expression
    :   relation ((AND | OR) relation)*
    ;

function
    :   FUNCTION_NAME OPEN_ROUND_BRACKET (expression (COMMA expression)*)? CLOSE_ROUND_BRACKET
    ;

term : function | INTEGER | DECIMAL | PERCENT | reference | OPEN_ROUND_BRACKET expression CLOSE_ROUND_BRACKET;

index.js:

const formula = "=IF($I$11=D169,1,0)";
var chars = new InputStream(formula, true);
var lexer = new MyGrammarLexer(chars);
var tokens = new CommonTokenStream(lexer);
var parser = new MyGrammarParser(tokens);


const tree = parser.formula();

const visitor = new GrammarParserVisitor(); // FormulaWalker 

let res = visitor.visitFormula(tree);
console.log(JSON.stringify(res));

我可以显示部分结果(当访问此输入的

=IF($I$11=D169,1,0)
第三个参数时),这是我从访问函数中的 console.log 获得的:

term "0"
unary ["0"]
expo [["0"]]
mult [[["0"]]]
add [[[["0"]]]]
relation [[[[["0"]]]]]

但我只需要返回字符串而不将其包装在数组中。为什么我会遇到这个问题以及如何解决它?

antlr antlr4
1个回答
0
投票

这是 JavaScript 访问者的起点。我稍微简化了你的语法(是的,知道这是不正确的,但这只是一个演示):

grammar MyGrammar;

formula    : ST expression EOF;
expo       : unary (CARRET unary)*;
unary      : (ADD | negation)* term;
negation   : SUB;
mult       : expo ((MUL | DIV) expo)*;
add        : mult ((ADD | SUB) mult)*;
relation   : add (rel_op add)*;
expression : relation ((AND | OR) relation)*;
function   : FUNCTION_NAME OPEN_ROUND_BRACKET (expression (COMMA expression)*)? CLOSE_ROUND_BRACKET;
term       : function | INTEGER | DECIMAL | PERCENT | reference | OPEN_ROUND_BRACKET expression CLOSE_ROUND_BRACKET;
reference  : CELL;
rel_op     : RELATION_SYMBOL | ST;

INTEGER             : [0-9]+;
DECIMAL             : INTEGER '.' INTEGER;
PERCENT             : '%';
CELL                : [A-Z] INTEGER;
ST                  : '=';
CARRET              : '^';
ADD                 : '+';
SUB                 : '-';
MUL                 : '*';
DIV                 : '/';
COMMA               : ',';
AND                 : 'AND';
OR                  : 'OR';
OPEN_ROUND_BRACKET  : '(';
CLOSE_ROUND_BRACKET : ')';
RELATION_SYMBOL     : '>=' | '>' | '<=' | '<';
FUNCTION_NAME       : [A-Z]+;

访客可能看起来像这样:

import MyGrammarVisitor from "./MyGrammarVisitor.js";

export default class DemoVisitor extends MyGrammarVisitor {

  // formula : ST expression EOF;
  visitFormula(ctx) {
    return this.visit(ctx.expression());
  }

  // expo: unary (CARRET unary)*;
  visitExpo(ctx) {
    // TODO account for multiple `unary`s
    return this.visit(ctx.unary(0));
  }

  // unary : (ADD | negation)* term;
  visitUnary(ctx) {
    return this.visit(ctx.term());
  }

  // negation : SUB;
  visitNegation(ctx) {
    throw new Error('TODO: negation')
  }

  // mult : expo ((MUL | DIV) expo)*;
  visitMult(ctx) {
    // TODO account for multiple `expo`s
    return this.visit(ctx.expo(0));
  }

  // add : mult ((ADD | SUB) mult)*;
  visitAdd(ctx) {
    // TODO account for multiple `mult`s
    return this.visit(ctx.mult(0));
  }

  // relation : add (rel_op add)*;
  visitRelation(ctx) {
    if (ctx.add().length > 1) {
      const v1 = this.visit(ctx.add(0));
      const v2 = this.visit(ctx.add(1));
      const operator = this.visit(ctx.rel_op());

      if (operator == '=') {
        return v1 == v2;
      }
      else {
        throw new Error('Operator not implemented: `' + operator + '`');
      }
    }

    return this.visit(ctx.add(0));
  }

  // expression : relation ((AND | OR) relation)*;
  visitExpression(ctx) {
    // TODO account for multiple `relation`s
    return this.visit(ctx.relation(0));
  }

  // function : FUNCTION_NAME OPEN_ROUND_BRACKET (expression (COMMA expression)*)? CLOSE_ROUND_BRACKET;
  visitFunction(ctx) {
    // Assume there is only 1 type of function: an IF with 3 expressions
    const e1 = this.visit(ctx.expression(0));
    const e2 = this.visit(ctx.expression(1));
    const e3 = this.visit(ctx.expression(2));
    console.log('e1:', e1, ', e2:', e2, ', e2:', e3);
    return e1 === true ? e2 : e3;
  }

  // term : function | INTEGER | DECIMAL | PERCENT | reference | OPEN_ROUND_BRACKET expression CLOSE_ROUND_BRACKET;
  visitTerm(ctx) {
    if (ctx.function_() != null) return this.visit(ctx.function_());
    if (ctx.INTEGER() != null) return parseInt(ctx.INTEGER().getText());
    if (ctx.reference() != null) return this.visit(ctx.reference());
    throw new Error('TODO: implement other terms');
  }

  // reference : CELL;
  visitReference(ctx) {
    return ctx.CELL().getText();
  }

  // rel_op : RELATION_SYMBOL | ST;
  visitRel_op(ctx) {
    return ctx.RELATION_SYMBOL() == null ? ctx.ST().getText() : ctx.RELATION_SYMBOL().getText();
  }
}

可以使用以下

main.js
进行测试:

import antlr4 from 'antlr4';
import MyGrammarLexer from './MyGrammarLexer.js';
import MyGrammarParser from './MyGrammarParser.js';
import DemoVisitor from './DemoVisitor.js';

const input = "=IF(D169=D169,100,4242)";
const lexer = new MyGrammarLexer(new antlr4.InputStream(input));
const parser = new MyGrammarParser(new antlr4.CommonTokenStream(lexer));
const visitor = new DemoVisitor();
const result = visitor.visit(parser.formula());

console.log('result:', result);

如果您现在使用

node ./main.js
运行此文件,您将在控制台上看到以下内容:

e1: true , e2: 100 , e2: 4242
result: 100
© www.soinside.com 2019 - 2024. All rights reserved.