ANTLR Lexer, соответствующий неправильному правилу - PullRequest
2 голосов
/ 12 февраля 2020

Я работаю над лексером и парсером для старой объектно-ориентированной системы чата (MOO на случай, если кто-нибудь из читателей знаком с ее языком). На этом языке любой из приведенных ниже примеров является допустимым числом с плавающей запятой:

2.3

3.

.2

3e + 5

Язык также реализует синтаксический индекс для извлечения одного или нескольких символов из строки или списка (который представляет собой набор разделенных запятыми выражений, заключенных в фигурные скобки). Проблема возникает из-за того, что язык поддерживает оператор диапазона внутри скобок индекса. Например: a = foo [1..3];

Я понимаю, что ANTLR хочет сначала найти самое длинное из возможных совпадений. К сожалению, это приводит к тому, что лексер видит «1..3» как два числа с плавающей точкой (1. и .3), а не как два целых числа с оператором диапазона («..») между ними. Есть ли способ решить эту проблему с использованием режимов лексера? Учитывая, что значения внутри выражения индексации могут быть любым допустимым выражением, мне пришлось бы дублировать множество правил токенов (по сути, все, кроме чисел с плавающей запятой, насколько я понимаю). Теперь, если я новичок в ANTLR, я уверен, что что-то упустил, и любая помощь очень ценится. Я предоставлю свою грамматику лексера ниже:

lexer grammar MooLexer;

channels { COMMENTS_CHANNEL }

SINGLE_LINE_COMMENT
    : '//' INPUT_CHARACTER* -> channel(COMMENTS_CHANNEL);

DELIMITED_COMMENT
    : '/*' .*? '*/' -> channel(COMMENTS_CHANNEL);

WS
    :   [ \t\r\n] -> channel(HIDDEN)
    ;

IF
    : I F
    ;

ELSE
    : E L S E
    ;

ELSEIF
    : E L S E I F
    ;

ENDIF
    : E N D I F
    ;

FOR
    : F O R;

ENDFOR
    : E N D F O R;

WHILE
    : W H I L E
    ;

ENDWHILE
    : E N D W H I L E
    ;

FORK
    : F O R K
    ;

ENDFORK
    : E N D F O R K
    ;

RETURN
    : R E T U R N
    ;

BREAK
    : B R E A K
    ;

CONTINUE
    : C O N T I N U E
    ;

TRY
    : T R Y
    ;

EXCEPT
    : E X C E P T
    ;

ENDTRY
    : E N D T R Y
    ;

IN
    : I N
    ;

SPLICER
    : '@';

UNDERSCORE
    : '_';

DOLLAR
    : '$';

SEMI
    : ';';

COLON
    : ':';

DOT
    : '.';

COMMA
    : ',';

BANG
    : '!';

OPEN_QUOTE
    : '`';

SINGLE_QUOTE
    : '\'';

LEFT_BRACKET
    : '[';

RIGHT_BRACKET
    : ']';

LEFT_CURLY_BRACE
    : '{';

RIGHT_CURLY_BRACE
    : '}';

LEFT_PARENTHESIS
    : '(';

RIGHT_PARENTHESIS
    : ')';

PLUS
    : '+';

MINUS
    : '-';

STAR
    : '*';

DIV
    : '/';

PERCENT
    : '%';

PIPE
    : '|';

CARET
    : '^';

ASSIGNMENT
    : '=';

QMARK
    : '?';

OP_AND
    : '&&';

OP_OR
    : '||';

OP_EQUALS
    : '==';

OP_NOT_EQUAL
    : '!=';

OP_LESS_THAN
    : '<';

OP_GREATER_THAN
    : '>';

OP_LESS_THAN_OR_EQUAL_TO
    : '<=';

OP_GREATER_THAN_OR_EQUAL_TO
    : '>=';

RANGE
    : '..';

ERROR
    : 'E_NONE'
    | 'E_TYPE'
    | 'E_DIV'
    | 'E_PERM'
    | 'E_PROPNF'
    | 'E_VERBNF'
    | 'E_VARNF'
    | 'E_INVIND'
    | 'E_RECMOVE'
    | 'E_MAXREC'
    | 'E_RANGE'
    | 'E_ARGS'
    | 'E_NACC'
    | 'E_INVARG'
    | 'E_QUOTA'
    | 'E_FLOAT'
    ;

OBJECT
    : '#' DIGIT+
    | '#-' DIGIT+
    ;

STRING 
    : '"' ( ESC | [ !] | [#-[] | [\]-~] | [\t] )* '"';

INTEGER
    : DIGIT+;

FLOAT
    : DIGIT+ [.] (DIGIT*)? (EXPONENTNOTATION EXPONENTSIGN DIGIT+)? 
    | [.] DIGIT+ (EXPONENTNOTATION EXPONENTSIGN DIGIT+)? 
    | DIGIT+ EXPONENTNOTATION EXPONENTSIGN DIGIT+
    ;

IDENTIFIER
    : (LETTER | DIGIT | UNDERSCORE)+
    ;

LETTER
    : LOWERCASE 
    | UPPERCASE
    ;

/* 
 * fragments 
 */

fragment LOWERCASE  
    : [a-z] ;

fragment UPPERCASE  
    : [A-Z] ;

fragment EXPONENTNOTATION
    : ('E' | 'e');

fragment EXPONENTSIGN
    : ('-' | '+');

fragment DIGIT 
    : [0-9] ;

fragment ESC 
    : '\\"' | '\\\\' ;

fragment INPUT_CHARACTER
    : ~[\r\n\u0085\u2028\u2029];

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 голосов
/ 12 февраля 2020

Нет, AFAIK, нет способа решить эту проблему с помощью режимов лексера. Вам понадобится предикат с небольшим количеством целевого значения c code . Если ваша цель - Java, это может выглядеть следующим образом:

lexer grammar RangeTestLexer;

FLOAT
 : [0-9]+ '.' [0-9]+
 | [0-9]+ '.' {_input.LA(1) != '.'}?
 | '.' [0-9]+
 ;

INTEGER
 : [0-9]+
 ;

RANGE
 : '..'
 ;

SPACES
 : [ \t\r\n] -> skip
 ;

Если вы запустите следующий код Java:

Lexer lexer = new RangeTestLexer(CharStreams.fromString("1 .2 3. 4.5 6..7 8 .. 9"));
CommonTokenStream tokens = new CommonTokenStream(lexer);
tokens.fill();

for (Token t : tokens.getTokens()) {
  System.out.printf("%-20s `%s`\n", RangeTestLexer.VOCABULARY.getSymbolicName(t.getType()), t.getText());
}

, вы получите следующий вывод:

INTEGER              `1`
FLOAT                `.2`
FLOAT                `3.`
FLOAT                `4.5`
INTEGER              `6`
RANGE                `..`
INTEGER              `7`
INTEGER              `8`
RANGE                `..`
INTEGER              `9`
EOF                  `<EOF>`

{ ... }? - это предикат, и встроенный код должен вычисляться как логическое значение. В моем примере код Java _input.LA(1) != '.' возвращает true, если поток символов на 1 шаг впереди текущей позиции не равен '.' char.

...