ANTLR4: проблемы с соответствием [2 - 9], пока [2 - 9] работает нормально - PullRequest
2 голосов
/ 25 марта 2020

Извините за название, это было лучшее, что я мог придумать, пытаясь быть точным ... Я пытаюсь разобрать Lucene-like (Упрощенно с упорядочением) ...

Последовательности, которые я попытка сопоставления может выглядеть так (каждая строка представляет собой отдельную последовательность, переданную сама по себе):

age: [5 TO 9]        // fails: line 1:11 mismatched input '9' expecting WS
age: [5 TO *]        // fails: line 1:11 mismatched input '*' expecting WS
age: [* TO 9]        // success
age: [2345 TO 2110]  // fails: line 1:14 mismatched input '2110' expecting WS

height: [1.6 TO 2.0] // success
height: [* TO 2.0]   // success
height: [1.6 TO *]   // success

born: [2020-03-02 TO 2020-03-25]                                  // success
born: [2020-03-02T21:21:00 TO 2020-03-25T21:23:00]                // success
born: [2020-03-02T21:24:00+01:00 TO 2020-03-25T21:24:00+01:00]    // success
born: [* TO 2020-03-25T21:24:00+01:00]                            // success
born: [2020-03-02T21:24:00+01:00 TO *]                            // success

etc.

Если я заменю «TO» на «-», все те, которые потерпели неудачу, внезапно проходят.

age: [5 - 9]        // success
age: [5 - *]        // success
age: [* - 9]        // success
age: [2345 - 2110]  // success

Однако в данный момент нежелательно поддерживать этот синтаксис, я добавил его, чтобы посмотреть, оказал ли он влияние, и, к моему удивлению, это оказало ... Итак, в грамматике у меня есть WS ( TO | MINUS ) WS прямо сейчас, которое должно быть просто WS TO WS


Проблемная область грамматики:

rangeClause    :
    fieldName = name
    WS? COLON WS?
    start = ( LSBR | LCBR ) WS?
    from = simple_value
    WS ( TO | MINUS ) WS
    to = simple_value WS?
    end =( RSBR | RCBR );

Очевидно, похоже, что у него возникают проблемы, как только он сталкивается "Integer" в начале предложения Range, когда используется TO, но я пока не могу понять, почему.

Полная грамматика: -target = JavaScript (я не проверял других на данный момент)

grammar SimplifiedWithOrdering;

/* Inspired by: https://github.com/lrowe/lucenequery */

/*
 * Parser Rules
 */

query  : WS? clause = defaultClause  (WS order = orderingClause)? WS? EOF;

/*
 This implements all clauses grouped into batches of the same type.
 The order implements precedence (important).
*/

defaultClause : orClause (WS? orClause)*;
orClause      : andClause (orOperator andClause)*;
andClause     : notClause (andOperator notClause)*;
notClause     : basicClause (notOperator basicClause)*;
basicClause   :
  WS? LPA defaultClause WS? RPA
  | WS? atom
  ;

atom : value | field | rangeClause;

rangeClause    :
    fieldName = name
    WS? COLON WS?
    start = ( LSBR | LCBR ) WS?
    from = simple_value
    WS ( TO | MINUS ) WS
    to = simple_value WS?
    end =( RSBR | RCBR );

//Order
orderingClause    : WS? ORDER WS BY WS orderingField ( WS? COMMA WS? orderingField )* WS?;
orderingField     : WS? fieldName = name (WS direction = orderingDirection)?;
orderingDirection : (ASC | DESC);

field       : fieldName = name WS? fieldOperator = operator WS? fieldValue = value;
name        : TERM;

value       : TERM                                #VTerm
            | WILDCARD_TERM                       #VWildcard
            | NUMBER                              #VNumber
            | PHRASE                              #VPhrase
            | STAR                                #VMatchAll
            | DATE                                #VDate
            | DATE_TIME                           #VDateTime
            | DATE_OFFSET                         #VDateOffset
            ;

simple_value : TERM                                #STerm
             | STAR                                #SMatchAll
             | NUMBER                              #SNumber
             | DATE                                #SDate
             | DATE_TIME                           #SDateTime
             | DATE_OFFSET                         #SDateOffset
             ;

andOperator : WS? AND;
orOperator  : WS? OR;
notOperator : WS? (AND WS)? NOT;

operator : COLON  #Equals
         ;



/*
 * Lexer Rules
 */

LPA   : '(';
RPA   : ')';
LSBR  : '[';
RSBR  : ']';
LCBR  : '{';
RCBR  : '}';
STAR  : '*';
QMARK : '?';
COMMA : ',';
PLUS  : '+';
MINUS : '-';
DOT   : '.';
COLON : ':';

