【问题标题】:Grammar To Parse Multiline Comments and String Literals Simultaneously同时解析多行注释和字符串文字的语法
【发布时间】:2011-09-23 09:05:24
【问题描述】:

我正在尝试解析 C++/Java 样式的源文件,并希望将 cmets、字符串文字和空格隔离为标记。

对于空格和 cmets,通常建议的解决方案是(使用 ANTLR 语法):

// WS comments*****************************
WS: (' '|'\n'| '\r'|'\t'|'\f' )+ {$channel=HIDDEN;};
ML_COMMENT: '/*' (options {greedy=false;}: .)* '*/' {$channel=HIDDEN;};
SL_COMMENT: '//' (options {greedy=false;}: .)* '\r'? '\n' {$channel=HIDDEN;};

但是,问题是我的源文件也包含字符串文字,例如

printf("   /* something looks like comment and whitespace \n");
printf("    something looks like comment and whitespace */ \n");

"" 中的整个内容应该被视为单个标记,但我的 ANTLR 词法分析器规则显然会将它们视为 ML_COMMENT 标记:

    /* something looks like comment and whitespace \n");
printf("    something looks like comment and whitespace */

但是我不能创建另一个词法分析器规则来将标记定义为一对 " 中的东西(假设 \" 转义序列被正确处理),因为这将被错误地视为字符串标记:

/*  comment...."comment that looks */   /*like a string literal"...more comment */

简而言之,2 对 /**/ 和 "" 会相互干扰,因为每一对都可以包含另一对的开头作为其有效内容。那么我们应该如何定义一个词法分析器来处理这两种情况呢?

【问题讨论】:

    标签: parsing antlr grammar lexer


    【解决方案1】:

    JavaMan 写道:

    我正在尝试解析 C++/Java 样式的源文件,并希望将注释、字符串文字和空格隔离为标记。

    你不应该也匹配 char 文字吗?考虑:

    char c = '"';
    

    不应将双引号视为字符串文字的开头!

    JavaMan 写道:

    简而言之,2对/**/和“”会互相干扰。

    呃,不。如果首先“看到”/*,它将一直消耗到第一个 */。对于像这样的输入:

    /*  comment...."comment that looks like a string literal"...more comment */
    

    这意味着双引号也被消耗了。字符串文字也是如此:当首先看到双引号时,/* 和/或 */ 将被使用,直到遇到下一个(未转义的)"

    还是我误会了?

    请注意,您可以在 .*.+ 之前从语法中删除 options {greedy=false;}:,它们默认是不贪婪的。

    这是一种方法:

    grammar T;
    
    parse
      :  (t=. 
           {
             if($t.type != OTHER) {
               System.out.printf("\%-10s >\%s<\n", tokenNames[$t.type], $t.text);
             }
           }
         )+
         EOF
      ;
    
    ML_COMMENT
      :  '/*' .* '*/'
      ;
    
    SL_COMMENT
      :  '//' ~('\r' | '\n')*
      ;
    
    STRING
      :  '"' (STR_ESC | ~('\\' | '"' | '\r' | '\n'))* '"'
      ;
    
    CHAR
      :  '\'' (CH_ESC | ~('\\' | '\'' | '\r' | '\n')) '\''
      ;
    
    SPACE
      :  (' ' | '\t' | '\r' | '\n')+
      ;
    
    OTHER
      :  . // fall-through rule: matches any char if none of the above matched
      ;
    
    fragment STR_ESC
      :  '\\' ('\\' | '"' | 't' | 'n' | 'r') // add more:  Unicode esapes, ...
      ;
    
    fragment CH_ESC
      :  '\\' ('\\' | '\'' | 't' | 'n' | 'r') // add more: Unicode esapes, Octal, ...
      ;
    

    可以通过以下方式进行测试:

    import org.antlr.runtime.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        String source = 
            "String s = \" foo \\t /* bar */ baz\";\n" +
            "char c = '\"'; // comment /* here\n" +
            "/* multi \"no string\"\n" +
            "   line */";
        System.out.println(source + "\n-------------------------");
        TLexer lexer = new TLexer(new ANTLRStringStream(source));
        TParser parser = new TParser(new CommonTokenStream(lexer));
        parser.parse();
      }
    }
    

    如果你运行上面的类,控制台会打印以下内容:

    String s = " foo \t /* bar */ baz";
    char c = '"'; // comment /* here
    /* multi "no string"
       line */
    -------------------------
    
    SPACE      > <
    SPACE      > <
    SPACE      > <
    STRING     >" foo \t /* bar */ baz"<
    SPACE      >
    <
    SPACE      > <
    SPACE      > <
    SPACE      > <
    CHAR       >'"'<
    SPACE      > <
    SL_COMMENT >// comment /* here<
    SPACE      >
    <
    ML_COMMENT >/* multi "no string"
       line */<
    

    【讨论】:

    • 谢谢,你说得对。我对词法分析器规则工作方式的误解。顺便说一句,SL_COMMENT 中的 ~('\r' | '\n')* 是否等同于 (~('\r' | '\n'))* ?
    • @JavaMan,是的,~('\r' | '\n')*(~('\r' | '\n'))* 完全一样
    • OTHER lexer 规则是必须的吗?如果一个字符无法与任何词法分析器规则匹配会发生什么?
    • @JavaMan,词法分析器会警告“看到”任何词法分析器规则都不匹配的字符。所以是的,OTHER 规则必须在那里。
    【解决方案2】:

    基本上,您的问题是:在字符串文字中,必须忽略 cmets(/* 和 //),反之亦然。 IMO 这只能通过顺序读取来解决。在逐个字符地浏览源文件时,您可能会将其视为具有 Text、BlockComment、LineComment、StringLiteral 状态的状态机。

    这是一个很难用正则表达式甚至语法来解决的问题。

    请注意,任何 C/C++/C#/Java 词法分析器也需要处理完全相同的问题。我很确定它采用了类似状态机的解决方案。所以我的建议是,如果可以的话,以这种方式自定义你的词法分析器。

    【讨论】:

    • “这是一个很难用正则表达式甚至语法来解决的问题。”,不,不是。它可以很容易地翻译成几个正则表达式:看我的回答。
    • 您的答案的高度复杂性导致我提供此答案作为替代方案。在这种特殊情况下,您的解决方案可能是一种解决方案,但不可羡慕。
    • 我不同意我的答案很复杂的事实:只有 ANTLR 基本知识的人可以理解它。此外,您所说的不正确:OP问题的解决方案是几个正则表达式(语法是正则的!)。
    • 这是正确的,即使在这种情况下。但是您的解决方案可以立即生效。不过,我觉得它被解释了。同意不同意。
    猜你喜欢
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    • 1970-01-01
    • 2017-01-23
    • 2013-06-09
    • 1970-01-01
    相关资源
    最近更新 更多