NC编译器结题以及软件重用性的思索

来源:互联网 发布:手机在linux查arp 编辑:程序博客网 时间:2024/06/11 09:29

经过3月29日到今天一共17天的紧张学习赶工,NC编译器的编写环节暂时告一段落。从开始的简单制导翻译到后来的词法语法分析,虽然其中还有很多细节没有学到位,但在与无数BUG的斗争中大体上已经领略到了一个编译前端的结构。这里做一下总结:
输入一段代码、一些规则,按规则输出代码中的信息,这就是一个编译器的工作。看似简单,但如果规则较复杂,不进行理论有序的描述就很难得到正确的结果。因此,把编译过程划分为几个部分,其中最前端的部分是词法分析,它读入源代码的字符串,根据语言的字符集提取其中的词法单元;其次是语法分析部分,语法分析读入词法分析返回的词法单元,根据语法规则对词法单元序列进行处理;语法分析之后是对语义的分析,语义即一段代码的含义,可以通过制导翻译的方法将代码或者属性附到语法分析产生的语法树的内部结点中,从而进行计算得到结果。

之前的代码主要写到了语法分析部分,语义的部分很难写出来,这是因为词法、语法部分我都想做成可重用的,即可以根据配置文件的词法、语法规则改写翻译不同的代码,但是语义部分却很难同样地实现。如果使用配置文件来描述语义,则相当于我还需要一个语言的编译器来编译语义的文件,因此我将词法、语法部分封起来,当做一个库使用,而语义部分则交给用户(使用库的人)来完成。
因此程序的最后部分是这样完成的:
语义动作以及语义单元:

//语义动作struct smt_action{    function<string(int,vector<string>&)> func;//第一个参数为选择哪个产生式 第二个是参数表        //其他参数?};struct smt_unit{public:    static map<production*,smt_action*>table;    static void init();private:    production*pd;//产生式指针    int cho;//选择哪一个产生式    int cur;//当前计算完成数    string result;//计算结果    bool set_func;    function <string(int,vector<string>&)> func;//动作public:    vector<string>value;//完成的值    void calculate(syn_unit*);//求值 传入语法单元    void calculate(smt_unit*);//求值 传入其他语义单元的结果    bool is_calcu_over;    void output();//输出值    smt_unit(production*,int);};

编译器基类:

//分析器基类 需要添加函数到各语义动作中class parser{    lex_parser*lex;    nc_parser*grammar;    string codefile;    smt_action basic_smt;public:    parser(string code, string gram = "NC\\grammar.txt", string tk = "NC\\token.txt")        :codefile(code){        lex = new lex_parser(tk);        lex->init(code);        grammar = new nc_parser(gram, lex);        grammar->initial();    }    void run(){        grammar->translate();    }    //向函数表中添加语义动作函数,name为产生式的头名    void addfunc(string name, function<string(int, vector<string>)> func){        auto p = grammar->get_production(name);        if (p){            basic_smt.func = func;            smt_unit::table[p] = new smt_action(basic_smt);        }    }};

这里实现重用的方法是,要求用户继承parser类,以添加自己的初始化部分,并且通过给定的宏来添加语义函数,对其产生式进行语义输出。语义动作函数有严格的格式(这也是这个程序的局限性之一),必须是指定的类型。
用户使用示例如下:

//使用者通过继承parser类实现语义定义class Cusr_parser :public parser{private:    int addrs_num;//地址数    stack<int> stmt_num_stk;//各条指令的地址数    bool addrs_count;    stack<addrs_word>stk_instruct;//地址字栈 存储每个当前语句下的指令    stack<statement> stk_stmt;//语句栈    vector<statement> table_stmt;public:    Cusr_parser(string s1,string s2,string s3):parser(s1,s2,s3){        addrs_count = false;        addrs_num = 0;    }    //地址的语义动作    string addrs(int i, vector<string>&args){        if (i > 7)return "error";        if (addrs_count == false){            addrs_num = 0;            addrs_count = true;        }        string str = "匹配addrs" + args[0];        output(str);        addrs_num++;        return args[0];    }    //instruct的语义动作    string instruct(int i, vector<string>&args){        if (i == 1)return "";        if (addrs_count==true){            stmt_num_stk.push(addrs_num);            addrs_count = false;        }        stk_instruct.push(addrs_word(args[0], args[1]));        string str = "匹配instruct" + args[0];        output(str);        return str;    }    //stmt的语义动作    string stmt(int i, vector<string>&args){        if (i == 1)return "";        statement t;        string str = "匹配statement" + args[0];        output(str);        int count = stmt_num_stk.top();        stmt_num_stk.pop();        while (count--){            t.inst.push_back(stk_instruct.top());            stk_instruct.pop();        }        stk_stmt.push(t);        return str;    }    void init(){        ADD_SEM_FUNC(Cusr_parser, addrs);        ADD_SEM_FUNC(Cusr_parser, stmt);        ADD_SEM_FUNC(Cusr_parser, instruct);    }    void show(){        while(!stk_stmt.empty()){            table_stmt.push_back(stk_stmt.top());            stk_stmt.pop();        }        for (auto&t : table_stmt){            t.show();        }    }};

