对表达式进行词法和语法分析

    最近在看编译原理,涉及到了词法分析和语法分析等阶段,故此自己尝试着用C++实现了对表达式形如:expression=x1-x2*c的词法分析和语法分析,预想的运行结果为:
符号表:                               |  分析树:
 expression       标识符         |                     =
 =                    操作符         | expression              -
 x1                   标识符         |                       x1              *
 -                     操作符         |                                  x2           c
 x2                   标识符         |
 *                     操作符         |
 c                     标识符          

思路:大体遵循着编译原理中提到的词法和语法分析步骤,即先对表达式进行区分为操作符或标志符,将其存放在符号表中,而后从符号表中识别符号类别和操作符优先级存放在二叉树中。

自定义类型:
typedef int ExpType;    //符号表中类别标志:1为标识符,2为操作符
typedef string ElemType;   //表达式中的符号类型

词法分析中符号表的构造如下:

typedef struct   wordsTable                     //词法分析中存储数据--符号表
{
        ElemType  w;                          //符号
        ExpType    t;                       //类型
        struct   wordsTable   *next;  
        };

涉及到的操作有:
int wordsTable_Inital(wordsTable* ws){ };   //符号表的初始化
bool Add_Table_at_end(wordsTable* w,wordsTable* node_to_add){ };//在符号表尾部插入节点
int PrintTable(wordsTable* ws){ };                     //显示符号表
int DivisionExp(ElemType arrary[Size],string Exp){};  //识别表达式,将其存放在字符串数组中
int InsertSignTable(ElemType arrary[Size],int N,wordsTable* ws){ };//从字符串数组中读取数据并插入符号表中

语法分析中二叉树的构造如下:

typedef struct ParsingTreeNode                   //语法分析中存储数据--语法树
{                
       ElemType Oper;                                  //符号表中各符号在分析树中的表示形式
       struct ParsingTreeNode *left;               //左节点
       struct ParsingTreeNode *right;             //右节点
 struct ParsingTreeNode *father;                 //父节点
       };

涉及到的操作有:
//初始化分析树 
      int ParsingTree_Inital(ParsingTreeNode* root) {};        
//中排序分析树显示 (丛网上找到的竖向的树状排序算法)
      void PrintTree(ParsingTreeNode* Root,int Layer){ };     
//显示分析树 
      int PrintParsing(ParsingTreeNode* ROOT) {};            
//查找出分析树中右节点为空的操作符节点,用于就标志符的插入操作 
      ParsingTreeNode* Tree_FindOper(ParsingTreeNode* Root){};
//查找出分析树中自根节点最右的操作符节点,用于操作符的插入操作
      ParsingTreeNode* Tree_FindOper1(ParsingTreeNode* Root){};
//查找出分析树中自节点Op的父节点始操作符优先级等于NewP的节点
      ParsingTreeNode* Tree_FindOper2(ParsingTreeNode* Op,ParsingTreeNode* NewP){};
//将新节点进行分析并插入分析树中 
      int Insert_ParsingTree(ParsingTreeNode* ROOT,ParsingTreeNode* NewP,ExpType T){};
 主函数如下:

int main()
{
    int i;
    wordsTable* ws=new wordsTable;
    wordsTable* q;
    ParsingTreeNode* root=new ParsingTreeNode;
    string a;
    string arrary[Size];
    int N;
    wordsTable_Inital(ws);
    cout<<"请输入表达式\n"<<endl;
    cin>>a;
    getchar();
    N=DivisionExp(arrary,a);     //识别表达式,存放在字符串数组中
    InsertSignTable(arrary,N,ws);//从字符串中将数据插入符号表中
    PrintTable(ws);              //显示符号表
    ParsingTree_Inital(root);    //分析树初始化
    q=ws->next;
     while(q)
    {
            ParsingTreeNode* NewP=new ParsingTreeNode;
            NewP->Oper=q->w;
            NewP->left=NULL;
            NewP->right=NULL;
            NewP->father=NULL;
            Insert_ParsingTree(root,NewP,q->t);
            q=q->next;
             }
    PrintParsing(root);          //分析树打印
    getchar();
   return 0;
    }

运行结果为:

分析:
整个过程中,主要在树的操作上(生疏的缘故)以及如何将符号表中的数据存放在二叉树的花费了不少时间。在此,对二叉树的构造思路进行分析。
假设 1、二叉树以一个空的头节点Root标识,将符号表中的数据以二叉树根节点的右节点开始插入,故实际操作中根节点应为Root的右节点
假设 2、标识符的左右节点为NULL,只有操作符的左右节点分别对应着该操作符相应的操作数
假设 3、不考虑括号类的操作符标志
首先根据符号表中数据创建一个新节点,然后对符号的类型进行判断
A、当为标识符时:判断分析树是否为空
       a、空:直接将该新节点作为根节点
       b、非空:查找出没有右节点的操作符节点,并将新节点作为操作符节点的右节点        
B、当为操作符时:判断分析树中是否存在操作符节点(即左节点是否为空)
       a、不存在:将根节点作为新节点的左节点,并将新节点设为根节点
       b、存在:在当前分析树中自根节点依次查找其为操作符的最右节点,并判断新节点和已存在最右节点的操作符优先级:
              1、新节点优先级大于:将当前最右节点的右节点(操作数)作为新节点的左节点,并将新节点替换成最右节点的右节点
              2、新节点优先级等于:将当前的最右节点作为新节点的左节点,将新节点取代最右节点的位置,即将新节点作为最右节点的父节点的右节点
              3、新节点优先级小于:自当前最右节点的父节点始查找优先级等于新节点的节点,然后以新找到的节点和新节点做替换,即按照2操作
总结:
重新对树有了认识,感觉挺不错的。还存在很多的不足之处:
1、没有对表达式的输入进行有效性控制
2、没有考虑括号等复杂的操作
3、没有进行报错控制
4、没有正确的显示树状,只是暂时的从网上搜了竖向的显示


附:源码下载

 

 

转载于:https://www.cnblogs.com/Dana-Jing/archive/2009/06/24/1510126.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值