Antlr4:如何在语法中隐藏和使用令牌

pas*_*seg 5 whitespace parsing antlr4

我正在解析一种定义两种类型的语句的脚本语言;控制语句和非控制语句。非控制语句始终以 结尾,而控制语句可能以or ('\n')';'结尾。语法的一部分如下所示:';'EOL

script
    :   statement* EOF
    ;

statement
    :   control_statement
    |   no_control_statement
    ;

control_statement
    :   if_then_control_statement
    ;

if_then_control_statement
    :   IF expression THEN end_control_statment
        ( statement ) *
        ( ELSEIF expression THEN end_control_statment ( statement )* )*
        ( ELSE end_control_statment ( statement )* )?
        END IF end_control_statment
    ;

no_control_statement
    :   sleep_statement
    ;

sleep_statement
    :   SLEEP expression END_STATEMENT
    ;

end_control_statment
    :   END_STATEMENT
    |   EOL
    ;

END_STATEMENT
    :   ';'
    ;

ANY_SPACE
    :   ( LINE_SPACE | EOL )    ->  channel(HIDDEN)
    ;

EOL
    :   [\n\r]+
    ;

LINE_SPACE
    :   [ \t]+
    ;
Run Code Online (Sandbox Code Playgroud)

在脚本语言的所有其他方面,我从不关心,EOL因此我使用普通的词法分析器规则来隐藏空格。

这在所有情况下都工作得很好,但在我需要使用 aEOL来查找控制语句的终止的情况下,但使用上面的语法,所有内容都EOL被隐藏并且不在控制语句规则中使用。

有没有办法改变我的语法,以便我可以跳过除EOL终止部分控制语句所需的语法之外的所有语法?

pas*_*seg 6

找到了一种方法来处理这个问题。

\n\n

这个想法是将 EOL 转移到一个隐藏通道中,并将我不想在另一个隐藏通道中看到的其他内容(例如空格和注释)。然后,当 EOL 应该出现时,我使用一些代码来回溯令牌并检查以前的令牌通道(因为它们已经被消耗)。如果我在遇到普通渠道的某些内容之前在 EOL 渠道上找到了某些内容,那么就可以了。

\n\n

它看起来像这样:

\n\n

更改了词法分析器规则:

\n\n
@lexer::members {\n    public static int EOL_CHANNEL = 1;\n    public static int OTHER_CHANNEL = 2;\n}\n\n...\n\nEOL\n  : '\\r'? '\\n'  ->  channel(EOL_CHANNEL)\n  ;\n\nLINE_SPACE\n  : [ \\t]+  ->  channel(OTHER_CHANNEL)\n  ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

我还将所有其他隐藏频道(评论)转移到OTHER_CHANNEL。\n然后我更改了规则end_control_statment

\n\n
end_control_statment\n  : END_STATEMENT\n  | { isEOLPrevious() }?\n  ;\n
Run Code Online (Sandbox Code Playgroud)\n\n

并添加了

\n\n
@parser::members {\n  public static int EOL_CHANNEL = 1;\n  public static int OTHER_CHANNEL = 2;\n\n  boolean isEOLPrevious()\n  {\n        int idx = getCurrentToken().getTokenIndex();\n        int ch;\n\n        do\n        {\n            ch = getTokenStream().get(--idx).getChannel();\n        }\n        while (ch == OTHER_CHANNEL);\n\n        // Channel 1 is only carrying EOL, no need to check token itself\n        return (ch == EOL_CHANNEL);\n     }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

人们可以坚持使用普通的隐藏通道,但是在回溯时需要同时跟踪通道和令牌,因此这可能会更容易一些......

\n\n

希望这可以帮助其他人处理此类问题......

\n