这让我感到震惊,我只是找不到解决方案。我有一个用于搜索查询的语法,并且希望匹配由可打印字母组成的查询中的任何搜索词,但特殊字符“(”,“)”除外)。用引号引起来的字符串将单独处理并起作用。

这是一个可行的语法:

    /* 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>

09-05 23:05
查看更多