【问题标题】:ANTLR grammar to match the comment lines started with #ANTLR 语法以匹配以 # 开头的注释行
【发布时间】:2018-01-05 23:45:16
【问题描述】:

我正在尝试将以下文本与 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; 

但实际上我想丢弃所有的注释行。如果它必须是解析器规则,我不能使用-&gt;skip 来丢弃它。

添加 2

我想我现在可以解释了。

要记住的关键事项是:

  • 词法分析器阶段发生在解析器阶段之前。
  • 如果 跳过 标记 T1 被另一个词法分析器规则引用,例如标记 T2,则标记 T1 部分 标记 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 phaseCommentLine是一个token规则,里面没有空格,所以不会匹配输入内容中的任何内容。

正如@macmoonshine 所说,我必须将TS 添加到CommentLine 令牌中。

【问题讨论】:

  • 请务必查看我在下面建议的许多选项。
  • 那里肯定有一个“接受”——我还能提供什么帮助来获得接受;)

标签: antlr antlr4


【解决方案1】:

试试这个:看来您的注释与普通的单行注释相同,只是将'#' 换成了'//'。如果您在哈希后需要一个空格,请使用:'# '。如果您要求哈希值位于第 1 列,请使用:[\n\r] '# ' ~[\n\r]。通过查看示例,这应该涵盖所有可能的选项。

COMMENT_LINE
    : '#'  ~[\n\r]* ->( skip )
    ;

【讨论】:

    【解决方案2】:

    也许你正在寻找:

    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 现在被丢弃。因此,如果您想忽略并丢弃 cmets,请使用类似第二个的内容,并且不要在解析器规则中提及CommentLine,只需让词法分析器剥离它们即可。或者如果你想保留cmets,你可以使用上一个。

    重新发送评论

    最后一个想法是将 cmets 重新路由到另一个频道:

    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;
    

    在这个语法中,cmets 仍然是 lexed,但被路由到另一个通道以进行可能的处理。我添加了另一个规则 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]
    

    这些选项中的一个肯定会为您做到这一点;)

    【讨论】:

    • 我认为这应该可行。但我只是不知道为什么将词法分析器规则CommentLine 更改为解析器规则commentLine 可以使其工作?其实我想放弃所有的评论。如果它必须是解析器规则,我不能使用-&gt;skip
    • 您不必使用匹配的解析器规则。您可以在 Listener 或 Visitor 中简单地忽略它。不过还有其他方法可以做到这一点,请稍等。
    【解决方案3】:

    您的语法在 cmets 中不包含空格,但您的 cmets 包含。

    编辑:您是否尝试过 commentLine : '#' TS AsciiChars; 作为评论规则?

    【讨论】:

    • 谢谢,但我确实将所有空格与TS 匹配,并用-&gt;skip 转储它们。这发生在解析器阶段之前的词汇阶段。所以我认为我不需要在解析器规则中指定空格。
    • 您的评论规则是 # 后跟 ASCII 字符,不包括 TS 规则。 ANTLR 将 '# '# 和一个空格)声明为错误。
    • 我试过了。错误更改为missing TS。我想这是因为我在词汇阶段抛弃了它们。所以解析器阶段不会看到它。
    • TS规则放在评论规则前面。
    • 您还可以在注释规则commentLine : '#' [ A-Za-z]*; 中包含空格,这也应该允许在 cmets 中使用空格。
    猜你喜欢
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 2016-05-02
    • 1970-01-01
    • 2014-11-30
    • 1970-01-01
    • 2013-05-23
    相关资源
    最近更新 更多