C语言的语法分析器——java实现

来源:互联网 发布:淘宝点结算没反应 编辑:程序博客网 时间:2024/05/20 13:11

编译原理语法分析的实验已经是上上周的事了,可是今天才得以更新博客,其原因必然是一直木有做完,唉想想就伤感。。我这两周的大部分青春都献给了编译原理,虽然花了好长时间,可说实话写这篇博文心里还是十分没底,因为到最后还是没有一个令自己满意的结果,特别愧疚,实验报告交的都是最基础的课件上的例子。但是已经拖了两周了,第三次实验又浩浩荡荡的袭来,唉水平有限,力不从心,老了啊。。没办法分数还得说的过去,昨天找老师检查了一下,先做到这个程度吧,后面需要学习的地方还有很多,可能能顿悟呢也说不定哦~有时间再回头修改吧~

上次实验用的是C,所以必然要通过读文件的方式来获取上次的结果,先把过程缕一下哈~


上次实验是词法分析,使用C程序将单词分开,忽略空格和注释,还有头文件(因为语法分析不想处理啦~),然后输出该单词,以及token(种别码,入口地址),当然有些没有入口地址,常量和变量的符号表也保存在文件中,并给以编号(即入口地址)。重复的常量会剔除,而重复的变量并没有(因为可能作用域不同,符号表的内容也不一定一样)。根据词法分析的结果,可以进行语法分析,即通过读入token串(相当于在词法分析识别了单词的含义),根据给定的文法进行产生式的推导。为了方便读入,我将之前的token文件只保留了种别码,并在末尾手动加上了结束符号100(种别码在词法分析的头文件里宏定义了,没有100,固选择100,之所以是数字因为是终结符,语法分析的程序通过判断是不是数字来判断是不是终结符)。我采用了自顶向下的分析方法,对文法的要求是LL1文法,要求同一非终结符的产生式的select集互不相交,这样才不会产生歧义导致不知道选择哪个产生式,其实单纯根据select集也可以分析出来,只不过通过预测分析表效率更高些,预测分析表也是通过Select集来的。所以求select集是很关键的,求出来就能分析了,有错的话可能推导不下去,错误处理待会儿再说。


求select集的过程中需要用到follow集和first集,算法如下:







有两点需要注意:

1、注意分清first(X)和first(α)的区别。X表示一个符号,而α表示一串符号,求Select集的时候用到的是求一串符号的first集,而中间会用到求一个符号的first集,两种算法分别如下:

