编译原理语法分析的实验已经是上上周的事了,可是今天才得以更新博客,其原因必然是一直木有做完,唉想想就伤感。。我这两周的大部分青春都献给了编译原理,虽然花了好长时间,可说实话写这篇博文心里还是十分没底,因为到最后还是没有一个令自己满意的结果,特别愧疚,实验报告交的都是最基础的课件上的例子。但是已经拖了两周了,第三次实验又浩浩荡荡的袭来,唉水平有限,力不从心,老了啊。。没办法分数还得说的过去,昨天找老师检查了一下,先做到这个程度吧,后面需要学习的地方还有很多,可能能顿悟呢也说不定哦~有时间再回头修改吧~
上次实验用的是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;
}
/**
* 非递归求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不能修改原来的right
for(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);
}
}
}
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 stub
initFormulas(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 block
e.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#STATS
STATS#@
STATS#STAT STATS
STAT#ARITHME 405
STAT#FUNC_CALL
STAT#FUNC_DEFINE
STAT#LOOP
STAT#IF_ELSE
STAT#RETURN
STAT#ASSIGN
STAT#DECLARES
STAT#SELF_CALC 405
STAT#405
SELF_CALC#300 421
SELF_CALC#300 422
SELF_CALC#421 300
SELF_CALC#422 300
FUNC_DEFINE#TYPE P 300 410 PARAMS_DEFINE 409 407 STATS 408
TYPE#288
TYPE#273
TYPE#260
TYPE#269
TYPE#265
TYPE#277
TYPE#274
P#402
P#@
PARAMS_DEFINE#TYPE P 300 ARRAY PARAMS_DEFINE_TAIL
PARAMS_DEFINE#@
PARAMS_DEFINE_TAIL#406 TYPE P 300 ARRAY PARAMS_DEFINE_TAIL
PARAMS_DEFINE_TAIL#@
RETURN#276 VALUE 405
RETURN#276 ARITHME 405
FUNC_CALL#300 410 PARAMS 409 405
PARAMS#VALUE PARAMS_TAIL
PARAMS#@
PARAMS_TAIL#406 VALUE PARAMS_TAIL
PARAMS_TAIL#@
DECLARES#TYPE P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL 405
DECLARE_ASSIGN#413 VALUE
DECLARE_ASSIGN#@
ARRAY#414 301 415 ARRAY
ARRAY#@
DECLARE_TAIL#406 P 300 ARRAY DECLARE_ASSIGN DECLARE_TAIL
DECLARE_TAIL#@
ASSIGN#300 ARRAY 413 VALUE 405
ASSIGN#300 413 ARITHME 405
ASSIGN#300 413 FUNC_CALL
VALUE#CONST
VALUE#300
CONST#301
CONST#302
CONST#303
CONST#304
LOGIC#VALUE EQU VALUE LOGIC_TAIL
LOGIC_TAIL#@
LOGIC_TAIL#431 LOGIC
LOGIC_TAIL#430 LOGIC
EQU#423
EQU#429
EQU#411
EQU#412
EQU#427
EQU#428
ARITHME#VALUE OP VALUE ARITHME_TAIL
ARITHME_TAIL#@
ARITHME_TAIL#OP VALUE ARITHME_TAIL
OP#400
OP#401
OP#402
OP#403
OP#404
FOR_ASSIGN#405
FOR_ASSIGN#300 413 VALUE 405
LOOP#270 410 FOR_ASSIGN LOGIC 405 SELF_CALC 409 407 STATS 408
LOOP#287 410 LOGIC 409 407 STATS 408
LOOP#264 407 STATS 408 287 410 LOGIC 409 405
IF_ELSE#272 410 LOGIC 409 407 STATS 408 ELSE_IF ELSE
ELSE_IF#266 272 410 LOGIC 409 407 STATS 408
ELSE#266 407 STATS 408
ELSE#@
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