本文共 7623 字,大约阅读时间需要 25 分钟。
三千行代码,写了个寂寞。
做了这么几个项目,一直都在操作语法树,我也不知道怎样才写的好,之前的任务都完成了是因为复杂的我都跳过了(汗)多亏了VS的调试很强大,懂得使用SHIFT+F9的才能熟练掌握C++内存管理(误)。之前的判断运算我取了巧,让判断式只能有一个判断符,一般是没问题的,但是引入逻辑运算就有问题了!一个表达式(无赋值)可以出现多个判断符、多个逻辑符、五则运算、字符串、判断符和逻辑符混在一起这些情况都要考虑清楚——现在还没到要转方向的时候,一点点摸索着!!!
之前都字符串拼接模式,是在completedName_flag为真,即定义了变量之后,碰见的第一个Token是Str时设置的。并且字符串拼接模式下,不能除了加号和字符串本身,不能有其他符号在字符串拼接的表达式中!
改进之后,1.允许定义变量后的第一个Token是Id,当此Id是字符串类型时,设置表达式为字符串拼接模式。
2.字符串拼接模式下的表达式可以有类型为字符串的Id存在!void Parser::DealToken_Id(int& _t) { if (exprType == ExprType_StringConnect) { /*即出现了这种情况:"xxx"+a 其中a是字符串类型 + -> + / \ / \ "xxx" None "xxx" a */ if (expTmp == NULL) { //不存在待处理的节点 ThrowException("[Id] string connect expression wrong"); return; } //字符串拼接模式,不处理非Str类型的id if (SuatinEnv[global_infix[_t]->name]->type != SuatinIDType_string) { return; } SymbolExpr* node_tmp = dynamic_cast (expTmp); node_tmp->SetRight(new IDExpr(global_infix[_t]->name)); expTmp = NULL; return; } //处理关键字 if (global_infix[_t]->k_type != SuatinKeyWordType_NOT_KEYWORD) { (this->*k_funcMap[global_infix[_t]->k_type])(_t); return; } //存在待处理的节点 if (expTmp) { if (typeid(*(expTmp->GetClassType())) == typeid(LLeftExpr)) { //待处理节点是左括号节点 LLeftExpr* node_tmp = dynamic_cast (expTmp); node_tmp->SetContent(new IDExpr(global_infix[_t]->name)); } else { SymbolExpr* node_tmp = dynamic_cast (expTmp); node_tmp->SetRight(new IDExpr(global_infix[_t]->name)); expTmp = expTmpLeft;//上移待处理节点的指针 } return; } //不存在待处理的节点 exprRoot = new IDExpr(global_infix[_t]->name); //检查一下,表达式的开头第一个id的类型,如果是字符串类型就转变表达式类型为字符串拼接模式 if (firstId_is_string_flag) { if (SuatinEnv[global_infix[_t]->name]->type == SuatinIDType_string) { exprType = ExprType_StringConnect; firstId_is_string_flag = false; } } }
目前24个类,类多不多到是无所谓,只是代码是真的乱!!!
这次多加了二十多个函数,用来处理关键字!在进行词法分析的时候,关键字和变量我都当成是标识符,在遍历中缀表达式时,遇到Id会进入DealToken_Id函数,在其中判断是否是关键字并进入对应函数!
#pragma once#ifndef _PARSER_H_#define _PARSER_H_#include"Expr.h"namespace sua { //成员函数指针,如果不加上Parser::修饰,就只能把对应的成员函数都改成静态的!!! class Parser; typedef void(Parser::*DealFuncPtr)(int&); //语法解析器 class Parser { private: //处理Token的函数 void DealToken_Num(int& _t); void DealToken_Id(int& _t); void DealToken_Str(int& _t); void DealToken_Pow(int& _t); void DealToken_Mul(int& _t); void DealToken_Div(int& _t); void DealToken_Add(int& _t); void DealToken_Sub(int& _t); void DealToken_Gre(int& _t); void DealToken_GreEq(int& _t); void DealToken_Les(int& _t); void DealToken_LesEq(int& _t); void DealToken_Neq(int& _t); void DealToken_EqEq(int& _t); void DealToken_Eq(int& _t); void DealToken_Com(int& _t); void DealToken_ML(int& _t); void DealToken_MR(int& _t); void DealToken_LL(int& _t); void DealToken_LR(int& _t); void DealToken_BL(int& _t); void DealToken_BR(int& _t); void DealToken_Dot(int& _t); void DealToken_Sem(int& _t); //void DealToken_Eol(int& _t); //处理关键字的函数 void Deal_k_if(int& _t); void Deal_k_elif(int& _t); void Deal_k_else(int& _t); void Deal_k_for(int& _t); void Deal_k_break(int& _t); void Deal_k_continue(int& _t); void Deal_k_do(int& _t); void Deal_k_until(int& _t); void Deal_k_while(int& _t); void Deal_k_local(int& _t); void Deal_k_const(int& _t); void Deal_k_and(int& _t); void Deal_k_or(int& _t); void Deal_k_not(int& _t); void Deal_k_function(int& _t); void Deal_k_end(int& _t); void Deal_k_return(int& _t); //创建语法树 void SetupASTree(int start, int end);//实际的创建函数 //打印漂亮的二叉树 void DisplayASTree(Expr* _node, int _num = 0);//实际打印的函数 //删除二叉树 void DelTree(Expr* _expr); public: Parser(); ~Parser(); //创建语法树 void CreateASTree();//留给外面的接口 //打印漂亮的语法树 void ShowASTree();//留给外面的接口 //解释语法树 void interpret(); //是否已经构造完成 bool GetCompletedASTreeFlag()const; private: bool completedASTree_flag = false;//是否已经完成语法树的构造 int v_forDisplayASTree[100] = { 0 };//打印语法树要用的工具 bool firstEq_flag = true;//是否第一次遇到等于号 bool firstId_is_string_flag = true; //表达式的第一个Id是否是flag std::mapfuncMap;//为了减少if-else的个数,使用查表的方法,根据不同的枚举来调用不同的函数 std::map k_funcMap;//处理keyword的函数表 ExprType exprType = ExprType_NumberCalculate;//表达式类型,默认是数字运算模式,只有第一个Token是字符串时会变为字符串拼接模式 bool completedName_flag = false;//是否完成左边的变量的声明 /*root不为空时语句是赋值语句,exprRoot不为空时是表达式,root和exprRoot在构造完语法树后不能同时为空也不能同时不为空*/ //与exprRoot进行交互 Expr* root = NULL; //语法树根节点 /*logicRoot不为空时语句和exprRoot不为空时一样,都是表达式!!!*/ //与judgeRoot进行交互,最后用exprRoot去替换掉logicRoot Expr* logicRoot = NULL; //逻辑式语法树根节点 /*judgeRoot不为空时语句和exprRoot不为空时语句一样,都是表达式!!! */ //judgeRoot与exprRoot进行交互,最后用exprRoot去替换掉judgeRoot Expr* judgeRoot = NULL; //判断式语法树根节点 //普通表达式要构造语法树用得到的指针 Expr* exprRoot = NULL; //表达式语法树根节点 Expr* expTmp = NULL; //待处理的节点(为空)的父节点 Expr* expTmpLeft = NULL; //最下层的没匹配到右括号的左括号节点 std::stack v_NoMatchedLLeft; //保存所有的未匹配到右括号的左括号节点 };};#endif
//下面的语句都是合法的1 + 2 * (3 / 4) ^5 - 100;a = TEST_C + "AAAAA" +"SSSSS";//TEST_C是预定义变量,用了测试的,前一篇有提到 //返回author@DemllieAAAAASSSSSa = b = c = 2;a = b = 3 + 1 > 5 * 8 ;//返回falsea = 1 >2 and 2 ~= 3 or 4<5 and 5 ==6;//返回falsea = not 1 > 3;//返回truenot 1~=2 and 3~= 4;//返回false
文件中内容: a = not 1 < 2 + 1 and 4 == 5 or 2~=1;
词法分析 time consumed 211 ms初始化语言环境 time consumed 1 ms语法分析·构造语法树 time consumed 1 ms------------------------------------------------------------=├── a└── not └── or ├── and │ ├── < │ │ ├── 1 │ │ └── + │ │ ├── 2 │ │ └── 1 │ └── == │ ├── 4 │ └── 5 └── ~= ├── 2 └── 1------------------------------------------------------------语法分析·显示语法树 time consumed 39 msresult = false语法分析·解释语法树 time consumed 1 ms语言环境> name isconst type funcPtr flag num str NIL true nil 00000000 false 0 TEST_C true int 00000000 true 7 FALSE true bool 00000000 false 0 TEST_D true bool 00000000 false 0 TRUE true bool 00000000 true 0 a false bool 00000000 false 0 TEST_B true number 00000000 true 1.22323e+06 TEST_A true string 00000000 true 0 author@Demllie显示环境信息 time consumed 650 ms
项目代码地址CSDN
http://download.csdn.net/download/weixin_41374099/12203591 项目代码地址BDWP 链接:https://pan.baidu.com/s/1S4e1KvZ5uv8XBAkwUvklEA 提取码:i72f转载地址:http://qtngz.baihongyu.com/