这让我感到震惊,我只是找不到解决方案。我有一个用于搜索查询的语法,并且希望匹配由可打印字母组成的查询中的任何搜索词,但特殊字符“(”,“)”除外)。用引号引起来的字符串将单独处理并起作用。
这是一个可行的语法:
/* ANTLR Grammar for Minidb Query Language */
grammar Mdb;
start
: searchclause EOF
;
searchclause
: table expr
;
expr
: fieldsearch
| searchop fieldsearch
| unop expr
| expr relop expr
| lparen expr relop expr rparen
;
lparen
: '('
;
rparen
: ')'
;
unop
: NOT
;
relop
: AND
| OR
;
searchop
: NO
| EVERY
;
fieldsearch
: field EQ searchterm
;
field
: ID
;
table
: ID
;
searchterm
:
| STRING
| ID+
| DIGIT+
| DIGIT+ ID+
;
STRING
: '"' ~('\n'|'"')* ('"' )
;
AND
: 'and'
;
OR
: 'or'
;
NOT
: 'not'
;
NO
: 'no'
;
EVERY
: 'every'
;
EQ
: '='
;
fragment VALID_ID_START
: ('a' .. 'z') | ('A' .. 'Z') | '_'
;
fragment VALID_ID_CHAR
: VALID_ID_START | ('0' .. '9')
;
ID
: VALID_ID_START VALID_ID_CHAR*
;
DIGIT
: ('0' .. '9')
;
/*
NOT_SPECIAL
: ~(' ' | '\t' | '\n' | '\r' | '\'' | '"' | ';' | '.' | '=' | '(' | ')' )
; */
WS
: [ \r\n\t] + -> skip
;
问题在于搜索字词过于受限。它应与注释掉的NOT_SPECIAL中的任何字符匹配,即有效查询应为:
Person Name=%
Person Address=^%Street%%%$^&*@^
但是,每当我尝试以任何方式将NOT_SPECIAL放入searchterm的定义中时,它都不会起作用。我也尝试将它按字面意义放入规则中(注释NOT_SPECIAL)和许多其他事情,但这只是行不通。在我的大多数尝试中,语法只是提示“=”之后的多余输入,并表示期望EOF。但是我也不能将EOF放到NOT_SPECIAL中。
我有什么办法可以简单地解析规则fieldsearch中“=”之后的每个文本,直到出现空格或“)”,“(”?
N.B. STRING规则可以正常工作,但是不必要求用户每次都使用引号,因为这是命令行工具,因此需要转义。
目标语言是Go。
最佳答案
您可以通过引入lexical mode来解决该问题,只要您与EQ
token 匹配就可以输入。一旦进入该词法模式,您就可以匹配(
,)
或空格(在这种情况下,您会跳出词法模式),或者保持匹配NOT_SPECIAL
字符。
通过使用词法模式,必须在自己的文件中定义词法分析器和解析器规则。确保使用lexer grammar ...
和parser grammar ...
代替在组合的grammar ...
文件中使用的.g4
。
快速演示:
lexer grammar MdbLexer;
STRING
: '"' ~[\r\n"]* '"'
;
OPAR
: '('
;
CPAR
: ')'
;
AND
: 'and'
;
OR
: 'or'
;
NOT
: 'not'
;
NO
: 'no'
;
EVERY
: 'every'
;
EQ
: '=' -> pushMode(NOT_SPECIAL_MODE)
;
ID
: VALID_ID_START VALID_ID_CHAR*
;
DIGIT
: [0-9]
;
WS
: [ \r\n\t]+ -> skip
;
fragment VALID_ID_START
: [a-zA-Z_]
;
fragment VALID_ID_CHAR
: [a-zA-Z_0-9]
;
mode NOT_SPECIAL_MODE;
OPAR2
: '(' -> type(OPAR), popMode
;
CPAR2
: ')' -> type(CPAR), popMode
;
WS2
: [ \t\r\n] -> skip, popMode
;
NOT_SPECIAL
: ~[ \t\r\n()]+
;
您的解析器语法将像这样开始:
parser grammar MdbParser;
options {
tokenVocab=MdbLexer;
}
start
: searchclause EOF
;
// your other parser rules
My Go有点生锈,但是是一个小的Java测试:
String source = "Person Address=^%Street%%%$^&*@^()";
MdbLexer lexer = new MdbLexer(CharStreams.fromString(source));
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();
for (Token t : tokens.getTokens()) {
System.out.printf("%-15s %s\n", MdbLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
}
打印以下内容:
ID Person
ID Address
EQ =
NOT_SPECIAL ^%Street%%%$^&*@^
OPAR (
CPAR )
EOF <EOF>