为什么这个 ANTLR 文法 return 不是正确的类型?

Why this ANTLR grammar does not return the proper type?

我正在为各种基本语言编写示例语法,其中包含如下说明:

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.....` 这样的情况不感兴趣所以让我们接受它们:)

我目前使用的PoC语法在下面的段落中:

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 ']'
        ;

在我的访客中使用访问方法可以轻松访问 NUMBERTYPE。我正在考虑使用 ctx.type().getToken()

有没有更好的方法来实现?请考虑我想添加其他更复杂的类型。

非常感谢您的宝贵时间

首先:带-tokens的命令只会输出词法分析器规则(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
 ;

选项末尾的 #... 部分称为 rule element labels