我正在使用ANTLR4为某些JavaScript预处理器生成一个Lexer(基本上它标记了一个javascript文件并提取每个字符串文字)。
我使用了最初为Antlr3制作的语法,并为v4导入了相关部分(仅限词法规则)。
我只剩下一个问题:我不知道如何处理RegEx文字的角落案例,如下所示:
log(Math.round(v * 100) / 100 + ' msec/sample');
/ 100 + ' msec/
被解释为RegEx文字,因为词法分析器规则始终处于活动状态。
我想要的是合并这个逻辑(C#代码。我需要JavaScript,但我根本不知道如何调整它):
/// <summary>
/// Indicates whether regular expression (yields true) or division expression recognition (false) in the lexer is enabled.
/// These are mutual exclusive and the decision which is active in the lexer is based on the previous on channel token.
/// When the previous token can be identified as a possible left operand for a division this results in false, otherwise true.
/// </summary>
private bool AreRegularExpressionsEnabled
{
get
{
if (Last == null)
{
return true;
}
switch (Last.Type)
{
// identifier
case Identifier:
// literals
case NULL:
case TRUE:
case FALSE:
case THIS:
case OctalIntegerLiteral:
case DecimalLiteral:
case HexIntegerLiteral:
case StringLiteral:
// member access ending
case RBRACK:
// function call or nested expression ending
case RPAREN:
return false;
// otherwise OK
default:
return true;
}
}
}
此规则在旧语法中作为内联谓词出现,如下所示:
RegularExpressionLiteral
: { AreRegularExpressionsEnabled }?=> DIV RegularExpressionFirstChar RegularExpressionChar* DIV IdentifierPart*
;
但我不知道如何在ANTLR4中使用这种技术。
在ANTLR4的书中,有一些关于在解析器级别解决这类问题的建议(第12.2章 - 上下文敏感的词汇问题),但我不想使用解析器。我只想提取所有的标记,除了字符串文字之外,保持一切都不受影响,并保持解析不受影响。
任何建议都会非常感谢,谢谢!
答案 0 :(得分:0)
我在这里发布了最终解决方案,开发了适应现有的ANTLR4新语法,并解决了JavaScript语法的差异。
我只发布相关部分,为其他人提供有关工作策略的线索。
该规则编辑如下:
RegularExpressionLiteral
: DIV {this.isRegExEnabled()}? RegularExpressionFirstChar RegularExpressionChar* DIV IdentifierPart*
;
函数isRegExEnabled在词法分析器语法之上的@members
部分中定义,如下所示:
@members {
EcmaScriptLexer.prototype.nextToken = function() {
var result = antlr4.Lexer.prototype.nextToken.call(this, arguments);
if (result.channel !== antlr4.Lexer.HIDDEN) {
this._Last = result;
}
return result;
}
EcmaScriptLexer.prototype.isRegExEnabled = function() {
var la = this._Last ? this._Last.type : null;
return la !== EcmaScriptLexer.Identifier &&
la !== EcmaScriptLexer.NULL &&
la !== EcmaScriptLexer.TRUE &&
la !== EcmaScriptLexer.FALSE &&
la !== EcmaScriptLexer.THIS &&
la !== EcmaScriptLexer.OctalIntegerLiteral &&
la !== EcmaScriptLexer.DecimalLiteral &&
la !== EcmaScriptLexer.HexIntegerLiteral &&
la !== EcmaScriptLexer.StringLiteral &&
la !== EcmaScriptLexer.RBRACK &&
la !== EcmaScriptLexer.RPAREN;
}}
如您所见,定义了两个函数,一个是lexer的nextToken
方法的覆盖,它包装现有的nextToken并保存最后一个非注释或空白标记以供参考。然后,语义谓词调用isRegExEnabled,检查最后一个有意义的标记是否与RegEx文字的存在兼容。如果不是,则返回false。
感谢Lucas Trzesniewski的评论:它指出了我正确的方向,以及Patrick Hulsmeijer对v3的原创工作。