ANTLR4 - 用JavaScript语法解析正则表达式文字

时间:2016-08-12 23:14:34

标签: parsing antlr4 lexer

我正在使用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章 - 上下文敏感的词汇问题),但我不想使用解析器。我只想提取所有的标记,除了字符串文字之外,保持一切都不受影响,并保持解析不受影响。

任何建议都会非常感谢,谢谢!

1 个答案:

答案 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的原创工作。