ANTLR 语法来匹配以 # 开头的注释行
ANTLR grammar to match the comment lines started with #
我正在尝试将以下文本与 ANTLR 语法匹配:
ANTLR 语法是:
grammar header;
start : commentBlock
EOF;
commentBlock : CommentLine+;
CommentLine : '#' AsciiChars+;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
我得到的错误是:
line 1:0 token recognition error at: '# '
line 2:0 token recognition error at: '# '
line 3:0 token recognition error at: '# '
[@0,2:2='a',<AsciiChars>,1:2]
[@1,7:7='b',<AsciiChars>,2:2]
[@2,12:12='c',<AsciiChars>,3:2]
[@3,15:14='<EOF>',<EOF>,4:0]
line 1:2 mismatched input 'a' expecting CommentLine
我猜语法是合理的,但为什么会出现错误?
加 1
奇怪,在我将 lexer 规则 CommentLine
更改为 parser 规则 commentLine
后,它起作用了:
grammar header;
start : commentBlock
EOF;
commentBlock : commentLine+;
commentLine : '#' AsciiChars+; // <=== here CommentLine -> commentLine
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
但实际上我想丢弃所有注释行。如果它必须是解析器规则,我不能使用 ->skip
来丢弃它。
添加 2
我想我现在可以解释了。
要记住的重要事项是:
- 词法分析器阶段发生在解析器阶段之前。
- 如果 skipped 标记 T1 被另一个词法分析器规则引用,比如标记 T2,标记 T1 部分 within 标记 T2 将 不被跳过。
让我用一个简洁的例子来解释一下:
要匹配的文档:
# abc
语法 1:
grammar test;
t : T2;
p : t
EOF;
Char : [a-z];
T2 : '#' T1+ Char+; // <<<< Here T2 reference the so-skipped T1.
fragment Tab : '\t';
fragment Space : ' ';
T1 : (Tab|Space)+ ->skip; //<<<<< T1 is to be skipped.
在语法1中,跳过了T1,但是没有跳过T2中的T1部分。 T2 将匹配词法分析器阶段的输入文本。 (即使我们将 T2 放在 T1 之后,T2 仍然会匹配。我认为 ANTLR 做了一些贪婪匹配来匹配最长的令牌。)
语法 2:
跳过的 T1 未被另一个标记规则引用,而是直接在解析器规则中引用。
grammar test;
t : '#' T1+ Char+; // <<<<<<<<<<<< HERE
p : t
EOF;
Char : [a-z];
fragment Tab : '\t';
fragment Space : ' ';
T1 : (Tab|Space)+ ->skip; //<<<<< T1 is to be skipped.
这一次,没有 T2 规则来帮助空格在词法分析器阶段存活下来,输入文件中的所有 T1 都将被跳过。所以之后在解析器阶段,匹配将失败并出现此错误:
[@0,0:0='#',<'#'>,1:0]
[@1,4:4='a',<Char>,1:4]
[@2,5:5='b',<Char>,1:5]
[@3,6:6='c',<Char>,1:6]
[@4,7:6='<EOF>',<EOF>,1:7]
line 1:4 mismatched input 'a' expecting T1
因为所有 T1 都已经 在词法分析器阶段被丢弃 。
加 3
回到我原来的问题,我犯的一个微妙的错误是,我认为跳过TS
之后,剩下的字符可以重新分组到没有空格的新标记 CommentLine
。这对于 ANTLR 来说是完全错误的。
因为lexer phase all happens before parser phase,CommentLine
是token规则,里面没有空格,所以不会匹配输入内容中的任何内容。
正如@macmoonshine 所说,我必须将 TS
添加到 CommentLine
令牌中。
您的语法在注释中不包含空格,但您的注释包含空格。
编辑:您是否尝试过 commentLine : '#' TS AsciiChars;
作为评论规则?
也许您正在寻找:
grammar Header;
start : CommentLine+ EOF;
CommentLine : '#' ' ' AsciiChars+;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
现在这个只使用词法分析器规则。
忽略评论
grammar Header;
start : CommentLine+ EOF;
CommentLine : '#' ' ' AsciiChars+ -> skip;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
这将完全忽略注释,实际上会给出一个错误,因为规则 'start
需要一个 CommentLine
现在被丢弃了。所以如果你想 忽略和丢弃 评论,使用像第二个这样的东西并且不要在你的解析器规则中提及 CommentLine
,让词法分析器去除它们。或者如果你想保留评论,你可以使用以前的。
重新安排评论
最后一个想法是将评论重新路由到另一个频道:
grammar Header;
start : other EOF;
other: AsciiChars;
CommentLine : '#' ' ' AsciiChars+ -> channel(2);
AsciiChars : [a-zA-Z]+;
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
在这个语法中,评论仍然被词法化,但被路由到另一个通道以进行可能的处理。我在中添加了另一条规则
start
只是为了有一些东西可以匹配:
# a
# b
something
# c
[@0,0:2='# a',<CommentLine>,channel=2,1:0]
[@1,5:7='# b',<CommentLine>,channel=2,2:0]
[@2,10:18='something',<AsciiChars>,3:0]
[@3,21:23='# c',<CommentLine>,channel=2,4:0]
[@4,26:25='<EOF>',<EOF>,5:0]
这些选项中的一个肯定能为您做到;)
试试这个:看起来您的评论与普通的单行评论相同,只是 '#'
换成了 '//'
。如果在散列后需要 space,请使用:'# '
。如果您要求散列位于第 1 列,请使用:[\n\r] '# ' ~[\n\r]
。从查看示例来看,这应该涵盖所有可能的选项。
COMMENT_LINE
: '#' ~[\n\r]* ->( skip )
;
我正在尝试将以下文本与 ANTLR 语法匹配:
ANTLR 语法是:
grammar header;
start : commentBlock
EOF;
commentBlock : CommentLine+;
CommentLine : '#' AsciiChars+;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
我得到的错误是:
line 1:0 token recognition error at: '# '
line 2:0 token recognition error at: '# '
line 3:0 token recognition error at: '# '
[@0,2:2='a',<AsciiChars>,1:2]
[@1,7:7='b',<AsciiChars>,2:2]
[@2,12:12='c',<AsciiChars>,3:2]
[@3,15:14='<EOF>',<EOF>,4:0]
line 1:2 mismatched input 'a' expecting CommentLine
我猜语法是合理的,但为什么会出现错误?
加 1
奇怪,在我将 lexer 规则 CommentLine
更改为 parser 规则 commentLine
后,它起作用了:
grammar header;
start : commentBlock
EOF;
commentBlock : commentLine+;
commentLine : '#' AsciiChars+; // <=== here CommentLine -> commentLine
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
但实际上我想丢弃所有注释行。如果它必须是解析器规则,我不能使用 ->skip
来丢弃它。
添加 2
我想我现在可以解释了。
要记住的重要事项是:
- 词法分析器阶段发生在解析器阶段之前。
- 如果 skipped 标记 T1 被另一个词法分析器规则引用,比如标记 T2,标记 T1 部分 within 标记 T2 将 不被跳过。
让我用一个简洁的例子来解释一下:
要匹配的文档:
# abc
语法 1:
grammar test;
t : T2;
p : t
EOF;
Char : [a-z];
T2 : '#' T1+ Char+; // <<<< Here T2 reference the so-skipped T1.
fragment Tab : '\t';
fragment Space : ' ';
T1 : (Tab|Space)+ ->skip; //<<<<< T1 is to be skipped.
在语法1中,跳过了T1,但是没有跳过T2中的T1部分。 T2 将匹配词法分析器阶段的输入文本。 (即使我们将 T2 放在 T1 之后,T2 仍然会匹配。我认为 ANTLR 做了一些贪婪匹配来匹配最长的令牌。)
语法 2:
跳过的 T1 未被另一个标记规则引用,而是直接在解析器规则中引用。
grammar test;
t : '#' T1+ Char+; // <<<<<<<<<<<< HERE
p : t
EOF;
Char : [a-z];
fragment Tab : '\t';
fragment Space : ' ';
T1 : (Tab|Space)+ ->skip; //<<<<< T1 is to be skipped.
这一次,没有 T2 规则来帮助空格在词法分析器阶段存活下来,输入文件中的所有 T1 都将被跳过。所以之后在解析器阶段,匹配将失败并出现此错误:
[@0,0:0='#',<'#'>,1:0]
[@1,4:4='a',<Char>,1:4]
[@2,5:5='b',<Char>,1:5]
[@3,6:6='c',<Char>,1:6]
[@4,7:6='<EOF>',<EOF>,1:7]
line 1:4 mismatched input 'a' expecting T1
因为所有 T1 都已经 在词法分析器阶段被丢弃 。
加 3
回到我原来的问题,我犯的一个微妙的错误是,我认为跳过TS
之后,剩下的字符可以重新分组到没有空格的新标记 CommentLine
。这对于 ANTLR 来说是完全错误的。
因为lexer phase all happens before parser phase,CommentLine
是token规则,里面没有空格,所以不会匹配输入内容中的任何内容。
正如@macmoonshine 所说,我必须将 TS
添加到 CommentLine
令牌中。
您的语法在注释中不包含空格,但您的注释包含空格。
编辑:您是否尝试过 commentLine : '#' TS AsciiChars;
作为评论规则?
也许您正在寻找:
grammar Header;
start : CommentLine+ EOF;
CommentLine : '#' ' ' AsciiChars+;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
现在这个只使用词法分析器规则。
忽略评论
grammar Header;
start : CommentLine+ EOF;
CommentLine : '#' ' ' AsciiChars+ -> skip;
AsciiChars : [a-zA-Z];
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
这将完全忽略注释,实际上会给出一个错误,因为规则 'start
需要一个 CommentLine
现在被丢弃了。所以如果你想 忽略和丢弃 评论,使用像第二个这样的东西并且不要在你的解析器规则中提及 CommentLine
,让词法分析器去除它们。或者如果你想保留评论,你可以使用以前的。
重新安排评论
最后一个想法是将评论重新路由到另一个频道:
grammar Header;
start : other EOF;
other: AsciiChars;
CommentLine : '#' ' ' AsciiChars+ -> channel(2);
AsciiChars : [a-zA-Z]+;
fragment CR : '\r';
fragment LF : '\n';
EOL : CR?LF ->skip;
fragment Tab : '\t';
fragment Space : ' ';
TS : (Tab|Space)+ ->skip;
在这个语法中,评论仍然被词法化,但被路由到另一个通道以进行可能的处理。我在中添加了另一条规则
start
只是为了有一些东西可以匹配:
# a
# b
something
# c
[@0,0:2='# a',<CommentLine>,channel=2,1:0]
[@1,5:7='# b',<CommentLine>,channel=2,2:0]
[@2,10:18='something',<AsciiChars>,3:0]
[@3,21:23='# c',<CommentLine>,channel=2,4:0]
[@4,26:25='<EOF>',<EOF>,5:0]
这些选项中的一个肯定能为您做到;)
试试这个:看起来您的评论与普通的单行评论相同,只是 '#'
换成了 '//'
。如果在散列后需要 space,请使用:'# '
。如果您要求散列位于第 1 列,请使用:[\n\r] '# ' ~[\n\r]
。从查看示例来看,这应该涵盖所有可能的选项。
COMMENT_LINE
: '#' ~[\n\r]* ->( skip )
;