您的位置:首页 > 其它

Windows系统“无法打开”故障解决方法之一

2007-01-10 16:19 302 查看
最近学习编译原理课程,老师布置的实验,由于实际使用的语法分析器相对复杂,老师先让我们做了一个很简单的面向数学表达式的语法分析程序,程序比较简单,和大家分享一下。接下来该写相对复杂的语法分析了,fighting!

设计目标
所设计的程序用于简单的语法分析。
接受的输入为一个数字计算式,具体要求如下: 参与运算的数字为1位正整数 算术表达式中可以使用的运算符有:+、-、*,可以使用括号强制改变运算优先级 算法的输出为一个二叉树表示的语法分析树,其中叶节点为参与运算的数字,非叶节点为运算符。

算术表达式的文法如下
exp -> term addop exp | term
addop -> + | -
term -> factor mulop term | factor
mulop -> *
factor -> (exp) | number

这个文法经过一次简单的修改,原文法中exp和term的产生式为:
exp -> exp addop term | exp
term -> term mulop factor | factor

为了便于程序实现,消除左递归,将产生式中的操作数的位置做了一个交换,这样只会影响识别的顺序,而并不影响实际的运算。

简单算法描述
本分析算法采用递归下降的方式对输入串进行解析。对识别非终结符exp、term、factor的过程定义为函数,通过这些函数的相互调用,达到递归下降语法分析的目的。注意到还有2个非终结符addop和mulop,由于它们只是简单的直接推导出终结符,因此在程序中直接对这些终结符进行识别而不再定义函数识别过程。

算法最终的目标是建立一颗由二叉树表示的语法分析树,其节点类node中包含4个数据项:num、data、lchild、rchild分别对应与节点编号、节点存储数据(运算符或数字)、左子节点指针、右子节点指针。

Node节点类如下:
class node{
public:
int num;
char data;
node* lchild;
node* rchild;
//构造函数,用于程序中使用4个数据新建节点使用
node(int node_count,char datain, node* lchildin, node* rchildin)
{
num=node_count;
data=datain;
lchild=lchildin;
rchild=rchildin;
}
};

以exp函数为例说明算法如下:
node* exp(void)//识别表达式(形如“op1+op2”或“op1-op2”,还有term型的表达式) {
node* rt_node=new node(++count,'?',NULL,NULL);
node* temp_term=new node(++count,'?',NULL,NULL);
temp_term=term();//首先调用term函数识别出一个term
if(token=='+'||token=='-')//判断term之后是否有一个运算符(+或-),有的话则 选择产生式exp -> term addop exp,否则选择exp->term
{
rt_node->data=token;//将读到的运算符写入节点
rt_node->lchild=temp_term;//将左子节点指向term子函数(term将返回一个 node类节点)
token=getchar();//读取下一个字符存入token中
rt_node->rchild=exp();//将右子节点指向exp子函数(exp也将返回一个node类 节点)
return rt_node;//将使用exp -> term addop exp得到的节点返回上层调用
}
else
return temp_term;//将使用exp->term得到的节点返回上层调用
}

每调用一次非终结符对应的分析函数,就建立一个节点,将本级运算符写入data中,并调用下一层的函数进行进一步分析,将本节点的左右子节点分别指向对应的子函数返回的node节点。

程序细节说明

产生式的选择:exp、term、factor的产生式的右部的选择是不唯一的,这需要在其中进行选择,而有些的FIRST集合交集非空,这给选择带来了困难。具体的处理中,考虑到exp的右部都是以term开头(这是进行文法修改的目的),区别在于一个在term后含有运算符addop而另一个没有,这就可以进行选择了。Term中和exp是一样的,而factor终可以直接由括号和数字的区分进行识别和选择。
Count的使用:在同一个表达式中,同一个运算符或数字出现的次数可能有多次,为了在语法分析数中对其进行区分,node节点类中加入了一个num的整型值,使用一个全局变量count记录当前的节点编号,在每次建立新的节点时使用++count对新的节点进行编号,这样通过编号就可以唯一确定一个符号。
分析树的输出:树结构是非线性的结构,其输出是个比较复杂的问题,本程序中没有直观的图形化输出所生成的语法分析而是采用遍历的方式输出。由先序遍历和中序遍历两个节点序列可以唯一的确定一颗二叉树,因此采用这种方式来输出语法分析数。
Match函数:match是一个简单的匹配识别字符的函数,它主要的作用是在程序中消耗不会被存入树中
的括号,并进行匹配识别,在匹配失败时报错。
Initial:依照文法的定义,输入的表达式是一个exp的表达式,因此在main函数中调用一次exp即可对整个输入的表达式完成分析操作,将其返回之保存在语法分析数的根节点root中。

文件说明
Parser.h包含:
全局变量声明
Node节点类的定义
函数原型声明

Parser.cpp包含:
Main函数
功能函数定义

功能验证示例
说明: 输入的算术表达式为:1+2*3-5*(6-2)
分析结果显示为对生成的语法树的先序遍历和中序遍历的序列,其中第一列显示的是为了区分节点所使用的节点编号,第二列显示的是节点所存储的数据(运算符或数字)。 依照给出的两个序列,唯一确定的二叉树如下:
这个二叉树与所输入的算术表达式所对应的计算语法是一致的。

功能验证示例





说明: 输入的算术表达式为:1+2*3-5*(6-2)
分析结果显示为对生成的语法树的先序遍历和中序遍历的序列,其中第一列显示的是为了区分节点所使用的节点编号,第二列显示的是节点所存储的数据(运算符或数字)。 依照给出的两个序列,唯一确定的二叉树如下:



这个二叉树与所输入的算术表达式所对应的计算语法是一致的。
本文出自 “INDI Technology” 博客,转载请与作者联系!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: