我们对编译器理论类的最后一项任务是让我们为Java的一小部分(不是MiniJava)创建编译器。我们的教授给了我们使用我们想要的任何工具的选择,经过大量的探索,我选择了ANTLR。我设法让扫描仪和解析器启动并运行,解析器输出AST。我现在卡住了试图获得一个树语法文件进行编译。我理解基本的想法是从解析器复制语法规则并消除大部分代码,保留重写规则,但它似乎不想编译(offendingToken错误)。我是在正确的轨道上吗?我错过了一些微不足道的事情吗?
Tree Grammar:
tree grammar J0_SemanticAnalysis;
options {
language = Java;
tokenVocab = J0_Parser;
ASTLabelType = CommonTree;
}
@header
{
package ritterre.a4;
import java.util.Map;
import java.util.HashMap;
}
@members
{
}
walk
: compilationunit
;
compilationunit
: ^(UNIT importdeclaration* classdeclaration*)
;
importdeclaration
: ^(IMP_DEC IDENTIFIER+)
;
classdeclaration
: ^(CLASS IDENTIFIER ^(EXTENDS IDENTIFIER)? fielddeclaration* methoddeclaration*)
;
fielddeclaration
: ^(FIELD_DEC IDENTIFIER type visibility? STATIC?)
;
methoddeclaration
: ^(METHOD_DEC IDENTIFIER type visibility? STATIC? ^(PARAMS parameter+)? body)
;
visibility
: PRIVATE
| PUBLIC
;
parameter
: ^(PARAM IDENTIFIER type)
;
body
: ^(BODY ^(DECLARATIONS localdeclaration*) ^(STATEMENTS statement*))
;
localdeclaration
: ^(DECLARATION type IDENTIFIER)
;
statement
: assignment
| ifstatement
| whilestatement
| returnstatement
| callstatement
| printstatement
| block
;
assignment
: ^(ASSIGN IDENTIFIER+ expression? expression)
;
ifstatement
: ^(IF relation statement ^(ELSE statement)?)
;
whilestatement
: ^(WHILE relation statement)
;
returnstatement
: ^(RETURN expression?)
;
callstatement
: ^(CALL IDENTIFIER+ expression+)
;
printstatement
: ^(PRINT expression)
;
block
: ^(STATEMENTS statement*)
;
relation
// : expression (LTHAN | GTHAN | EQEQ | NEQ)^ expression
: ^(LTHAN expression expression)
| ^(GTHAN expression expression)
| ^(EQEQ expression expression)
| ^(NEQ expression expression)
;
expression
// : (PLUS | MINUS)? term ((PLUS | MINUS)^ term)*
: ^(PLUS term term)
| ^(MINUS term term)
;
term
// : factor ((MULT | DIV)^ factor)*
: ^(MULT factor factor)
| ^(DIV factor factor)
;
factor
: NUMBER
| IDENTIFIER (DOT IDENTIFIER | LBRAC expression RBRAC)?
| NULL
| NEW IDENTIFIER LPAREN RPAREN
| NEW (INT | IDENTIFIER) (LBRAC RBRAC)?
;
type
: (INT | IDENTIFIER) (LBRAC RBRAC)?
| VOID
;
Parser Grammar:
parser grammar J0_Parser;
options
{
output = AST; // Output an AST
tokenVocab = J0_Scanner; // Pull Tokens from Scanner
//greedy = true; // forcing this throughout?! success!!
//cannot force greedy true throughout. bad things happen and the parser doesnt build
}
tokens
{
UNIT;
IMP_DEC;
FIELD_DEC;
METHOD_DEC;
PARAMS;
PARAM;
BODY;
DECLARATIONS;
STATEMENTS;
DECLARATION;
ASSIGN;
CALL;
}
@header { package ritterre.a4; }
// J0 - Extended Specification - EBNF
parse
: compilationunit EOF -> compilationunit
;
compilationunit
: importdeclaration* classdeclaration*
-> ^(UNIT importdeclaration* classdeclaration*)
;
importdeclaration
: IMPORT IDENTIFIER (DOT IDENTIFIER)* SCOLON
-> ^(IMP_DEC IDENTIFIER+)
;
classdeclaration
: (PUBLIC)? CLASS n=IDENTIFIER (EXTENDS e=IDENTIFIER)? LBRAK (fielddeclaration|methoddeclaration)* RBRAK
-> ^(CLASS $n ^(EXTENDS $e)? fielddeclaration* methoddeclaration*)
;
fielddeclaration
: visibility? STATIC? type IDENTIFIER SCOLON
-> ^(FIELD_DEC IDENTIFIER type visibility? STATIC?)
;
methoddeclaration
: visibility? STATIC? type IDENTIFIER LPAREN (parameter (COMMA parameter)*)? RPAREN body
-> ^(METHOD_DEC IDENTIFIER type visibility? STATIC? ^(PARAMS parameter+)? body)
;
visibility
: PRIVATE
| PUBLIC
;
parameter
: type IDENTIFIER
-> ^(PARAM IDENTIFIER type)
;
body
: LBRAK localdeclaration* statement* RBRAK
-> ^(BODY ^(DECLARATIONS localdeclaration*) ^(STATEMENTS statement*))
;
localdeclaration
: type IDENTIFIER SCOLON
-> ^(DECLARATION type IDENTIFIER)
;
statement
: assignment
| ifstatement
| whilestatement
| returnstatement
| callstatement
| printstatement
| block
;
assignment
: IDENTIFIER (DOT IDENTIFIER | LBRAC a=expression RBRAC)? EQ b=expression SCOLON
-> ^(ASSIGN IDENTIFIER+ $a? $b)
;
ifstatement
: IF LPAREN relation RPAREN statement (options {greedy=true;} : ELSE statement)?
-> ^(IF relation statement ^(ELSE statement)?)
;
whilestatement
: WHILE LPAREN relation RPAREN statement
-> ^(WHILE relation statement)
;
returnstatement
: RETURN expression? SCOLON
-> ^(RETURN expression?)
;
callstatement
: IDENTIFIER (DOT IDENTIFIER)? LPAREN (expression (COMMA expression)*)? RPAREN SCOLON
-> ^(CALL IDENTIFIER+ expression+)
;
printstatement
: PRINT LPAREN expression RPAREN SCOLON
-> ^(PRINT expression)
;
block
: LBRAK statement* RBRAK
-> ^(STATEMENTS statement*)
;
relation
: expression (LTHAN | GTHAN | EQEQ | NEQ)^ expression
;
expression
: (PLUS | MINUS)? term ((PLUS | MINUS)^ term)*
;
term
: factor ((MULT | DIV)^ factor)*
;
factor
: NUMBER
| IDENTIFIER (DOT IDENTIFIER | LBRAC expression RBRAC)?
| NULL
| NEW IDENTIFIER LPAREN RPAREN
| NEW (INT | IDENTIFIER) (LBRAC RBRAC)?
;
type
: (INT | IDENTIFIER) (LBRAC RBRAC)?
| VOID
;
答案 0 :(得分:3)
问题是在你的树语法中,你做了以下(我相信3次):
classdeclaration
: ^(CLASS ... ^(EXTENDS IDENTIFIER)? ... )
;
^(EXTENDS IDENTIFIER)?
部分是错误的:您需要将树包裹在括号周围,然后才将其设为可选:
classdeclaration
: ^(CLASS ... (^(EXTENDS IDENTIFIER))? ... )
;
然而,如果那就完全有点太容易了,不是吗? :)
当您解决上述问题时,ANTLR会在尝试从树语法生成树形漫游器时抱怨树语法不明确。 ANTLR会向您抛出以下内容:
错误(211):J0_SemanticAnalysis.g:61:26:[致命]规则分配具有非LL(*) 由于递归规则调用可以从alts 1,2到达的决定。解决方案 左分解或使用句法谓词或使用backtrack = true选项。
它抱怨语法中的assignment
规则:
assignment
: ^(ASSIGN IDENTIFIER+ expression? expression)
;
由于ANTLR是一个 LL 解析器生成器 1 ,它从左到右解析令牌。因此IDENTIFIER+ expression? expression
中的可选表达式使语法模糊不清。通过将?
移至上一个expression
:
assignment
: ^(ASSIGN IDENTIFIER+ expression expression?)
;
1 不要让名字ANT LR 中的最后两个字母误导你,他们代表 L anguage R ecognition,不它生成的解析器类!