如何构建 JavaScript ANTLR 访问者
How to build a JavaScript ANTLR visitor
我实际上在 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);
}
}
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;
此时我不知道如何继续为访问者实现这些方法。
在Java脚本中visitChildren
returns 包含访问children 的结果的数组。因此,如果一个节点有两个 children,那么 visitChildren(node)
将 return [visit(node.child1), visit(node.child2)]
(而在 Java 中你只会得到 visit(node.child2)
和 visit(node.child1)
的结果将被丢弃)。如果它只有一个 child,你会得到一个只包含一个元素的数组(而在 Java 中,你只会得到没有数组的元素)。
在您的代码中,您到处都在调用 visitChildren
,所以这就是您最终得到嵌套数组的原因。如果你不想要数组,你要么不调用 visitChildren
要么调用它,解压数组然后 return 其他东西。例如,这里有两种方法可以实现 visitPlus
到 return 加法的结果(如果这是目标):
// 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
};
当然,要实现这一点,所有其他访问者方法也需要更改为 return 数字。如果您希望访问者执行除计算表达式以外的其他操作,则需要相应地调整代码。
PS:您可以简单地通过使用 ANTLR4 的优先级处理规则来简化您的语法,它允许您按优先级降序列出中缀运算符,运算符为 left-associative,除非您指定<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
;
我实际上在 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);
}
}
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;
此时我不知道如何继续为访问者实现这些方法。
在Java脚本中visitChildren
returns 包含访问children 的结果的数组。因此,如果一个节点有两个 children,那么 visitChildren(node)
将 return [visit(node.child1), visit(node.child2)]
(而在 Java 中你只会得到 visit(node.child2)
和 visit(node.child1)
的结果将被丢弃)。如果它只有一个 child,你会得到一个只包含一个元素的数组(而在 Java 中,你只会得到没有数组的元素)。
在您的代码中,您到处都在调用 visitChildren
,所以这就是您最终得到嵌套数组的原因。如果你不想要数组,你要么不调用 visitChildren
要么调用它,解压数组然后 return 其他东西。例如,这里有两种方法可以实现 visitPlus
到 return 加法的结果(如果这是目标):
// 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
};
当然,要实现这一点,所有其他访问者方法也需要更改为 return 数字。如果您希望访问者执行除计算表达式以外的其他操作,则需要相应地调整代码。
PS:您可以简单地通过使用 ANTLR4 的优先级处理规则来简化您的语法,它允许您按优先级降序列出中缀运算符,运算符为 left-associative,除非您指定<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
;