这次实验让人煞费苦心啊,话说我已经写了一天的C语言文法了,囧。
总结一下,可以说:程序编写很帅很顺利,文法编写很挫很纠结。我用的是LL(1)分析法(又叫预测分析法),开始的时候花了一段时间来理解LL(1)算法,后来到设计、实现、各种测试,可谓经历了一番波折。记得刚开始写的时候想用C++,后来发现竟然忘的差不多了,囧,于是索性挫到底——用Java实现,轻喷啊。
这次实验的内容就是让你采用一种语法分析技术分析类高级语言中的基本语句,至少包括函数定义、变量说明、赋值、循环、分支等语句,同时还必须让程序有一定的错误处理的机制。
开始要精度ppt和龙书相关的章节,知道这个LL(1)分析法到底是个啥,具体的相关的知识我不再赘述,其实我弄得也不是特别透彻,首先得知道LL(1)分析器的系统结构吧:
有一句话很重要,LL(1)分析器是模拟了最左推导的过程,这句话对理解LL(1)很有帮助。
然后要理解LL(1)的通用控制算法:
漂亮,其实说的通俗一点,LL(1)分析器有一个栈,用来存放推导式,在分析的过程中,由当前栈顶元素和当前输入符号来决定下一步的操作(比如使用哪个产生式继续往下推导,或者弹出栈顶元素前移输入指针,或者报错等操作),另外不要忘了,LL(1)分析法是在模拟最左推导。
根据这个算法,我们要做的事就是构造预测分析表,根据通用控制算法写总控程序,此外,构造预测分析法要用到文法每个产生式的SELECT集,构造SELECT集需要求出每个终结符和非终结符的First集,还要求出每个非终结符的Follow集。
由于代码太多,这次不贴代码了,只是大体说说我的数据结构的设计。
我为非终结符和终结符设计了一个基类(尽管很多情况下我设计的基类被人说成是鸡肋,我还是喜欢这样做):
public abstract class MyCharacter {
public String what;
public List<String> First;
public MyCharacter() {
First = new ArrayList<String>();
}
}
然后终结符和非终结符继承这个基类:
public class TerminateCharacter extends MyCharacter {
public TerminateCharacter(String what) {
super();
this.what = what;
}
}
public class NonterminalCharacter extends MyCharacter {
public List<String> Follow;
public List<String> Sync;
public NonterminalCharacter(String what) {
super();
this.what = what;
Follow = new ArrayList<String>();
Sync = new ArrayList<String>();
}
/**
* 根据一些启发式的方法设置该终结符的同步记号集合,即把该终结符的FOLLOW集、FIRST集加入Sync集
*/
public void setSync() {
Sync.addAll(Follow); // 添加Follow集
for (String s : First) { // 添加First集
if (!Sync.contains(s)) {
Sync.add(s);
}
}
}
}
此外,产生式也作为程序的一个实体类:
public class Production {
// 产生式的左部
private String Left;
// 产生式的右部
private ArrayList<String> right;
// 产生式的Select集
public ArrayList<String> Select;
public Production(String left, ArrayList<String> right) {
Left = left;
this.right = right;