我正在尝试实现解析查询的语法。单个查询包含items
,其中每个项目可以是name
或name-ref
。
name
是mystring
(仅限字母,无空格)或"my long string"
(字母和空格,始终引用)。 name-ref
与name
非常相似,唯一的区别是它应该以{{1}}(ref:
,ref:mystring
)开头。查询应包含至少1个项目(ref:"my long string"
或name
)。
这就是我所拥有的:
name-ref
这个语法演示了我基本上需要获得的内容,唯一的特性是它不支持长引号字符串(对于没有空格的名称,它可以正常工作)。
NAME: ('a'..'z')+;
REF_TAG: 'ref:';
SP: ' '+;
name: NAME;
name_ref: REF_TAG name;
item: name | name_ref;
query: item (SP item)*;
但这不起作用。有什么想法是什么问题?可能,这很重要:SHORT_NAME: ('a'..'z')+;
LONG_NAME: SHORT_NAME (SP SHORT_NAME)*;
REF_TAG: 'ref:';
SP: ' '+;
Q: '"';
short_name: SHORT_NAME;
long_name: LONG_NAME;
name_ref: REF_TAG (short_name | (Q long_name Q));
item: (short_name | (Q long_name Q)) | name_ref;
query: item (SP item)*;
应被视为3 my first query
s(3 item
s),name
为1 "my first query"
(1 item
)。
答案 0 :(得分:3)
ANTLR的词法分析器贪婪地匹配:这就是为什么像my first query
这样的输入被标记为LONG_NAME
而不是3 SHORT_NAME
s,其间有空格。
只需删除LONG_NAME
规则,然后在解析器规则long_name
中对其进行定义。
以下语法:
SHORT_NAME : ('a'..'z')+;
REF_TAG : 'ref:';
SP : ' '+;
Q : '"';
short_name : SHORT_NAME;
long_name : Q SHORT_NAME (SP SHORT_NAME)* Q;
name_ref : REF_TAG (short_name | (Q long_name Q));
item : short_name | long_name | name_ref;
query : item (SP item)*;
将解析输入:
my first query "my first query" ref:mystring
如下:
但是,你还可以对词法分析器中的引用名称进行标记,并使用一些自定义代码从中删除引号。从词法分析器中删除空格也是一种选择。像这样:
SHORT_NAME : ('a'..'z')+;
LONG_NAME : '"' ~'"'* '"' {setText(getText().substring(1, getText().length()-1));};
REF_TAG : 'ref:';
SP : ' '+ {skip();};
name_ref : REF_TAG (SHORT_NAME | LONG_NAME);
item : SHORT_NAME | LONG_NAME | name_ref;
query : item+ EOF;
将解析相同的输入,如下所示:
请注意,实际令牌LONG_NAME
将被删除其开始和结束引用。
答案 1 :(得分:1)
这是一个符合您要求的语法:
SP: ' '+;
SHORT_NAME: ('a'..'z')+;
LONG_NAME: '"' SHORT_NAME (SP SHORT_NAME)* '"';
REF: 'ref:' (SHORT_NAME | LONG_NAME);
item: SHORT_NAME | LONG_NAME | REF;
query: item (SP item)*;
如果你把它放在最上面:
grammar Query;
@members {
public static void main(String[] args) throws Exception {
QueryLexer lex = new QueryLexer(new ANTLRFileStream(args[0]));
CommonTokenStream tokens = new CommonTokenStream(lex);
QueryParser parser = new QueryParser(tokens);
try {
TokenSource ts = parser.getTokenStream().getTokenSource();
Token tok = ts.nextToken();
while (EOF != (tok.getType())) {
System.out.println("Got a token: " + tok);
tok = ts.nextToken();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
你应该看到词法分析器很好地区分了一切(我希望;-))
hi there "long name" ref:shortname ref:"long name"
应该给:
Got a token: [@-1,0:1='hi',<6>,1:0]
Got a token: [@-1,2:2=' ',<7>,1:2]
Got a token: [@-1,3:7='there',<6>,1:3]
Got a token: [@-1,8:8=' ',<7>,1:8]
Got a token: [@-1,9:19='"long name"',<4>,1:9]
Got a token: [@-1,20:20=' ',<7>,1:20]
Got a token: [@-1,21:33='ref:shortname',<5>,1:21]
Got a token: [@-1,34:34=' ',<7>,1:34]
Got a token: [@-1,35:49='ref:"long name"',<5>,1:35]
我不是100%确定你的语法有什么问题,但我怀疑这个问题与你没有引号的LONG_NAME
的定义有关。也许你可以看出它的区别是什么?