宏定义如下,用于实现对象与函数的绑定:

#define ADD_SEM_FUNC(clsname,name) addfunc(#name,bind(&##clsname##::##name,this,_1,_2))

在运行完之后,stk_stmt语句栈中将存储着指令的数据,用户可以根据自己的需求翻译为目标代码或者直接运行。运行结果如下:
这里写图片描述

我个人对于可重用的软件有一种谜之执着,不管写什么程序,都会考虑其重用性,写完只能跑一次的代码我一般是不会写的。但对可重用代码质量的考察也将是很严格甚至于苛刻的,毕竟要拿去无数次的用。反面教材比如我这次的程序,其效率完全不适合拿来通用(多半是要赶工毕设的锅),但其中所体现的重用性思想我觉得值得思考。比如配置文件的方法、利用继承和宏等等。而且,有通用也必然会带来一些约束,比如之前语义动作的格式,这不仅对效率有一定的影响,对使用者也带来一定的不便。要更优化地实现重用,还需一定的磨练。

在《形式语义学基础与形式说明》的前言中读到,如今的信息大草原已经不是当初的那片荒原,经过几十年的发展已经充满了各种软件资源,我们面临的是更加面向主体的程序设计和行为开发方法。因此,如何更准确地描述需求、行为将是一个重要的课题。

之前的文章链接:
制导翻译概述:http://blog.csdn.net/u011602557/article/details/68111472
简单翻译程序实现:http://blog.csdn.net/u011602557/article/details/68943237
词法分析:http://blog.csdn.net/u011602557/article/details/69763334
语法分析(预测分析法):http://blog.csdn.net/u011602557/article/details/69938255 ; http://blog.csdn.net/u011602557/article/details/70064284
语法制导定义:http://blog.csdn.net/u011602557/article/details/70147540

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 家里有小飞虫怎么办呀 汽车里进老鼠了怎么办 老鼠跑到车里了怎么办 我的小车有老鼠怎么办 车里面进了老鼠怎么办 街电充电宝丢了怎么办 充电宝充不了电怎么办 脚裸扭伤肿了怎么办 大货车电瓶亏电怎么办 货车电瓶被偷了怎么办 小乌龟尾巴断了怎么办 长青春痘怎么办简单小妙招 一关灯就有蚊子怎么办 狗狗误食蟑螂药怎么办 泰迪误食蟑螂药怎么办 猫咪吃了蟑螂药怎么办 狗把蟑螂药吃了怎么办 猫吃了蟑螂诱饵怎么办 猫吃了蟑螂中毒怎么办 孕妇胃烧的难受怎么办 6个月孕妇胃难受怎么办 飞机杯吸盘不上怎么办 我的车位被占用怎么办 占别人车位的车怎么办 头受凉受风了疼怎么办 看电脑时间长了眼睛疼怎么办 电脑玩久了头疼怎么办 屋里有死老鼠味怎么办 老鼠死在车里面怎么办 手机锁屏怎么办求解锁 玩lol左键失灵怎么办 小米鼠标没电了怎么办 电脑键盘鼠标没反应怎么办 win10玩游戏很卡怎么办 鞋子夹脚怎么办小妙招 新鞋两边夹脚怎么办 新鞋子两边夹脚怎么办 鞋子瘦两边夹脚怎么办 鞋子小脚趾磨脚怎么办 鞋子买小了顶脚怎么办 鼠标不亮了怎么办呢