如何解析具有相互重叠的分隔符的字符串?

How to parse strings with delimiters that overlap each other?

提问人:Tiiba 提问时间:6/7/2023 更新时间:6/9/2023 访问量:46

问:

我正在使用 ANTLR4 解析文档(wikitext)。在本文档中,用“”括起来的字符串是斜体的(如“斜体”)。被 ''' 括起来的字符串是粗体的(如 '''bold''')。但也有可能同时应用两种样式的字符串:''''''both'。

问题在于这些分隔符相互重叠。因此,当解析器遇到五个单引号时,它无法理解它是粗体后跟斜体还是斜体后跟粗体。我不知道该怎么说。

理想情况下,它会知道它应该选择导致成功解析的任何解释,但如果它能做到这一点,我不知道如何让它工作。

到目前为止,我的语法:

grammar WikiText;

NOWIKI_OPEN: '<nowiki>';
NOWIKI_CLOSE: '</nowiki>';
BOLD: '\'\'\'';
ITALICS: '\'\'';
CHAR: .;

nowiki: NOWIKI_OPEN CHAR* NOWIKI_CLOSE;
plainText: CHAR+;

boldText: BOLD wiki* BOLD;
italicText: ITALICS wiki* ITALICS;
nonPlainText: (boldText | italicText)+;

wiki: (nonPlainText | plainText)+;
document: (wiki | nowiki)*;
解析 ANTLR ANTLR4

评论

1赞 kaby76 6/7/2023
'''''hi'' there.'''.不能标记超过单个引号。解析后必须采用语义分析。然后,重写 and 运算符的树。'''''

答:

1赞 Mike Cargal 6/7/2023 #1

正如@kaby76所评论的那样,词法分析器没有足够的上下文来区分应该标记粗体和斜体的项目。这使它们成为语义问题(即解析器规则)

词法分析器只是逐个字符地尝试匹配词法分析器规则(越长越好,如果两个规则匹配相同长度的字符序列,则第一个规则获胜)。另一方面,解析器使用递归下降方法,该方法将仅在其他规则中遇到规则时才尝试匹配规则(从起始规则递归),因此它可以在上下文中获取标记。TICK

您还需要将备选方案组合到一个规则中以设置优先级。请务必在匹配项上使用非贪婪匹配。否则,ANTLR 将在评估规则时尝试匹配尽可能多的输入,并且您将获得包装在 或 中的大块。(您可以删除并查看生成的解析树以查看我所指的内容。*?wikiwiki*bolditalics?

grammar WikiText
    ;

NOWIKI_OPEN:  '<nowiki>';
NOWIKI_CLOSE: '</nowiki>';
TICK:         '\'';
CHAR:         .;

nowiki: NOWIKI_OPEN CHAR* NOWIKI_CLOSE;

bold:   TICK TICK TICK;
italic: TICK TICK;

wiki
    : CHAR+                # plainText
    | italic wiki*? italic # italicText
    | bold wiki*? bold     # boldText
    ;

wikiText: wiki+;
document: (wiki | nowiki)*;
echo "''italic'' '''bold''' '''''bold italic'''''" |  grun WikiText document -gui 

enter image description here


回复:您的后续评论:

我用了:

grammar WikiText
    ;

NOWIKI_OPEN:  '<nowiki>';
NOWIKI_CLOSE: '</nowiki>';
TICK:         '\'';
CHAR:         .;

nowiki: NOWIKI_OPEN CHAR* NOWIKI_CLOSE;

wiki
    : CHAR+                                # plainText
    | TICK TICK TICK wiki*? TICK TICK TICK # boldText
    | TICK TICK wiki*? TICK TICK           # italicText
    ;

wikiText: wiki+;
document: (wiki | nowiki)*;

并得到这个解析树:enter image description here

(我使用了在图中显示上下文类型的 IntelliJ ANTLR 插件)

它似乎没有混淆。

恕我直言,第一个更容易处理,但无论如何,在使用解析树时,您在很大程度上会忽略标记,因此几乎没有区别。TICK

评论

0赞 Tiiba 6/8/2023
老实说,我没有掌握原理。似乎如果分隔符被命名为单独的解析器规则(粗体:TICK TICK TICK;),一切都很好。但是,如果我尝试使用原始刻度(TICK TICK TICK wiki*?嘀嗒嘀嗒嘀嗒),又回到了困惑中。我不明白为什么。
0赞 Mike Cargal 6/9/2023
修改后的答复,因为答复在评论中的格式不正确。