为什么这个ANTLR语法不返回正确的类型?

为什么这个ANTLR语法不返回正确的类型?,antlr,antlr4,Antlr,Antlr4,我正在为一种不同的基本语言编写一个语法示例,其说明如下: 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我对“变量名”这样的大小写不感兴趣,所以让我们接受它们:) 我目前使用的PoC语法如下: g

我正在为一种不同的基本语言编写一个语法示例,其说明如下:

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我对“变量名”这样的大小写不感兴趣,所以让我们接受它们:)

我目前使用的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
在我的访问者内部使用访问方法可以轻松访问
编号
类型
。我正在考虑使用
ctx.type().getToken()

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


非常感谢您的时间首先:带有
-tokens
的命令将只输出lexer规则(tokens),而不是解析器规则。您的
类型
是解析器规则,因此永远不会成为
-tokens
输出的一部分

解析器规则
类型
中的文字标记:

type : 'i' NUMBER
     | 'void'
     ;
它们实际上被翻译为lexer规则,使您的lexer看起来像这样:

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
instr : type word
      ;

word  : WORD
      | type
      ;

type  : TYPE
      | VOID
      ;
如果您现在向lexer提供输入
i32
,它将创建一个
WORD
标记。它不会创建两个令牌
T_uu0
i
)和
NUMBER
32
),因为lexer尝试为给定输入进行最长匹配。这就是它的工作原理

另外,通过将
type
设置为解析器规则,您可以允许像
i32
(中间有空格的
i
)这样的输入作为
类型进行匹配。换句话说:不要在解析器中创建
类型
,而是将其作为词法规则,并确保在
单词
规则之前定义它:

type : TYPE
     | VOID
     ;

VOID     : 'void';
TYPE     : 'i' NUMBER;
NUMBER   : '-'* [0-9]+;
WORD     : [a-zA-Z_.0-9]+;
NEWLINE  : [\r\n]+;
WS       : [ \t]+ -> skip;
这将导致
i32
匹配为
类型
,而不是
单词
。如果在某些情况下(例如,输入的
i32 i32
也有效),您还希望将
i32
匹配为
WORD
,请执行以下操作:

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
instr : type word
      ;

word  : WORD
      | type
      ;

type  : TYPE
      | VOID
      ;
最后,您允许
NUMBER
前面有零个或多个
-
符号,但您可能不希望
i-32
作为
类型
标记进行匹配,对吗?最好删除
-
符号,并在解析器规则中匹配它:

expr : instr
     | MINUS expr
     | NUMBER
     | WORD
     | ...
     ;
...
MINUS    : '-';
...
NUMBER   : [0-9]+;
...
type
 : TYPE                    #simpleType
 | VOID                    #voidType
 | '[' NUMBER '*' TYPE ']' #arrayType
 ;
更复杂的类型,如
[8*i32]
更适合作为解析器规则:

expr : instr
     | MINUS expr
     | NUMBER
     | WORD
     | ...
     ;
...
MINUS    : '-';
...
NUMBER   : [0-9]+;
...
type
 : TYPE                    #simpleType
 | VOID                    #voidType
 | '[' NUMBER '*' TYPE ']' #arrayType
 ;

备选方案末尾的
#…
部分被调用。

回答得很好!它解决了我的问题。谢谢!对不起,我现在如何访问该类型的数字32?我认为像ctx.type(0.type().NUMBER()之类的东西。。谢谢你没有:)。一旦它成为单个令牌,您就无法提取其中的一部分。您可以在visitor
{“i32”:32,“i16”:16,…}
中保留一个带有类型映射的字典,或者在lexer中使类型更加具体:
i32:'i32'以便在侦听器或访问者中解析它更直接。但是将这些类型粘在解析器规则中并不是一条真正的道路!好的,我想我在访问者内部找到了一种方法来区分类型和大小。