为什么这个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'代码>以便在侦听器或访问者中解析它更直接。但是将这些类型粘在解析器规则中并不是一条真正的道路!好的,我想我在访问者内部找到了一种方法来区分类型和大小。