我正在尝试解析C ++ / Java样式的源文件,并希望将注释,字符串文字和空格隔离为标记。
对于空格和注释,通常建议的解决方案是(使用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对/ ** /和“”将相互干扰,因为每个都可以包含另一对的开头作为其有效内容。那么我们应该如何定义词法分析器语法来处理这两种情况?
答案 0 :(得分:4)
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 */<
答案 1 :(得分:-3)
基本上你的问题是:在字符串文字中,必须忽略注释(/ *和//),反之亦然。 IMO只能通过顺序阅读来解决。在逐个字符的基础上遍历源文件时,您可以将其作为具有状态Text,BlockComment,LineComment,StringLiteral的状态机来处理。
尝试使用正则表达式甚至是语法来解决这个问题。
请注意,任何C / C ++ / C#/ Java词法分析器也需要处理这个完全相同的问题。我很确定它采用类似状态机的解决方案。所以我建议的是,如果可以的话,以这种方式定制你的词法分析器。