【问题标题】:ANTLR4 match any not-matched sections into one single STRING tokenANTLR4 将任何不匹配的部分匹配到一个单独的 STRING 令牌中
【发布时间】:2020-02-02 23:15:08
【问题描述】:

我正在尝试使用 ANTLR 创建一个 Lexer/Parser,它可以解析带有散布在其间的“标签”的纯文本。 这些标签由左括号 ({) 和右括号 (}) 表示,它们表示可以评估为字符串的 Java 对象,然后在原始输入中替换该字符串以创建各种动态模板。

这是一个例子: {player:name} 打招呼! {player:name} 应替换为玩家的名称并导致输出,即 Mark says hi!对于名为 Mark 的玩家。

现在我可以很好地识别和解析标签,我遇到的问题是后面的文本。 这是我使用的语法:

    grammar : content+

    content : tag 
            | literal
            ;

    tag : player_tag
        | <...>
        | <other kinds of tags, not important for this example>
        | <...>
        ;

    player_tag : BRACKET_OPEN player_identifier SEMICOLON player_string_parameter BRACKET_CLOSE ;
    player_string_parameter : NAME
                            | <...>
                            ;
    player_identifier : PLAYER ;

    literal : NUMBER
            | STRING
            ;

    BRACKET_OPEN : '{';
    BRACKET_CLOSE : '}';

    PLAYER : 'player'
    NAME : 'name'

    NUMBER : <...>
    STRING : (.+)? /* <- THIS IS THE PROBLEMATIC PART !*/

现在这个 STRING Lexer 定义应该匹配任何非空字符串,但问题是它过于贪婪,然后还消耗了标记规则所需的 { } 括号标记。 我尝试将其设置为 ~[{}]+ ,它应该与不包含 { } 括号的任何内容匹配,但与我也不理解的标签解析有关。 我可以将其设置为 [ a-zA-Z0-9!"§$%&/()= etc...]+ 但我真的不想将其限制为仅解析英式键盘上可用的字符(德语变音符号或法语口音以及其他语言必须使用的所有其他特殊字符!) 尽管我真的不喜欢它,但唯一有效的方法是强制字符串具有前缀和后缀,如下所示:

  STRING : '\'' ~[}{]+ '\'' ;

这迫使我改变“{player:name} say hi!”的形式。到“{player:name}' say hi!'”,我真的非常想避免这样的限制,因为我必须考虑字符串本身中的文字 ' 字符,而且使用起来很难看。

我想到的两个解决方案如下: - 有什么方法可以匹配任意数量的未被词法分析器匹配的字符作为字符串令牌并将其传递给解析器?这样我就可以匹配所有的标签,然后说输入的其余部分只是纯文本,把它作为一个 STRING 令牌或其他东西还给我...... - ANTLR 是否支持前瞻和后瞻正则表达式,我可以匹配第一个 '{' 之前、最后一个 '}' 之后以及 '}' 和 '{' 之间的任何字符的任意数量的字符? 我试过了

  STRING : (?<=})(.+)?(?={) ;

但我似乎无法获得正确的语法,因为它根本无法编译,这让我相信 ANTLR 不支持前瞻和后瞻语法,但我在互联网上找不到明确的答案这个问题。

有什么建议吗?

【问题讨论】:

    标签: parsing antlr antlr4 regex-lookarounds


    【解决方案1】:

    Antlr 不支持前瞻或后瞻。它确实支持非贪婪通配符匹配,但仅当.* 非贪婪通配符后在规则中 带有终止序列(正如你所说,它也包含在匹配中,尽管您可以将其推回输入流中)。

    所以~[{}]* 是正确的。但是有一个小问题:词法分析器规则(通常)始终处于活动状态。因此,词法分析器规则也将在大括号内 处于活动状态,这意味着它将吞下大括号之间的全部内容(除非有嵌套的大括号或大括号内的引号或类似的东西,那就更糟了)。

    所以你需要定义不同的词法内容,在 Antlr 中称为“词法模式”。 Antlr Definitive Reference 中有一个publically viewable example,它显示了一个非常相似的问题的解决方案:解析 HTML。

    【讨论】:

    • 对于这个简单的例子,词法模式起作用。但是,一旦我引入嵌套标签,这种方法也会失效。您提到的权威 ANTLR4 参考显示了解析 xml 时模式使用的示例,但是与 xml 的一个关键区别是标签内的标签的可能性。
    • 我相信,这是我的解析器语法的问题,而不是词法分析器的问题。词法分析器标记现在似乎已正确识别,但解析器看到类似 ``` {{}} ``` 并将内部标记解析为 {}} 留下外部没有右括号的标签。有什么建议可以在这里做什么?
    • @neph:你是怎么得到嵌套标签的?内部标签是字符串文字还是类似的?或者您是在谈论 Java 代码中的普通大括号,它们根本不是标签。可能您需要对 Java 进行正确的 lex,这需要识别 Java 字符串文字(和 cmets)。您应该能够在不做太多工作的情况下找到适用于 Java 的词法语法,否则编写起来也不会太难。
    • 我解决了我的问题,并认为我会分享它以防其他人遇到它。 @nici 词汇模式确实有效。我使用了“pushMode(...)”和“popMode”,这就像一个魅力。一开始并没有,因为我也忘记了从标签模式中推送模式,所以标签模式只被推送了一次,并且没有嵌套标签是可能的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    • 2022-06-20
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 2016-01-08
    • 1970-01-01
    相关资源
    最近更新 更多