我正在为自己的语言编写解析器。我试图解析短语
Number a is 10;
基本上等同于int a = 10;
。
它应与variable_def
规则匹配。当我运行它时,我收到错误
line 1:0 extraneous input 'Number' expecting {<EOF>, 'while', ';', 'if', 'function', TYPE, 'global', 'room', ID}
line 1:9 mismatched input 'is' expecting '('
这是我的语法:
grammar Script;
@header {
package script;
}
// PARSER
program
:
block EOF
;
block
:
(
statement
| functionDecl
)*
;
statement
:
(variable_def
| functionCall
| ifStatement
| forStatement
| whileStatement) ';'
;
whileStatement
:
'while' '(' expression ')' '{' (statement)* '}'
;
forStatement
:
;
ifStatement
:
'if' '(' expression ')' '{' statement* '}'
(
(
'else' '{' statement* '}'
)
|
(
'else' ifStatement
)
)?
;
functionDecl
:
'function' ID
(
'('
(
TYPE ID
)?
(
',' TYPE ID
)* ')'
)?
(
'returns' RETURN_TYPE
)? '{' statement* '}'
;
functionCall
:
ID '(' exprList? ')'
;
exprList
:
expression
(
',' expression
)*
;
variable_def
:
TYPE assignment
| GLOBAL variable_def
| ROOM variable_def
;
expression
:
'-' expression # unaryMinusExpression
| '!' expression # notExpression
| expression '^' expression # powerExpression
| expression '*' expression # multiplyExpression
| expression '/' expression # divideExpression
| expression '%' expression # modulusExpression
| expression '+' expression # addExpression
| expression '-' expression # subtractExpression
| expression '>=' expression # gtEqExpression
| expression '<=' expression # ltEqExpression
| expression '>' expression # gtExpression
| expression '<' expression # ltExpression
| expression '==' expression # eqExpression
| expression '!=' expression # notEqExpression
| expression '&&' expression # andExpression
| expression '||' expression # orExpression
| expression IN expression # inExpression
| NUMBER # numberExpression
| BOOLEAN # boolExpression
| functionCall # functionCallExpression
| '(' expression ')' # expressionExpression
;
assignment
:
ID ASSIGN expression
;
// LEXER
RETURN_TYPE
:
TYPE
| 'Nothing'
;
TYPE
:
'Number'
| 'String'
| 'Anything'
| 'Boolean'
| 'Growable'? 'List' 'of' TYPE
;
GLOBAL
:
'global'
;
ROOM
:
'room'
;
ASSIGN
:
'is'
(
'a'
| 'an'
| 'the'
)?
;
EQUAL
:
'is'?
(
'equal'
(
's'
| 'to'
)?
| 'equivalent' 'to'?
| 'the'? 'same' 'as'?
)
;
IN
:
'in'
;
BOOLEAN
:
'true'
| 'false'
;
NUMBER
:
'-'? INT '.' INT EXP? // 1.35, 1.35E-9, 0.3, -4.5
| '-'? '.' INT EXP? // -.35, .35e5
| '-'? INT EXP // 1e10 -3e4
| '-'? INT // -3, 45
;
fragment
EXP
:
[Ee] [+\-]? INT
;
fragment
INT
:
'0'
| [1-9] [0-9]*
;
STRING
:
'"'
(
' ' .. '~'
)* '"'
;
ID
:
(
'a' .. 'z'
| 'A' .. 'Z'
| '_'
)
(
'a' .. 'z'
| 'A' .. 'Z'
| '0' .. '9'
| '_'
)*
;
fragment
JAVADOC_COMMENT
:
'/*' .*? '*/'
;
fragment
LINE_COMMENT
:
(
'//'
| '#'
) ~( '\r' | '\n' )*
;
COMMENT
:
(
LINE_COMMENT
| JAVADOC_COMMENT
) -> skip
;
WS
:
[ \t\n\r]+ -> skip
;
如何解决此错误?
答案 0 :(得分:1)
发生特定错误是因为在语法的词法分析器中,TYPE术语与RETURN_TYPE词法分析词冲突。还有其他错误,但问题展示可能会被剥离到以下:
grammar Script;
program
:
block EOF
;
block
:
(
statement
| functionDecl
)*
;
statement
:
(
variable_def
) ';'
;
functionDecl
:
'function' ID
(
'returns' RETURN_TYPE
)?
'{' statement* '}'
;
variable_def
:
TYPE assignment
;
expression
:
NUMBER # numberExpression
;
assignment
:
ID ASSIGN expression
;
RETURN_TYPE
:
TYPE
| 'Nothing'
;
TYPE
:
'Number'
;
ASSIGN
:
'is'
(
'a'
| 'an'
| 'the'
)?
;
NUMBER
:
'-'? INT // -3, 45
;
fragment
INT
:
'0'
| [1-9] [0-9]*
;
ID
:
(
'a' .. 'z'
| 'A' .. 'Z'
| '_'
)
(
'a' .. 'z'
| 'A' .. 'Z'
| '0' .. '9'
| '_'
)*
;
WS
:
[ \t\n\r]+ -> skip
;
如果将RETURN_TYPE
转换为解析器规则,例如returnType
,则一切正常(对于此特定测试,因为我说你的语法包含其他错误,如此一)。这演示了关于Antlr(以及所有其他具有词法分析器和解析器分离的解析器生成器)行为的基本原理:词法分析器始终在其自己的上下文中工作,它无法确定特定的符号序列是否为如果两个术语共享相同的字符序列,则为一个术语。所以你有两个选择:引入 lexer 上下文(称为modes)或者在词法分析器级别只保留基本和明确的实体,并将其他所有内容移到解析器中。
答案 1 :(得分:1)
主要原因是因为在您当前的语法中,永远不会创建TYPE
令牌,因为RETURN_TYPE
也匹配TYPE
并且在TYPE
之前定义(并且因此优先于它。)
另外,你在词法分析器中做得太多了。一旦你开始在词法分析器中粘合单词,这就是你应该制作那些规则解析器规则的标志。
词法分析器可能会跳过空格,但只能从解析器规则中跳过。以您的ASSIGN
规则为例:
ASSIGN
: 'is' ( 'a' | 'an' | 'the' )?
;
此规则与字符串"is a"
("is"
和"a"
之间的空格)不匹配,只会匹配"isa"
,"isan"
和{{ 1}}。解决方案:从中创建解析器规则:
"isthe"
相当于:
assign
: 'is' ( 'a' | 'an' | 'the' )?
;
这会导致代币assign
: 'is' ( 'a' | 'an' | 'the' )?
;
IS : 'is';
A : 'a';
AN : 'an';
THE : 'the';
...
ID : [a-zA-Z_] [a-zA-Z_0-9]*;
,'is'
,'a'
和'an'
永远不会匹配为'the'
代币。因此,以下来源将作为正确的分配失败:
ID
因为Number a is 42;
被标记为'a'
令牌,而不是A
。
要解决此问题,您可以添加以下解析器规则:
ID
并在其他解析器规则中使用该规则而不是id
: ( ID | A | AN | IS | THE | ... )
;
。
快速演示如下所示:
ID