AND        : A N D      ;
OR         : O R        ;
NOT        : N O T      ;
ORDER      : O R D E R  ;
BY         : B Y        ;
ASC        : A S C      ;
DESC       : D E S C    ;
TO         : T O        ;

WS  : (' '|'\t'|'\r'|'\n'|'\u3000')+;

fragment INT        : [0-9];
fragment ESC        : '\\' .;

NUMBER  : MINUS? INT+ ('.' INT+)?;

// Special Date Handling:
//updated > 2018-03-04T14:41:23+00:00
fragment TIMEOFFSET  : ( MINUS | PLUS ) INT INT ( ':' INT INT );
TIME        : INT INT ':' INT INT ( ':' INT INT )? TIMEOFFSET?;
DATE        : INT INT INT INT MINUS INT INT MINUS INT INT;
DATE_TIME   : DATE 'T' TIME;

// Special Timespan Handling:
fragment TIME_IDEN_CHAR : [a-zA-Z];
fragment NOW         : N O W;
fragment TODAY       : T O D A Y;
fragment SIMPLE_TIMESPAN       : (INT+ '.')? INT INT ':' INT INT ( ':' INT INT ('.' INT INT))?;
fragment COMPLEX_TIMESPAN_PART : INT+ WS? TIME_IDEN_CHAR+;
fragment COMPLEX_TIMESPAN      : (COMPLEX_TIMESPAN_PART WS?)+;
fragment TIME_SPAN             : SIMPLE_TIMESPAN | COMPLEX_TIMESPAN;
DATE_OFFSET           : (NOW | TODAY)? WS? (PLUS|MINUS)? WS? TIME_SPAN;

fragment TERM_CHAR  : (~( ' ' | '\t' | '\n' | '\r' | '\u3000' | '\'' | '"'
                        | '(' | ')'  | '['  | ']'  | '{'      | '}'
                        | '!' | ':'  | '~'  | '>'  | '='      | '<'
                        | '?' | '*'
                        | '\\'| ',' )| ESC );

fragment WILDCARD_CHAR : (~( ' ' | '\t' | '\n' | '\r' | '\u3000' | '\'' | '"'
                           | '(' | ')'  | '['  | ']'  | '{'      | '}'
                           | '!' | ':'  | '~'  | '>'  | '='      | '<'
                           | '\\'| ',' )| ESC );

TERM   : TERM_CHAR+ ;
WILDCARD_TERM  : WILDCARD_CHAR+;

PHRASE : '"' ( ESC | ~('"'|'\\'))+ '"';

fragment A : [aA];
fragment B : [bB];
fragment C : [cC];
fragment D : [dD];
fragment E : [eE];
fragment F : [fF];
fragment G : [gG];
fragment H : [hH];
fragment I : [iI];
fragment J : [jJ];
fragment K : [kK];
fragment L : [lL];
fragment M : [mM];
fragment N : [nN];
fragment O : [oO];
fragment P : [pP];
fragment Q : [qQ];
fragment R : [rR];
fragment S : [sS];
fragment T : [tT];
fragment U : [uU];
fragment V : [vV];
fragment W : [wW];
fragment X : [xX];
fragment Y : [yY];
fragment Z : [zZ];

1 Ответ

3 голосов
/ 25 марта 2020

Вы должны понимать, что лексер создает токены независимо от парсера. Таким образом, если синтаксический анализатор «пытается» сопоставить определенный токен, лексер не «облегчает» анализатор в этом. Лексер просто создает токены с учетом следующих правил:

  1. попытаться сопоставить максимально возможное количество символов
  2. в случае, если два или более правил лексера соответствуют одинаковому количеству символов, пусть правило определен первый «выигрыш»

Из-за правила 1 вход 5 TO маркируется как токен DATE_OFFSET. Таким образом, если вы попытаетесь выполнить синтаксический анализ age: [5 TO 9], вашему синтаксическому анализатору придется работать со следующими токенами:

TERM             'age'
COLON            ':'
WS               ' '
LSBR             '['
DATE_OFFSET      '5 TO '
NUMBER           '9'
RSBR             ']'

, чего он не может (отсюда и сообщения об ошибках).

This объясняет, почему age: [* TO 9] анализируется нормально, потому что тогда для работы с парсером создаются следующие токены:

TERM             'age'
COLON            ':'
WS               ' '
LSBR             '['
STAR             '*'
WS               ' '
TO               'TO'
WS               ' '
NUMBER           '9'
RSBR             ']'

Возможным решением будет удалить DATE_OFFSET из лексера и попытаться создать правило парсера для него.

...