为什么这个 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 ']'
;
在我的访客中使用访问方法可以轻松访问 NUMBER
和 TYPE
。我正在考虑使用 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。
我正在为各种基本语言编写示例语法,其中包含如下说明:
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 ']'
;
在我的访客中使用访问方法可以轻松访问 NUMBER
和 TYPE
。我正在考虑使用 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。