Parsing Antlr4:如何在语法中隐藏和使用标记
我正在解析一种脚本语言,它定义了两种类型的语句;控制语句和非控制语句。非控制语句始终以Parsing Antlr4:如何在语法中隐藏和使用标记,parsing,whitespace,antlr4,Parsing,Whitespace,Antlr4,我正在解析一种脚本语言,它定义了两种类型的语句;控制语句和非控制语句。非控制语句始终以“;”结尾,而控制语句可能以“”;”结尾或EOL('\n')。语法的一部分如下所示: script : statement* EOF ; statement : control_statement | no_control_statement ; control_statement : if_then_control_statement
“;”结尾代码>,而控制语句可能以“”;”结尾代码>或EOL
('\n')。语法的一部分如下所示:
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]+
;
在脚本语言的所有其他方面,我从不关心EOL
,所以我使用普通的lexer规则来隐藏空白
这在所有情况下都可以正常工作,但在我需要使用EOL
来查找控制语句的终止的情况下除外,但是使用上面的语法,所有EOL
都是隐藏的,不在控制语句规则中使用
有没有一种方法可以改变我的语法,这样我就可以跳过所有的EOL
,但可以跳过终止部分控制语句所需的语法?找到了一种方法来处理这个问题
这个想法是把EOL转移到一个隐藏的频道,而其他我不想在另一个隐藏频道看到的东西(比如空格和评论)。然后,当一个EOL应该出现时,我使用一些代码来回溯令牌,并检查以前的令牌通道(因为它们已经被使用)。如果在我遇到普通频道的内容之前,我在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]+
;
更改了lexer规则:
@lexer::members {
public static int EOL_CHANNEL = 1;
public static int OTHER_CHANNEL = 2;
}
...
EOL
: '\r'? '\n' -> channel(EOL_CHANNEL)
;
LINE_SPACE
: [ \t]+ -> channel(OTHER_CHANNEL)
;
我还将所有其他隐藏频道(评论)转移到其他频道
。
然后我更改了规则end\u control\u station
:
end_control_statment
: END_STATEMENT
| { isEOLPrevious() }?
;
并补充说
@parser::members {
public static int EOL_CHANNEL = 1;
public static int OTHER_CHANNEL = 2;
boolean isEOLPrevious()
{
int idx = getCurrentToken().getTokenIndex();
int ch;
do
{
ch = getTokenStream().get(--idx).getChannel();
}
while (ch == OTHER_CHANNEL);
// Channel 1 is only carrying EOL, no need to check token itself
return (ch == EOL_CHANNEL);
}
}
你可以坚持使用普通的隐藏通道,但是在回溯时需要同时跟踪通道和令牌,所以这可能会更容易一些
希望这能帮助其他人处理这类问题