【问题标题】:ANTLR4 - parsing regex literals in JavaScript grammarANTLR4 - 在 JavaScript 语法中解析正则表达式文字
【发布时间】:2016-12-20 00:54:45
【问题描述】:

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

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

【问题讨论】:

  • 这显然是一个你无法通过词法单独解决的问题。 Lexing 只为您提供特定输入的标记值。它没有任何信息如何处理该 RE 输入。如果特定输入序列的含义根据某些上下文发生变化,那么您可以仅在解析器端或通过在词法分析后添加语义阶段来手动处理。
  • 虽然您的评论是正确的,但当提到词法分析的抽象任务时,在 Antlr3 中,您可以将少量逻辑附加到词法分析器语法中,就像解决我的问题所需要的那样。我在 v3 中不需要解析器。我现在在 v4 中需要它吗?
  • 您仍然可以在 ANTLR4 中使用谓词,但 syntax is different。此外,出于性能原因,将谓词放在规则的末尾(或者更好的是,就在第一个 / 分隔符字符之后)。

标签: parsing antlr4 lexer


【解决方案1】:

我在此发布最终解决方案,将现有解决方案改编为 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 上的原创作品。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-07-14
    • 1970-01-01
    • 2020-01-13
    • 2011-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多