/** * 求一个符号的First集 * @param X * @param formulas * @return  */public static HashSet<Integer> getFirst(String X, ArrayList<Formula> formulas){//System.out.println("getFirst:"+X);HashSet<Integer> first = new HashSet<Integer>();if(isNumeric(X))//如果是终结符{first.add(Integer.parseInt(X));return first;}String tmp;for(Formula f: formulas){int i=0;if(X.equals(f.getLeft())){if(i==f.getRight().size()){return first;}tmp = f.getRight().get(i);if(isNumeric(tmp)){first.add(Integer.parseInt(tmp));//不能return}else if(!tmp.equals("@")){while(i<f.getRight().size()){if(!f.getRight().get(i).equals(X))//避免死循环{first.addAll(getFirst(f.getRight().get(i), formulas));//递归调用getFirst}if(!canNull(f.getRight().get(i++), formulas)){break;}}}}}return first;}/** * 求一串的first集 * @param right * @param formulas * @return */public static HashSet<Integer> getFirsts(ArrayList<String> right, ArrayList<Formula> formulas){HashSet<Integer> first = new HashSet<Integer>();first.addAll(getFirst(right.get(0), formulas));int k=0;while(canNull(right.get(k), formulas) && k<right.size()-1){first.addAll(getFirst(right.get(k+1), formulas));k++;}return first;}

2、如果采用递归求follow集的话可能出现死循环。先说一层的,比如A-->B A,那么求A的follow集会求左部(还是A)的follow集,这样产生死循环,在此我们可以加上判断,当二者不相等的时候在求,算法如下(但仍面临问题):

/** * 获得Follow集  使用了递归 * @param X * @param formulas * @return */public static HashSet<Integer> getFollow(String X, ArrayList<Formula> formulas){//System.out.println("getFollow:"+X);HashSet<Integer> follow = new HashSet<Integer>();ArrayList<String> right = null;if(X.equals(LL1.start))//开始符号也可能死循环{follow.add(Integer.parseInt(LL1.end));}for(Formula f: formulas){right = f.getRight();for(int i=0; i<right.size(); i++){if(X.equals(right.get(i))){//后面没有符号串if(i==right.size()-1){if(!f.getLeft().equals(X)){follow.addAll(getFollow(f.getLeft(), formulas));return follow;//右部后面没有符号,直接return}}else{if(isNumeric(right.get(i+1))){follow.add(Integer.parseInt(right.get(i+1)));continue;}follow.addAll(getFirst(right.get(i+1), formulas));int j;//判断后面的符号串是否都能推出空for(j=i+1; j<right.size() && canNull(right.get(j), formulas); j++);if(j == right.size()){follow.addAll(getFollow(f.getLeft(), formulas));}}}}}return follow;}


但是这只是一层,文法我们很难用肉眼看出来,如果出现求A的follow集需要求B的follow集,而求B的follow集又要求A的follow集,这样的循环恐怕递归不好判断吧。没有想到合适的方法,固在主程序中增加了follow集的集合,由于没有给终结符和非终结符编号,采用HashMap实现,注意follow集要先初始化,代码简单就不贴了,算法如下:

/** * 非递归求follow集 */static void setFollows(){boolean changes;boolean flag;int sizeBefore;int sizeAfter;ArrayList<String> beta;HashSet<Integer> tmp = new HashSet<Integer>();do{changes = false;for(Formula f: formulas){beta = (ArrayList<String>) f.getRight().clone();//beta不能修改原来的rightfor(String right: f.getRight()){beta.remove(0);if(nonTerminal.contains(right)){//System.out.println("right+"+right);if(follows.get(right).isEmpty()){sizeBefore = 0;}else{sizeBefore = follows.get(right).size();}if(beta.size()!=0){tmp = Util.getFirsts(beta, formulas);//flag = tmp.remove("@");int x;for(x=0; x<beta.size() && Util.canNull(beta.get(x), formulas); x++);if(x==beta.size()){flag = true;}else{flag = false;}follows.get(right).addAll(tmp);}else{flag = true;}if(flag && !right.equals(f.getLeft())){follows.get(right).addAll(follows.get(f.getLeft()));}sizeAfter = follows.get(right).size();if(sizeBefore!=sizeAfter){changes = true;}}}}}while(changes);}

接下来就可以求select集了,我的程序还有一个Formula的产生式的类,数据结构如下:

String left;ArrayList<String> right = new ArrayList<String>();HashSet<Integer> select = new HashSet<Integer>();

保存左部,右部和select集,另外有一个格式化输出函数和判断和另一个产生式的右部是否相同(作用稍后提到)。
之前根据递归的求follow集的方法设置select集,有死循环出现,所以改用内存中保存的follow集的集合来设置,Select集的算法如下:

/**根据保存的follow集来设置select的值 *  */static void setSelectFromFollow(){HashSet<Integer> select = new HashSet<Integer>();int i;for(Formula f: formulas){if(f.getRight().get(0).equals("@")){select.addAll(follows.get(f.getLeft()));}else{select.addAll(Util.getFirsts(f.getRight(), formulas));for(i=0; i<f.getRight().size() && Util.canNull(f.getRight().get(i), formulas); i++);if(i==f.getRight().size()){select.addAll(follows.get(f.getLeft()));}}f.setSelect((HashSet<Integer>) select.clone());select.clear();}}
然后将select集填入预测分析表,空的部分采用一些启发式规则选择填写error或者同步记号,本程序将follow集作为同步记号,置为-2,error空项置为-1,有产生式的表中内容为产生式的编号。算法如下:

/** * 建立预测分析表,不能基于左部都在一起,因为文法变换后顺序不定 */static void fillPredictMap(){Iterator<Integer> terIter = terminal.iterator();HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();Formula f;Integer terInt;for(int i=0; i<formulas.size(); i++){f = formulas.get(i);while(terIter.hasNext()){terInt = terIter.next();if(f.getSelect().contains(terInt)){map.put(terInt, i);if(predictMap.get(f.getLeft())==null)//没有该终非结符的表,建立{predictMap.put(f.getLeft(), (HashMap<Integer, Integer>) map.clone());}else//有则直接get再添加map{predictMap.get(f.getLeft()).put(terInt, i);}map.clear();}}terIter = terminal.iterator();//迭代器复位}//填synch和error项Iterator<String> nonIter = nonTerminal.iterator();String nonStr;while(nonIter.hasNext()){terIter = terminal.iterator();nonStr = nonIter.next();while(terIter.hasNext()){terInt = terIter.next();if(predictMap.get(nonStr)==null){System.out.println("nonStr="+nonStr);}if(predictMap.get(nonStr).get(terInt)==null){if(follows.get(nonStr).contains(terInt)){predictMap.get(nonStr).put(terInt, -2);}else{predictMap.get(nonStr).put(terInt, -1);}}}}}

之后就可以根据预测分析表进行推导了,输出推导的过程,即采用的不同产生式,推导过程如下(附算法):


/** * 根据预测分析表推导,输出栈里的内容 */static void deriveFromTable(){int result;Formula f;while(!stack.isEmpty()){//System.out.println("stack peek: "+stack.peek()+"\t"+"input peek:"+input.peek());if(predictMap==null){System.out.println("预测分析表为空");}else if(predictMap.get(stack.peek())==null){System.out.println("表中"+stack.peek()+"为空");}else if(predictMap.get(stack.peek()).get(input.peek())==null){System.out.println("表中表"+stack.peek()+"\t"+input.peek()+"为空");}result = predictMap.get(stack.peek()).get(input.peek());switch(result){case -1:System.out.println(stack.peek()+" "+input.peek()+" error项,弹出输入符号:"+input.poll());break;case -2:System.out.println(stack.peek()+" "+input.peek()+" synch项,弹出栈顶元素:"+stack.pop());break;default:f = formulas.get(result);System.out.println(f.output());//将产生式左部出队列stack.pop();if(!f.getRight().get(0).equals("@")){//将产生式右部入队列for(int i=(f.getRight().size()-1); i>=0; i--){stack.push(f.getRight().get(i));}}while(!stack.isEmpty() && Util.isNumeric(stack.peek()))//不是if是while,加上非空判断{if( input.peek()==Integer.parseInt(stack.peek()))//不能是t,因为出栈后可能变化,是input.peek(){input.poll();stack.pop();}}}}}

大体思路就是这样,运行课件上的例子倒是简单,但是运行C语言的程序就稍微困难一些了。关键是C语言的文法比较复杂,之前写好了有各种左递归的回溯(下附左递归,回溯的消除方法),直接的消除还好,关键是有许多间接的左递归和回溯不容易看出来,给人工消除造成了很大的麻烦。其实两个星期以前就开始纠结于文法不符合LL1的要求,后来一直是在处理文法的问题。人工解决不行我们还有程序嘛~所以就写了消除直接左递归和直接回溯的算法,由于有间接左递归和回溯,固消除之后将右部第一个非终结符进行替换,在进行消除,知道产生式不再变化为止,之前写的基于相同的左部在一起的算法,但是由于产生式不断变化,左部相同的不一定在一起,后来写了新的算法,还有新加的产生式不能直接在左部后面加"'"之类的符号,这样会出现歧义。比如两个相同的E'所表达的含义其实不一定是一样的,所以用了int的静态变量给新的产生式编号,由于文法确定后会扫描文法根据是否是数字判断是不是终结符,所以该新非终结符左右各加入尖括号,变成这样“<数字>”,优化后的算法如下(还是先伪代码后代码实现):



 * 消除直接左递归,左部相同的不一定在一起 */static void removeLeftRecursion(){//System.out.println("removeLeft called...");Formula fi;Formula fj;String left;boolean flag;ArrayList<String>  forNull = new ArrayList<String>();ArrayList<String> tmp = new ArrayList<String>();forNull.add("@");for(int i=formulas.size()-1; i>=0; i--){fi = formulas.get(i);if(fi.getLeft().equals(fi.getRight().get(0))){flag = true;//System.out.println(f.output());formulaChange = true;System.out.println("removeLeftRecursion true");left = fi.getLeft();fi.getRight().remove(0);fi.getRight().add("<"+number+">");fi.setLeft("<"+number+">");for(int j=formulas.size()-1; j>=0; j--){fj = formulas.get(j);if(left.equals(fj.getLeft())){if(left.equals(fj.getRight().get(0)))//左递归{fj.setLeft("<"+number+">");//加尖括号避免看成终结符fj.getRight().remove(0);fj.getRight().add("<"+number+">");}else{flag = false;//看是否都是左递归,进入此处表示有不是左递归的产生式if(fj.getRight().get(0).equals("@")){fj.getRight().remove(0);}fj.getRight().add("<"+number+">");}}}formulas.add(new Formula("<"+number+">", (ArrayList<String>) forNull.clone()));if(flag){tmp.add("<"+number+">");formulas.add(new Formula(left, (ArrayList<String>) tmp.clone()));tmp.clear();}number++;}}}
/** * 消除直接回溯,提取左因子 */static void removeBack(){//System.out.println("removeBack called...");int k;boolean flag = false;ArrayList<String> right = new ArrayList<String>();do{flag = false;for(int i=formulas.size()-1; i>=0; i--){Formula fi = formulas.get(i);for(int j=i-1; j>=0; j--){Formula fj = formulas.get(j);if(fj.getRight().get(0).equals("@"))//空产生式略过{continue;}if(fj.getLeft().equals(fi.getLeft()) && Util.isNumeric(fi.getRight().get(0))){for(k=0; k<fi.getRight().size()&&k<fj.getRight().size()&&fi.getRight().get(k).equals(fj.getRight().get(k)); k++);if(k>0){//System.out.println("i="+i+":"+fi.output()+"   |||||    "+"j="+j+":"+fj.output());formulaChange = true;//System.out.println("removeBack true");for(int a=0; a<k; a++){right.add(fi.getRight().get(0));fi.getRight().remove(0);fj.getRight().remove(0);}right.add("<"+number+">");formulas.add(new Formula(fi.getLeft(), (ArrayList<String>) right.clone()));right.clear();if(fi.getRight().isEmpty()){fi.getRight().add("@");}if(fj.getRight().isEmpty()){fj.getRight().add("@");}fi.setLeft("<"+number+">");fj.setLeft("<"+number+">");number++;flag = true;break;}}}}}while(flag);}

/** * 将非终结符换位终结符代入,注意考虑空,此时不能有左递归,否则死循环, * 新的产生式加在后面,逆向遍历 */static void substituteFormula(){Formula fi;Formula fj;ArrayList<String> right = new ArrayList<String>();ArrayList<String> beta = new ArrayList<String>();for(int i=formulas.size()-1; i>=0; i--){fi = formulas.get(i);if(!Util.isNumeric(fi.getRight().get(0))&& !fi.getRight().get(0).equals("@")&& Util.isBeginWithTerminal(fi.getRight().get(0), formulas)){//System.out.println("substitute true..");//System.out.println(formulas.get(i).output());formulaChange = true;for(int j=formulas.size()-1; j>=0; j--){fj = formulas.get(j);if(fj.getLeft().equals(fi.getRight().get(0))){beta = (ArrayList<String>) fi.getRight().clone();beta.remove(0);right = (ArrayList<String>) fj.getRight().clone();right.addAll(beta);formulas.add(new Formula(fi.getLeft(), (ArrayList<String>) right.clone()));}}formulas.remove(i);}}}


这样实现下来还可能遇到意义相同但长得不一样的非终结符,即推出的产生式都是完全一样的。这样的非终结符可以合并,以免产生式太多不好debug。所以产生式的类里有一个判断右部是否相同的方法,如下:

public boolean isRightSame(Formula f){int k;if(f.getRight().size()==right.size()){for(k=0; k<right.size() && right.get(k).equals(f.getRight().get(k)); k++);if(k==right.size()){return true;}}return false;}


这样如果两个非终结符推出的产生式都完全一样,且个数相同(真子集也不能说明两个非终结符一样),就以一个为基础,将另一个的产生式删除,并且替换删掉的非终结符为基础的那个非终结符,算法如下:

/** * 合并相同的产生式,即合并实质一样的非终结符 */static void mergeFormulas(){Formula f1;Formula f2;String f2Left;ArrayList<String> right;for(int i=0; i<formulas.size(); i++){f1 = formulas.get(i);for(int j=i+1; j<formulas.size(); j++){ if(!f1.getLeft().equals(formulas.get(j).getLeft())){f2 = formulas.get(j);if(Util.isNonTerSame(f1.getLeft(), f2.getLeft(),formulas)){f2Left = f2.getLeft();//遍历一遍删掉该非终结符推出的产生式,并将右部有该非终结符的替换为i的for(int k=0; k<formulas.size(); k++){if(formulas.get(k).getLeft().equals(f2Left)){formulas.remove(k);}else{right = formulas.get(k).getRight();for(int m=0; m<right.size(); m++){if(right.get(m).equals(f2Left)){right.set(m, f1.getLeft());}}}}}}}}}

这样我们新的文法就产生啦~!庆祝一下~!但是理论上是这样,事实上总是有差距的。。可能我程序有bug,还求大家帮忙看看~总之有的文法还是不能分析,或者一直死循环一直会有改变,所以我最后没办法才修改了文法,简化了许多许多许多。。多到我都不好意思贴出来了。。没有考虑运算符号的优先级,也没有考虑括号神马的。。唉。。时间就是生命啊。。不过话说回来,并不是所有文法都能转变为LL1的,这也可能是死循环的原因?额。。我不是故意推卸责任说自己算法木有问题的。。但是到底哪些能哪些不能呢?我还不太清楚额。。总之。。还是写了个算法判断是不是LL1文法,当然你开始判断一下是的话就万事大吉啦~什么都不用消除咯~算法如下:

/** * 判断是否是LL1文法 * @return */static boolean isLL1(){HashSet<Integer> tmp = new HashSet<Integer>();for(int i=0; i<formulas.size(); i++){for(int j=i+1; j<formulas.size(); j++){tmp = (HashSet<Integer>) formulas.get(i).getSelect().clone();tmp.retainAll(formulas.get(j).getSelect());if(formulas.get(i).getLeft().equals(formulas.get(j).getLeft())&& tmp.size()!=0 && !(formulas.get(i).getRight().get(0).equals("@")&&formulas.get(j).getRight().get(0).equals("@")))//可能产生相同的空产生式{System.out.println("not LL1:"+i+"\t"+j);return false;}}}return true;}

有了上面这些做基础,就有了主要的程序来调用他们~得让他们有用武之地嘛~我写了好多测试函数,来测试文法,follow集,first集,select集,预测分析表等等,测试代码就不贴啦~主程序(main函数中)的代码如下:

public static void main(String[] args) {// TODO Auto-generated method stubinitFormulas(formulas);//System.out.println("从文件读入的初始文法如下:");//System.out.println("\n-------------------------------------------------------------------------------");//System.out.println("消除左递归的文法如下:");//System.out.println("\n-------------------------------------------------------------------------------");//System.out.println("右部无非终结符的文法如下:");//System.out.println("\n-------------------------------------------------------------------------------");//System.out.println("消除回溯的文法如下:");//removeLeftRecursion();//testFormulas();while(true){formulaChange = false;removeBack();removeLeftRecursion();substituteFormula();mergeFormulas();if(!formulaChange){break;}//不能在这判断是不是LL1文法,因为select集还没有确定}System.out.println("处理后的文法如下:");testFormulas();System.out.println("\n-------------------------------------------------------------------------------");fillSymbols();//要在文法确定之后再添加initFollows();setFollows();setSelectFromFollow();testSelect();System.out.println("预测分析表如下:");fillPredictMap();testPredictMap();//结束符号和开始符号入栈stack.push(end);stack.push(start);try {BufferedReader in = new BufferedReader(new FileReader(tokenName));String line;int i=0;while((line=in.readLine())!=null ){//输入进队列input.offer(Integer.parseInt(line));}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("推导过程如下:");deriveFromTable();}


最后遇到了好多问题,java也好久没用了。。(上次说C好久没有用不熟。。这次又说java。。请问您天天到底有木有编程啊!唉。。不吐槽了。。)我错了。。低级错误还是有的。。毕竟java还是我大学生涯的最低分啧啧。。还好谨记任大神语录——java都是指针好吗!其实不编理解还是不深刻啊。。那总结一下吧~

1、arraylist的赋值不能直接赋值,要clone或者构造函数中传参。虽然java都是指针,但是之前的函数跳出之后,内存会被回收。不用clone的话,即使是a = b;然后a.remove(0)之类的操作也把b给毁了。。本程序里b一般都是产生式。。亲~你把辛辛苦苦优化为LL1的产生式都毁了还编个毛毛呢~

2、类似这样的产生式E2->400T E2 会死循环,需要先判断,避免都是求E2的follow集而重复,如果递归时仍然是求E2的follow集则无需再求。这个其实只是判断了一层,归根到底应该还是不能用递归的,上文有说哦~看我这篇是上下文有关文法O(∩_∩)O哈哈~~开个玩笑。。

3、集合循环使用并给其他赋值,每次使用前要清空!因为每次都是调用add函数,不然后面的集合将包含之前的集合。

4、之前对求first集理解不深刻!再求select集的时候,注意first是对一串求,不是对一个非终结符求。而之前写的first算法是对某一个非终结符求first集,这个上面也说啦~!

5、终结符可能连续出栈,比如连着两个都是输入缓冲和栈顶匹配。不能用if判断,需要用While判断,并且需要用peek函数来判断。之前设置了变量保存栈顶和输入缓冲的队列头,但运行中不断变化,直接peek调用更加可靠。

6、考虑间接左递归和间接回溯!这是文法中最不容易看出来的地方,也是最最最最纠结的地方。。最终用程序实现了消除左递归。

7、程序修改后注意调用顺序,比如编程实现左递归之后,主程序还是先调用setSelect了,则不是对消除左递归之后的文法设置Select集,故分析出错。还有终结符,非终结符的集合填写也要在文法确定之后哦~改动太多也要注意函数的调用顺序呀~

8、左递归右部是空,不能当做β,要去掉。因为程序中用“@”表示空,也是字符串,判断时终结符是数字,而空面临当做非终结符处理的可能,需额外考虑。

9、对集合遍历时迭代器要归位啊,否则只能对第一次的遍历。

10、正则表达式没有检测-1,结束符也要是正数,设为100(种别码中没有,规定即可)附上正则表达式判断数字的函数吧~

/** * 判断字符串是否是数字 * @param str * @return */public static boolean isNumeric(String str){    Pattern pattern = Pattern.compile("[0-9]*");    Matcher isNum = pattern.matcher(str);   if( !isNum.matches() ){       return false;    }    return true; }

11、对于结束符号的理解没有到位。刚开始写文法按照报告的要求分开写的,这样非常清晰。先不说左递归和回溯问题,关于结束符号理解为表达式的是“;”,函数的是“}”这样,所以程序只能分析单独的一类文法。而最终分析的程序又好多语句构成,需要把上面单独的文法合为整体,所以对token串的最后增加自己设定的结束符号(当做终结符,本程序中设为100)。而语句的右部需要增加“;”,其他表达式,函数调用等各种语句后面就不需要“;”了。

12、StringTokenizer为“->”时读取出错,读不到非终结符最右端的“>”,改为“#”即可。

13、开始符号也可能出现死循环,理由同问题2.

14、文法中可以直接考虑优先级的,虽然我最终还是木有考虑。。比如课件中的例子:


15、如果全是左递归,要添加E->E'的式子,否则出现永远不会在左部出现的非终结符,那推到那里就悲剧了哦~之前算法中没有考虑到。

16、正则表达式没有检测-1,结束符刚开始设为-1了额。。


最后梳理一下整个流程,然后输出课件上的例子的结果,不然结果太多了不容易看,尤其是种别码我自己规定的,数字意义不够明确额。。

主图:



流程图:


运行结果:


附上我写的C语言的文法,实在是简单的可以额。。。

PROGRAM#STATSSTATS#@STATS#STAT STATSSTAT#ARITHME 405STAT#FUNC_CALLSTAT#FUNC_DEFINESTAT#LOOPSTAT#IF_ELSESTAT#RETURNSTAT#ASSIGNSTAT#DECLARESSTAT#SELF_CALC 405STAT#405SELF_CALC#300 421SELF_CALC#300 422SELF_CALC#421 300SELF_CALC#422 300FUNC_DEFINE#TYPE P 300 410 PARAMS_DEFINE 409 407 STATS 408TYPE#288TYPE#273TYPE#260TYPE#269TYPE#265TYPE#277TYPE#274P#402P#@PARAMS_DEFINE#TYPE P 300 ARRAY PARAMS_DEFINE_TAILPARAMS_DEFINE#@PARAMS_DEFINE_TAIL#406 TYPE P 300 ARRAY PARAMS_DEFINE_TAILPARAMS_DEFINE_TAIL#@RETURN#276 VALUE 405RETURN#276 ARITHME 405FUNC_CALL#300 410 PARAMS 409 405PARAMS#VALUE PARAMS_TAILPARAMS#@PARAMS_TAIL#406 VALUE PARAMS_TAILPARAMS_TAIL#@DECLARES#TYPE P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405DECLARE_ASSIGN#413 VALUEDECLARE_ASSIGN#@ARRAY#414 301 415 ARRAYARRAY#@DECLARE_TAIL#406 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAILDECLARE_TAIL#@ASSIGN#300 ARRAY 413 VALUE 405ASSIGN#300 413 ARITHME 405ASSIGN#300 413 FUNC_CALLVALUE#CONSTVALUE#300CONST#301CONST#302CONST#303CONST#304LOGIC#VALUE EQU VALUE LOGIC_TAILLOGIC_TAIL#@LOGIC_TAIL#431 LOGICLOGIC_TAIL#430 LOGICEQU#423EQU#429EQU#411EQU#412EQU#427EQU#428ARITHME#VALUE OP VALUE ARITHME_TAILARITHME_TAIL#@ARITHME_TAIL#OP VALUE ARITHME_TAILOP#400OP#401OP#402OP#403OP#404FOR_ASSIGN#405FOR_ASSIGN#300 413 VALUE 405LOOP#270 410 FOR_ASSIGN LOGIC 405 SELF_CALC 409 407 STATS 408LOOP#287 410 LOGIC 409 407 STATS 408LOOP#264 407 STATS 408 287 410 LOGIC 409 405IF_ELSE#272 410 LOGIC 409 407 STATS 408 ELSE_IF ELSEELSE_IF#266 272 410 LOGIC 409 407 STATS 408ELSE#266 407 STATS 408ELSE#@


下面是主程序处理之后的文法:

0 PROGRAM-->STATS 1 STATS-->@ 2 STAT-->405 3 <2>-->421 4 <2>-->422 5 SELF_CALC-->421 300 6 SELF_CALC-->422 300 7 TYPE-->288 8 TYPE-->273 9 TYPE-->260 10 TYPE-->269 11 TYPE-->265 12 TYPE-->277 13 TYPE-->274 14 P-->402 15 P-->@ 16 PARAMS_DEFINE-->@ 17 PARAMS_DEFINE_TAIL-->406 TYPE P 300 ARRAY PARAMS_DEFINE_TAIL 18 PARAMS_DEFINE_TAIL-->@ 19 FUNC_CALL-->300 410 PARAMS 409 405 20 PARAMS-->@ 21 PARAMS_TAIL-->406 VALUE PARAMS_TAIL 22 PARAMS_TAIL-->@ 23 DECLARE_ASSIGN-->413 VALUE 24 DECLARE_ASSIGN-->@ 25 ARRAY-->414 301 415 ARRAY 26 ARRAY-->@ 27 DECLARE_TAIL-->406 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 28 DECLARE_TAIL-->@ 29 <3>-->ARRAY 413 VALUE 405 30 VALUE-->300 31 CONST-->301 32 CONST-->302 33 CONST-->303 34 CONST-->304 35 LOGIC_TAIL-->@ 36 LOGIC_TAIL-->431 LOGIC 37 LOGIC_TAIL-->430 LOGIC 38 EQU-->423 39 EQU-->429 40 EQU-->411 41 EQU-->412 42 EQU-->427 43 EQU-->428 44 ARITHME_TAIL-->@ 45 OP-->400 46 OP-->401 47 OP-->402 48 OP-->403 49 OP-->404 50 FOR_ASSIGN-->405 51 FOR_ASSIGN-->300 413 VALUE 405 52 LOOP-->270 410 FOR_ASSIGN LOGIC 405 SELF_CALC 409 407 STATS 408 53 LOOP-->287 410 LOGIC 409 407 STATS 408 54 LOOP-->264 407 STATS 408 287 410 LOGIC 409 405 55 IF_ELSE-->272 410 LOGIC 409 407 STATS 408 ELSE_IF ELSE 56 ELSE_IF-->266 272 410 LOGIC 409 407 STATS 408 57 ELSE-->266 407 STATS 408 58 ELSE-->@ 59 <3>-->413 <0> 60 RETURN-->276 <1> 61 SELF_CALC-->300 <2> 62 ASSIGN-->300 <3> 63 ARITHME_TAIL-->404 VALUE ARITHME_TAIL 64 ARITHME_TAIL-->403 VALUE ARITHME_TAIL 65 ARITHME_TAIL-->402 VALUE ARITHME_TAIL 66 ARITHME_TAIL-->401 VALUE ARITHME_TAIL 67 ARITHME_TAIL-->400 VALUE ARITHME_TAIL 68 VALUE-->304 69 VALUE-->303 70 VALUE-->302 71 VALUE-->301 72 <20>-->410 PARAMS 409 405 73 DECLARES-->274 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 74 DECLARES-->277 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 75 DECLARES-->265 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 76 DECLARES-->269 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 77 DECLARES-->260 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 78 DECLARES-->273 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 79 DECLARES-->288 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 80 PARAMS-->301 PARAMS_TAIL 81 PARAMS-->302 PARAMS_TAIL 82 PARAMS-->303 PARAMS_TAIL 83 PARAMS-->304 PARAMS_TAIL 84 PARAMS-->300 PARAMS_TAIL 85 <15>-->405 86 PARAMS_DEFINE-->274 P 300 ARRAY PARAMS_DEFINE_TAIL 87 PARAMS_DEFINE-->277 P 300 ARRAY PARAMS_DEFINE_TAIL 88 PARAMS_DEFINE-->265 P 300 ARRAY PARAMS_DEFINE_TAIL 89 PARAMS_DEFINE-->269 P 300 ARRAY PARAMS_DEFINE_TAIL 90 PARAMS_DEFINE-->260 P 300 ARRAY PARAMS_DEFINE_TAIL 91 PARAMS_DEFINE-->273 P 300 ARRAY PARAMS_DEFINE_TAIL 92 PARAMS_DEFINE-->288 P 300 ARRAY PARAMS_DEFINE_TAIL 93 FUNC_DEFINE-->274 P 300 410 PARAMS_DEFINE 409 407 STATS 408 94 FUNC_DEFINE-->277 P 300 410 PARAMS_DEFINE 409 407 STATS 408 95 FUNC_DEFINE-->265 P 300 410 PARAMS_DEFINE 409 407 STATS 408 96 FUNC_DEFINE-->269 P 300 410 PARAMS_DEFINE 409 407 STATS 408 97 FUNC_DEFINE-->260 P 300 410 PARAMS_DEFINE 409 407 STATS 408 98 FUNC_DEFINE-->273 P 300 410 PARAMS_DEFINE 409 407 STATS 408 99 FUNC_DEFINE-->288 P 300 410 PARAMS_DEFINE 409 407 STATS 408 100 STAT-->422 300 405 101 STAT-->421 300 405 102 <7>-->ARRAY DECLARE_ASSIGN DECLARE_TAIL 405 103 <4>--><3> 104 STAT-->276 <1> 105 STAT-->272 410 LOGIC 409 407 STATS 408 ELSE_IF ELSE 106 STAT-->264 407 STATS 408 287 410 LOGIC 409 405 107 STAT-->287 410 LOGIC 409 407 STATS 408 108 STAT-->270 410 FOR_ASSIGN LOGIC 405 SELF_CALC 409 407 STATS 408 109 <7>-->410 PARAMS_DEFINE 409 407 STATS 408 110 <4>-->410 PARAMS 409 405 111 <12>--><4> 112 STAT-->274 P 300 <7> 113 STAT-->277 P 300 <7> 114 STAT-->265 P 300 <7> 115 STAT-->269 P 300 <7> 116 STAT-->260 P 300 <7> 117 STAT-->273 P 300 <7> 118 STAT-->288 P 300 <7> 119 <14>--><12> 120 <12>-->422 405 121 <12>-->421 405 122 ARITHME-->301 OP VALUE ARITHME_TAIL 123 ARITHME-->302 OP VALUE ARITHME_TAIL 124 ARITHME-->303 OP VALUE ARITHME_TAIL 125 ARITHME-->304 OP VALUE ARITHME_TAIL 126 ARITHME-->300 OP VALUE ARITHME_TAIL 127 LOGIC-->301 EQU VALUE LOGIC_TAIL 128 LOGIC-->302 EQU VALUE LOGIC_TAIL 129 LOGIC-->303 EQU VALUE LOGIC_TAIL 130 LOGIC-->304 EQU VALUE LOGIC_TAIL 131 LOGIC-->300 EQU VALUE LOGIC_TAIL 132 <0>-->304 OP VALUE ARITHME_TAIL 405 133 <0>-->303 OP VALUE ARITHME_TAIL 405 134 <0>-->302 OP VALUE ARITHME_TAIL 405 135 <0>-->301 OP VALUE ARITHME_TAIL 405 136 STAT-->304 OP VALUE ARITHME_TAIL 405 137 STAT-->303 OP VALUE ARITHME_TAIL 405 138 STAT-->302 OP VALUE ARITHME_TAIL 405 139 STAT-->301 OP VALUE ARITHME_TAIL 405 140 STATS-->301 OP VALUE ARITHME_TAIL 405 STATS 141 STATS-->302 OP VALUE ARITHME_TAIL 405 STATS 142 STATS-->303 OP VALUE ARITHME_TAIL 405 STATS 143 STATS-->304 OP VALUE ARITHME_TAIL 405 STATS 144 <13>--><12> STATS 145 STATS-->288 P 300 <7> STATS 146 STATS-->273 P 300 <7> STATS 147 STATS-->260 P 300 <7> STATS 148 STATS-->269 P 300 <7> STATS 149 STATS-->265 P 300 <7> STATS 150 STATS-->277 P 300 <7> STATS 151 STATS-->274 P 300 <7> STATS 152 STATS-->270 410 FOR_ASSIGN LOGIC 405 SELF_CALC 409 407 STATS 408 STATS 153 STATS-->287 410 LOGIC 409 407 STATS 408 STATS 154 STATS-->264 407 STATS 408 287 410 LOGIC 409 405 STATS 155 STATS-->272 410 LOGIC 409 407 STATS 408 ELSE_IF ELSE STATS 156 STATS-->276 <1> STATS 157 STATS-->421 300 405 STATS 158 STATS-->422 300 405 STATS 159 STATS-->405 STATS 160 STATS-->300 <13> 161 STAT-->300 <14> 162 <1>-->301 <15> 163 <1>-->302 <15> 164 <1>-->303 <15> 165 <1>-->304 <15> 166 <1>-->300 <15> 167 <0>-->300 <20> 168 <13>-->404 VALUE ARITHME_TAIL 405 STATS 169 <13>-->403 VALUE ARITHME_TAIL 405 STATS 170 <13>-->402 VALUE ARITHME_TAIL 405 STATS 171 <13>-->401 VALUE ARITHME_TAIL 405 STATS 172 <13>-->400 VALUE ARITHME_TAIL 405 STATS 173 <14>-->404 VALUE ARITHME_TAIL 405 174 <14>-->403 VALUE ARITHME_TAIL 405 175 <14>-->402 VALUE ARITHME_TAIL 405 176 <14>-->401 VALUE ARITHME_TAIL 405 177 <14>-->400 VALUE ARITHME_TAIL 405 178 <15>-->404 VALUE ARITHME_TAIL 405 179 <15>-->403 VALUE ARITHME_TAIL 405 180 <15>-->402 VALUE ARITHME_TAIL 405 181 <15>-->401 VALUE ARITHME_TAIL 405 182 <15>-->400 VALUE ARITHME_TAIL 405 183 <16>-->403 VALUE ARITHME_TAIL 405 184 <16>-->401 VALUE ARITHME_TAIL 405 185 <17>-->401 VALUE ARITHME_TAIL 405 186 <20>-->404 VALUE ARITHME_TAIL 405 187 <20>-->403 VALUE ARITHME_TAIL 405 188 <20>-->402 VALUE ARITHME_TAIL 405 189 <20>-->401 VALUE ARITHME_TAIL 405 190 <20>-->400 VALUE ARITHME_TAIL 405 



原创粉丝点击