计科70 徐明宽 2017011310
对methodDef
,我在Decaf.spec中仿照STATIC Type Id '(' VarList ')' Block FieldList
写了ABSTRACT Type Id '(' VarList ')' ';' FieldList
(classDef
已在PA1-A中改好)。
与PA1-A相同。
我参考Decaf.spec中的VarList
实现了TypeList
,并写了非终结符AfterAtomType
。我在SemValue.java中添加了List<MutablePair<List<Tree.TypeLit>, Integer>> typeListList
以同时存储形如int(int, int)[](int, int, int)
中的参数列表int, int
和int, int, int
以及其中的[]
的个数,并在AbstractParser.java中添加了protected SemValue svTypess()
。
然后我发现对new
的处理也用到了Type
的定义,需要手动加入函数类型,类比AfterAtomType
实现了AfterNewAtomType
。
我参照实验说明写了FUN '(' VarList ')' AfterFunExpr
,并在AfterFunExpr
的产生式中实现了Lambda表达式的两种情况。
对于AfterLParen
和Expr8
中的函数调用,我重写了ExprT8
使其对应成员字段选择、数组索引、函数调用这一优先级,同时发现函数类型部分可以使用。对于SemValue
的thunkList
而无需开一个List<MutablePair<List<Tree.TypeLit>, Integer>> typeListList
Expr9
中的函数调用,我直接将其删除了,因为函数调用并不属于这一优先级。
我实现了“实验内容”一节中推荐的算法,当遇到$Begin(A)$中的符号时恢复分析$A$,当遇到$End(A)$中的符号时返回null
(并且使得$A$的各父节点均返回null
),否则跳过该符号(消耗终结符)。
在实验过程中,我发现parseSymbol
的参数symbol
为int
类型,调试时不能直观看出输出的symbol
具体表示哪个非终结符,于是我读入了LLTable.java
,在里面查找字符串public static final int
,以此建立一个symbol
与非终结符字符串的对应关系表,方便调试。
我发现框架中对大小比较运算符实现的是左结合,而语言规范中写的是不结合,于是将产生式ExprT4 -> Op4 Expr5 ExprT4
改成了ExprT4 -> Op4 Expr5
。
如https://github.com/paulzfm/ll1pg/wiki/2.-Resolving-Conflicts所述,当冲突发生时写在前面的产生式优先级更高,所以`E : else S | /* empty */会优先将
E解析为
else S`而不是空,从而解决空悬else问题。
框架中用多个非终结符来描述二元运算符的优先级,如Expr6
表示“项”,即只含乘除模及更高优先级运算符的表达式;Expr5
则表示只含加减及更高优先级运算符的表达式。对于左结合的二元运算符如加减(Op5
),框架中实现了产生式Expr5 -> Expr6 ExprT5
与ExprT5 -> Op5 Expr6 ExprT5 | /* empty */
;右结合的只需将左结合的代码中在thunkList
的开头插入改为在结尾插入;不结合的可以写ExprT4 -> Op4 Expr5 | /* empty */
。
Q3. 无论何种错误恢复方法,都无法完全避免误报的问题。 请举出一个具体的 Decaf 程序(显然它要有语法错误),用你实现的错误恢复算法进行语法分析时会带来误报。 并说明该算法为什么无法避免这种误报。
class Main {
static void main() {
int(, int) x;
}
}
我的输出:
*** Error at (3,13): syntax error
*** Error at (3,18): syntax error
*** Error at (5,1): syntax error
其中Error at (5,1)
明显属于误报。对比我在PA1-A阶段的输出:
*** Error at (3,13): syntax error
这是因为集合$End(A)$太大了,应跳过一些符号时我们却选择了分析失败、不跳过符号直接返回。如$',' \in follow(AfterAtomType)$,但我们在应用展开式Var -> Type Id
时并不期望Id
以','
开头。因此,虽然$',' \in follow(AfterAtomType)$,我们也不应直接返回,因为$',' \in follow(AfterAtomType)$是因为有TypeList -> Type TypeList1
、TypeList1 -> ',' Type TypeList1
、Type -> AtomType AfterAtomType
这些产生式,而我们在分析到 AfterAtomType
时 并没有遇到过TypeList
这个非终结符,所以可以预见的是这个','
将会导致大量非终结符分析失败,但我们在这里甚至不能排除这个','
是Block
后面的这一可能性:我们无法权衡应跳过一个终结符还是回溯若干层非终结符,因而无法避免这种误报。
我们可能会想,
我们的错误恢复算法在读入int(
后看到','
,分析非终结符TypeList
、AfterAtomType
、Type
时均没有消耗这个','
就分析失败了,于是接下来试图分析Type
后面的Id
看到','
失败,于是分析Var
失败,分析Initializer
看到','
失败,于是分析SimpleStmt
失败,分析Stmt
失败,分析StmtList
失败,分析Block
失败,返回到FieldList
时才意识到必须要跳过这个','
了,于是应用了我们并不期望的产生式FieldList -> Type Id AfterIdField FieldList
,消耗了int
然后看到')'
于是分析Id
失败,跳过')' x
后消耗了';'
,回到ClassDef
消耗了第一个'}'
,于是在第二个'}'
处再次报错。
无。