我正在使用目标语言 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"]]]]]
但我只需要返回字符串而不将其包装在数组中。为什么我会遇到这个问题以及如何解决它?
这是 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