鉴于以下ANTLR语法:
tokens
{
SET;
UNKNOWN;
LIST;
}
statement : SET_KEYWORD list = value_list -> ^(SET $list)
| UNKNOWN_KEYWORD -> ^(UNKNOWN);
value_list : element += value (COMMA_KEYWORD element += value)* -> ^(LIST $element+);
value : STRING_KEYWORD;
SET_KEYWORD : 'SET';
UNKNOWN_KEYWORD : .;
fragment STRING_KEYWORD : '"' ('a'..'z' | 'A'..'Z')* '"';
当解析以下文本时(即不关闭双引号):
SET "example
ANTLR会出现以下错误:
TEST(1) : lexer error 1 :
Unexpected character at offset 12, (end of input).
This indicates a poorly specified lexer RULE
or unterminated input element such as: "STRING["]
The lexer was matching from line 1, offset 3, which
looks like this:
"example
有人会认为UNKNOWN_KEYWORD会赶上这类问题。如何修复语法,以便不再显示上述错误消息?
答案 0 :(得分:0)
你的语法的主要错误是将UNKNOWN_KEYWORD
和STRING_KEYWORD
描述为词法规则。您的解析器与SET "example
输入不匹配,因为词法分析器具有following:
Lexer寻找第一个最长的匹配规则。换句话说,如果多个规则与输入匹配,则选择最长的规则。
当词法分析器符合可以匹配为"
且UNKNOWN_KEYWORD
开头的STRING_KEYWORD
时,它会决定将输入与STRING_KEYWORD
匹配,因为此规则比UNKNOWN_KEYWORD
。
因此,将这些规则重写为解析器规则会更好。让词法分析器匹配更简单的标记。
除此之外,你的语法中还有其他错误:
options
块以描述在规则中使用AST运算符的输出类型。+
规则UNKNOWN_KEYWORD
之后遗失statement
。如果没有+
,则说该语句是SET_KEYWORD value_list
或任何单个字符。 EOF
规则末尾缺少statement
。像SET "abc"bla-bla-bla
这样的输入将被解析为SET_KEYWORD value_list
,其中(SET (LIST "abc"))
即bla-bla-bla
,EOF
将被忽略{。}}。COMMA_KEYWORD
规则的说明。STRING_KEYWORD
描述为片段规则。如果规则是片段,则解析器永远不会获得此规则描述的令牌。如果您将UNKNOWN_KEYWORD
和STRING_KEYWORD
重写为解析器规则并尝试使用SET "example"
输入的解析器,则会获得以下AST:
(SET (LIST " e x a m p l e "))
即。 "example"
不是AST的整个节点。每个字符都有单独的节点,因为AST中的一个节点在一般情况下构造一个令牌。但是可以通过调用CommonTree
类的构造函数创建虚构标记并创建节点来解决它(参见ANTLR 3 Runtime 3.5.2 API):
value : st = string_keyword -> {new CommonTree(new CommonToken(KEYWORD, $st.text))};
所以,你的语法可能如下所示:
options {
output=AST;
ASTLabelType=CommonTree;
}
tokens {
SET;
UNKNOWN;
LIST;
KEYWORD;
}
statement : ((SET_KEYWORD value_list)=> SET_KEYWORD list= value_list NEWLINE -> ^(SET $list)
| unknown_keyword+ NEWLINE -> ^(UNKNOWN)
)
EOF;
value_list : element += value (COMMA_KEYWORD element += value)* -> ^(LIST $element+);
value : st= string_keyword -> {new CommonTree(new CommonToken(KEYWORD,$st.text))};
NEWLINE:'\r'? '\n' ;
WS: (' '|'\t'|'\n'|'\r')+ {skip();};
COMMA_KEYWORD: ',';
SET_KEYWORD : 'SET';
QUOTES:'"';
LETTER:('a'..'z' | 'A'..'Z');
ANY:.;
string_keyword : QUOTES LETTER* QUOTES ;
unknown_keyword : SET_KEYWORD|QUOTES|LETTER|COMMA_KEYWORD|ANY;