Различные имена токенов для непараметрических и параметризованных операторов ИЛИ как перейти к предыдущему токену с помощью RuleLexer - PullRequest
1 голос
/ 18 июня 2020

Как добиться разных имен токенов для следующих примеров:

#someNameAttribute //where #someNameAttribute should be assigned to IDENTIFIER lexer rule
#someNameAttribute("2a3a796e-9870-4b88-9f2d-383eb9566613", 10) // where #someNameAttribute should be assigned to PARAMETERIZED_IDENTIFIER since we faced with parenthesis 

Грамматика, которая у меня есть прямо сейчас (но она всегда присваивается IDENTIFIER):

grammar Rule;

ruleExpression
    : identifierExpression EOF | parameterizedIdentifierExpression EOF
    ;

identifierExpression
    : IDENTIFIER
    ;

parameterizedIdentifierExpression
    : PIDENTIFIER LPAREN UUID DELIMETER NUMERIC RPAREN
    ;

DELIMETER           : ',';
LPAREN              : '(';
RPAREN              : ')';
UUID                : '"'[0-9a-fA-F]+'-'[0-9a-fA-F]+'-'[1-5][0-9a-fA-F]+'-'[89abAB][0-9a-fA-F]+'-'[0-9a-fA-F]+'"';
NUMERIC             : [0-9]+ ( '.' [0-9]+ )? ;
IDENTIFIER          : '#' [a-zA-Z$_] [a-zA-Z$_0-9]*;
// PARAMETERIZED_IDENTIFIER         : { behind(LPAREN) }? IDENTIFIER;  // Tried to use semantic predicate but no luck. Might be used it wrong way
WS                  : [ \r\t\u000C\n]+ -> skip;

Или, если это возможно как-нибудь проверить следующий токен в скобках после #someNameAttribute из кода Java - буду рад услышать, как это сделать. Я тоже пробовал этот способ, однако RuleLexer.nextToken () позволяет мне проверять следующий токен, но я не могу снова перейти к предыдущему токену, чтобы продолжить выполнение всего оператора (из-за него некоторые токены начинают теряться).

Как я можно добиться, чтобы предсказать, какое имя токена назначить или как перейти к предыдущему токену, используя RuleLexer из кода Java?

1 Ответ

2 голосов
/ 18 июня 2020

Попробуйте что-то вроде этого (работает только для Java):

grammar Rule;

any           : .*? EOF;

LPAREN        : '(';
RPAREN        : ')';
UUID          : '"'[0-9a-fA-F]+'-'[0-9a-fA-F]+'-'[1-5][0-9a-fA-F]+'-'[89abAB][0-9a-fA-F]+'-'[0-9a-fA-F]+'"';
NUMERIC       : [0-9]+ ( '.' [0-9]+ )? ;
PIDENTIFIER   : IDENTIFIER {_input.LA(1) == '('}?;
IDENTIFIER    : '#' [a-zA-Z$_] [a-zA-Z$_0-9]*;
WS            : [ \r\t\u000C\n]+ -> skip;
OTHER         : . ;

Если между идентификатором и ( разрешены пробелы, сделайте что-то вроде этого:

grammar Rule;

@lexer::members {
  boolean spacesAndOpenParenAhead() {
    for (int i = 1; ; i++) {
      char ch = (char)_input.LA(i);
      if (ch == '(') {
        return true;
      }
      else if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') {
        return false;
      }
    }
  }
}

...

PIDENTIFIER         : IDENTIFIER {spacesAndOpenParenAhead()}?;
IDENTIFIER          : '#' [a-zA-Z$_] [a-zA-Z$_0-9]*;

Когда я запускаю приведенный ниже код в обоих моих примерах грамматик:

import org.antlr.v4.runtime.*;

public class Main {

    public static void main(String[] args) throws Exception {

        String source = "#someNameAttribute\n" +
                "#someNameAttribute(\"2a3a796e-9870-4b88-9f2d-383eb9566613\", 10)";

        RuleLexer lexer = new RuleLexer(CharStreams.fromString(source));

        CommonTokenStream stream = new CommonTokenStream(lexer);
        stream.fill();

        for (Token t : stream.getTokens()) {
            System.out.printf("%-20s `%s`%n",
                    RuleLexer.VOCABULARY.getDisplayName(t.getType()),
                    t.getText().replace("\n", "\\n"));
        }
    }
}

, на моей консоли печатается следующее:

IDENTIFIER           `#someNameAttribute`
PIDENTIFIER          `#someNameAttribute`
'('                  `(`
UUID                 `"2a3a796e-9870-4b88-9f2d-383eb9566613"`
OTHER                `,`
NUMERIC              `10`
')'                  `)`
...