Почему эта грамматика ANTLR не возвращает правильный тип? - PullRequest
1 голос
/ 10 апреля 2020

Я работаю над примером грамматики для базового языка c, в котором есть такие инструкции:

i8 my_variable_1_8
i16 my_second_variable_2_something_else
i32 another_variable
i4 forth
i8 last_one_1
void empty
void empty_for_the_2_time

Чтобы быть понятным, имя переменной может содержать любую букву, цифру, подчеркивание и точка в любом порядке. ATM Меня не интересует случай, подобный `` `.... variable_name ....`, поэтому давайте их примем:)

Грамматика Po C, которую я сейчас использую, находится в абзаце ниже:

grammar example;

prog:   (expr NEWLINE)+;

expr    : instr
    ;

instr     : type WORD
      ; 

type    : 'i' NUMBER
    | 'void'
        ;

NUMBER  : ('-')* ([0-9])+
    ;

WORD :  (LETTER|'_'|'.'|[0-9])+
     ;

LETTER   : ([a-z]|[A-Z]) ;

NEWLINE  : [\r\n]+ ;

WS: [ \t\n\r]+ -> skip ;

Файл примера, который я пытаюсь проанализировать:

i32 i_cannot_parse_this_1_as_i_want
void hello 

Вывод

➜  grammar antlr4 -no-listener example.g4 && javac *.java && grun example prog -tokens example.txt
[@0,0:2='i32',<WORD>,1:0]
[@1,4:34='i_cannot_parse_this_1_as_i_want',<WORD>,1:4]
[@2,35:35='\n',<NEWLINE>,1:35]
[@3,36:39='void',<'void'>,2:0]
[@4,41:45='hello',<WORD>,2:5]
[@5,48:47='<EOF>',<EOF>,3:0]
line 1:0 mismatched input 'i32' expecting {'i', 'void'}
➜  grammar

Как видите, i32 считается быть WORD вместо типа. Должно быть что-то, чего мне не хватает в приоритетах, но я не могу этого понять.

Наконец, я хотел бы сказать, что я создаю правило синтаксического анализатора type, потому что во время выполнения, когда я переопределяю visitInstr метод Я хотел бы иметь возможность сделать что-то вроде ctx.type().NUMBER().

РЕДАКТИРОВАТЬ 1

Предположим, что теперь в качестве типа я хочу ввести массив my_variable = [ 8 * i32 ] Как бы решить эту ситуацию. Вы бы просто добавили что-то вроде:

TYPE
        : 'i' NUMBER
        | '[' NUMBER '*' TYPE ']'
        ;

Использование метода посещения внутри моего посетителя может легко получить доступ к NUMBER и TYPE. Я думал использовать ctx.type().getToken()

Есть ли лучший способ добиться этого? Пожалуйста, учтите, что я хотел бы добавить другие более сложные типы.

Большое спасибо за ваше время

1 Ответ

2 голосов
/ 10 апреля 2020

Первое: команда с -tokens будет выводить только правила лексера (токены), но не правила парсера. Ваш type является правилом синтаксического анализа, поэтому он никогда не будет частью вывода -tokens.

Буквальные токены внутри вашего правила синтаксического анализатора type:

type : 'i' NUMBER
     | 'void'
     ;

действительно переведены В соответствии с правилами лексера, ваш лексер будет выглядеть следующим образом:

T__0     : 'i';
T__1     : 'void';
NUMBER   : ('-')* ([0-9])+;
WORD     : (LETTER|'_'|'.'|[0-9])+;
LETTER   : ([a-z]|[A-Z]);
NEWLINE  : [\r\n]+;
WS       : [ \t\n\r]+ -> skip; // NOTE: remove the \n\r from this class since it is already matched by NEWLINE

Если вы теперь подадите лексеру ввод i32, он создаст один токен WORD. Он не создаст два токена T__0 (i) и NUMBER (32), потому что лексер пытается найти самое длинное соответствие для данного ввода. Вот как это работает.

Кроме того, делая type правилом синтаксического анализа, вы позволяете вводу типа i 32 (i с пробелами между ними) сопоставляться как type , Другими словами: не создавайте type в вашем парсере, но вместо этого сделайте его правилом лексера и убедитесь, что оно определено до правила WORD:

type : TYPE
     | VOID
     ;

VOID     : 'void';
TYPE     : 'i' NUMBER;
NUMBER   : '-'* [0-9]+;
WORD     : [a-zA-Z_.0-9]+;
NEWLINE  : [\r\n]+;
WS       : [ \t]+ -> skip;

Это приведет к совпадению i32 как TYPE, а не WORD. Если вы также хотите, чтобы i32 соответствовало WORD в некоторых случаях (например, ввод i32 i32 также допустим), сделайте что-то вроде этого:

instr : type word
      ;

word  : WORD
      | type
      ;

type  : TYPE
      | VOID
      ;

Наконец, вы разрешаете NUMBER будет иметь ноль или более - знаков перед ним, но вы, вероятно, не хотите, чтобы i-32 соответствовал токену TYPE, верно? Лучше всего удалить знак - и сопоставить его с правилом синтаксического анализа:

expr : instr
     | MINUS expr
     | NUMBER
     | WORD
     | ...
     ;
...
MINUS    : '-';
...
NUMBER   : [0-9]+;
...

Более сложный тип, например [ 8 * i32 ], будет более уместным в качестве правила синтаксического анализатора:

type
 : TYPE                    #simpleType
 | VOID                    #voidType
 | '[' NUMBER '*' TYPE ']' #arrayType
 ;

#... части в конце альтернативы называются метками элемента правила .

...