首页 > 编程知识 正文

编译原理语法树短语,编译原理构造语法树

时间:2023-05-04 08:53:00 阅读:224862 作者:3178

前言

三千行代码,写了个寂寞。
做了这么几个项目,一直都在操作语法树,我也不知道怎样才写的好,之前的任务都完成了是因为复杂的我都跳过了(汗)多亏了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<SuatinErrorType_NoDefined>("[Id] string connect expression wrong");return;} //字符串拼接模式,不处理非Str类型的idif (SuatinEnv[global_infix[_t]->name]->type != SuatinIDType_string) {return;}SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>(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<LLeftExpr*>(expTmp);node_tmp->SetContent(new IDExpr(global_infix[_t]->name));}else {SymbolExpr* node_tmp = dynamic_cast<SymbolExpr*>(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个类,类多不多到是无所谓,只是代码是真的乱!!!

Parser类

这次多加了二十多个函数,用来处理关键字!在进行词法分析的时候,关键字和变量我都当成是标识符,在遍历中缀表达式时,遇到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是否是flagstd::map<SuatinTokenType, DealFuncPtr> funcMap;//为了减少if-else的个数,使用查表的方法,根据不同的枚举来调用不同的函数std::map<SuatinKeyWordType, DealFuncPtr> k_funcMap;//处理keyword的函数表ExprType exprType = ExprType_NumberCalculate;//表达式类型,默认是数字运算模式,只有第一个Token是字符串时会变为字符串拼接模式bool completedName_flag = false;//是否完成左边的变量的声明 /*root不为空时语句是赋值语句,exprRoot不为空时是表达式,root和exprRoot在构造完语法树后不能同时为空也不能同时不为空*///与exprRoot进行交互Expr* root = NULL;//语法树根节点/*logicRoot不为空时语句和exprRoot不为空时一样,都是表达式!!!*///与judgeRoot进行交互,最后用exprRoot去替换掉logicRootExpr* logicRoot = NULL;//逻辑式语法树根节点/*judgeRoot不为空时语句和exprRoot不为空时语句一样,都是表达式!!!*///judgeRoot与exprRoot进行交互,最后用exprRoot去替换掉judgeRootExpr* judgeRoot = NULL;//判断式语法树根节点//普通表达式要构造语法树用得到的指针Expr* exprRoot = NULL;//表达式语法树根节点Expr* expTmp = NULL;//待处理的节点(为空)的父节点Expr* expTmpLeft = NULL;//最下层的没匹配到右括号的左括号节点std::stack<Expr*> v_NoMatchedLLeft;//保存所有的未匹配到右括号的左括号节点};};#endif 目前构造语法树的逻辑 一条语句,可以不是赋值语句,即root为空赋值语句后面可以跟着赋值语句,最后面的语句是表达式一条语句以分号结尾表达式有4种模式,分别是五则运算、字符串拼接、判断运算、逻辑运算几乎每一个Token都要区分一下几种表达式模式表达式模式除了有特定的标志外,与exprRoot、judgeRoot、logicRoot是否为空有关每次遇到一个等于号,都要把这个等于号挂载在root的语法树中。赋值语法树除了第一个赋值是左结合的外,后面跟着的其他赋值都是右结合的,即每假如一个赋值就要查找root语法树最右的赋值节点,并把新树挂在它右孩子上。赋值节点的左边一定是变量节点赋值节点最后一个孩子,在出现分号后,将exprRoot(简单表达式)挂在root语法树中没有逻辑运算的式子里只能有一个判断节点,6种判断节点优先级一样当出现判断节点后,将exprRoot挂在判断节点左孩子上,当出现逻辑节点或分号时,才把新的exprRoot挂在此判断节点的有孩子上判断模式的句子遇到分号后,会先装载好judeRoot语法树,然后用exprRoot替换掉judgeRoot,这样的话判断模式就不会和赋值语句有直接交互!!!当出现逻辑节点后,将exprRoot挂在逻辑节点左孩子上,当出现逻辑节点或分号时,先执行第11条,才把新的exprRoot挂在此逻辑节点的有孩子上逻辑模式的句子遇到分号后,先执行第12条,再装载好logicRoot语法树,然后用exprRoot替换掉logicRoot,这样的话逻辑模式就不会和赋值语句有直接交互!!!无论是判断模式还是逻辑模式,除了表达式可以有括号外,其他位置不能有括号!逻辑模式中,not只能有一个,必须是表达式开头第一个逻辑模式下,or优先级比and低 //下面的语句都是合法的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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。