ANTLR4 - 如何在两个字符匹配之前匹配某些内容?

ANTLR4 - How to match something until two characters match?

颤振:

Framework • revision 18116933e7 (vor 8 Wochen) • 2021-10-15 10:46:35 -0700
Engine • revision d3ea636dc5
Tools • Dart 2.14.4

Antrl4:
antlr4: ^4.9.3

我想实现一个简单的工具来格式化文本,如下面的定义:https://www.motoslave.net/sugarcube/2/docs/#markup-style

所以基本上每个 __ 都是带下划线文本的开始,下一个 __ 是结尾。

我在输入以下内容时遇到了一些问题:

^^subscript=^^

Shell: line 1:13 token recognition error at '^'
Shell: line 1:14 extraneous input '' expecting {'==', '//', '''', '__', '~~', '^^', TEXT}

MyLexer.g4:


STRIKETHROUGH : '==';
EMPHASIS : '//';
STRONG : '\'\'';
UNDERLINE : '__';
SUPERSCRIPT : '~~';
SUBSCRIPT : '^^';

TEXT
 : ( ~[<[$=/'_^~] | '<' ~'<' | '=' ~'=' | '/' ~'/' | '\'' ~'\'' | '_' ~'_' | '~' ~'~' | '^' ~'^' )+
;

MyParser.g4:


options {
    tokenVocab=SugarCubeLexer;
    //language=Dart;
}

parse
 : block EOF
 ;

block
 : statement*
 ;

statement
 : strikethroughStyle
 | emphasisStyle
 | strongStyle
 | underlineStyle
 | superscriptStyle
 | subscriptStyle
 | unstyledStatement
 ;

unstyledStatement
 : plaintext
 ;

strikethroughStyle
 : STRIKETHROUGH (emphasisStyle | strongStyle | underlineStyle | superscriptStyle | subscriptStyle | unstyledStatement)* STRIKETHROUGH
 ;

emphasisStyle
 : EMPHASIS (strikethroughStyle | strongStyle | underlineStyle | superscriptStyle | subscriptStyle | unstyledStatement)* EMPHASIS
 ;

strongStyle
 : STRONG (strikethroughStyle | emphasisStyle | underlineStyle | superscriptStyle | subscriptStyle | unstyledStatement)* STRONG
 ;

underlineStyle
 : UNDERLINE (strikethroughStyle | emphasisStyle | strongStyle | superscriptStyle | subscriptStyle | unstyledStatement)* UNDERLINE
 ;

superscriptStyle
 : SUPERSCRIPT (strikethroughStyle | emphasisStyle | strongStyle | underlineStyle | subscriptStyle | unstyledStatement)* SUPERSCRIPT
 ;

subscriptStyle
 : SUBSCRIPT (strikethroughStyle | emphasisStyle | strongStyle | underlineStyle | superscriptStyle | unstyledStatement)* SUBSCRIPT
 ;

plaintext
 : TEXT
 ;

如果有任何帮助,我将非常高兴。谢谢

是你 TEXT 规则:

TEXT
    : (
        ~[<[$=/'_^~]
        | '<' ~'<'
        | '=' ~'='
        | '/' ~'/'
        | '\'' ~'\''
        | '_' ~'_'
        | '~' ~'~'
        | '^' ~'^'
    )+
    ;

您不能像您尝试的那样在 ANTLR 中编写词法分析器规则(即一个“^”,除非它后面跟着另一个“^”)。 ~'^' 表示“任何不是 ^”的字符)

如果您 运行 使用 -tokens 选项通过 grun 输入,您会看到 TEXT 令牌通过 EOL 提取所有内容

[@0,0:1='^^',<'^^'>,1:0]
[@1,2:14='subscript=^^\n',<TEXT>,1:2]
[@2,15:14='<EOF>',<EOF>,2:0]

尝试这样的事情:

grammar MyParser
    ;

parse: block EOF;

block: statement*;

statement
    : STRIKETHROUGH statement STRIKETHROUGH # Strikethrough
    | EMPHASIS statement EMPHASIS           # Emphasis
    | STRONG statement STRONG               # Strong
    | UNDERLINE statement UNDERLINE         # Underline
    | SUPERSCRIPT statement SUPERSCRIPT     # SuperScript
    | SUBSCRIPT statement SUBSCRIPT         # Subscript
    | plaintext                             # unstyledStatement
    ;

plaintext: TEXT+;

STRIKETHROUGH: '==';
EMPHASIS:      '//';
STRONG:        '\'\'';
UNDERLINE:     '__';
SUPERSCRIPT:   '~~';
SUBSCRIPT:     '^^';

TEXT: .;

此语法正确地解析了您的输入,但代价是将特殊字符以外的所有内容都转换为单个字符标记。

再多考虑一下,我们可以将其最小化:

grammar MyParser
    ;

parse: block EOF;

block: statement*;

statement
    : STRIKETHROUGH statement STRIKETHROUGH # Strikethrough
    | EMPHASIS statement EMPHASIS           # Emphasis
    | STRONG statement STRONG               # Strong
    | UNDERLINE statement UNDERLINE         # Underline
    | SUPERSCRIPT statement SUPERSCRIPT     # SuperScript
    | SUBSCRIPT statement SUBSCRIPT         # Subscript
    | (U_TEXT | TEXT)+                      # unstyledStatement
    ;

STRIKETHROUGH: '==';
EMPHASIS:      '//';
STRONG:        '\'\'';
UNDERLINE:     '__';
SUPERSCRIPT:   '~~';
SUBSCRIPT:     '^^';

U_TEXT: ~[=/'_~^]+;
TEXT:   .;

这添加了 U_TEXT 词法分析器规则。此规则会将所有明确的字符集中到一个标记中。这显着减少了产生的代币数量。 (以及诊断警告的数量)。它的性能应该比第一个好得多(我没有 tried/timed 它在足够大的输入上看到差异,但生成的解析树要好得多。

阐述:

ANTLR 词法分析器规则评估通过检查您的输入来工作。当多个规则可以匹配输入的下一个 n 个字符时,它将继续查看输入字符,直到一个字符无法匹配任何“活动”词法分析器规则。这建立了可以匹配词法分析器规则的最长 运行 个字符。如果这是一个单一的规则,它获胜(凭借匹配最长的输入字符序列)。如果有多个规则匹配相同的 运行 输入字符,那么词法分析器会匹配这些规则中的第一个出现在您的语法中。 (从技术上讲,这些情况是“歧义”,因为从整个语法来看,ANTLR 可以通过多种方式 对其进行标记化。但是,由于 ANTLR 具有解决这些歧义的确定性规则,它们并不是真正的问题。)

Lexer 规则,除了否定一组字符(出现在 [] 之间)之外,不能使用否定。这意味着我们不能编写规则来匹配“< 后面没有另一个 <”。我们可以将“<<”匹配为比“<”更长的标记。为此,我们必须确保可以开始您的两个字符序列之一的所有标记都匹配单个标记规则。但是,我们希望避免让所有其他字符都成为单字符规则,因此我们可以引入一个“除了我们的特殊字符之外的一切”的规则。这将贪婪地消耗一切不可能“有问题”的东西。仅保留语法末尾的单个字符 `'.'`` 规则捕获的特殊字符。