用C++从0开始开发自己的编程语言

您所在的位置:网站首页 计算机程序是用某种编程语言编写 用C++从0开始开发自己的编程语言

用C++从0开始开发自己的编程语言

2024-07-04 01:52| 来源: 网络整理| 查看: 265

目录 导语取名记使用语言项目链接 项目架构项目架构图项目文件结构解释器流程 着手实现接口文件编写语言内存实现

导语

这是我第一次准备发文章,准备徐徐道来,慢慢推进与讲解编程语言解释器的算法和代码。 都说程序员的最大理想就是开发出自己的编程语言来,现在,我也开始完成我最大的理想——开发我属于我自己的编程语言。 阅读了许多关于自制编程语言的书籍,可略感失望,感觉很多都是使用yacc/lex这些他人写好的工具生成代码,从而制作解释器,唯一得到的帮助就是解释器的大概基本原理和结构。

取名记

名字,是一个好项目很重要的一点,我绞尽脑汁思索许久,终于创造出来一个英语短语:BerryMath。为什么取这个名呢?首先Berry就是浆果,梅的意思,然后Math是数学的意思,翻译成中文就是数梅语言。因为Berry是我比较喜欢吃的一项水果类型?,并且在编程中数学是非常重要的一环,所以取名BerryMath。

使用语言

为了速度和跨平台性着想,我选择C++语言进行编写。

项目链接

https://github.com/BerryMathDevelopmentTeam/BerryMath

项目架构 项目架构图 词法分析器 抽象语法树构建器 语言解释器 语言内存结构 项目文件结构 include BerryMath.hinterpreter.hvalue.hast.hlex.h include BerryMath.cppinterpreter.cppvalue.cppast.cpplex.cpp main.cpp 解释器流程 Created with Raphaël 2.2.0 开始 新建解释器 词法分析 构建抽象语法树 解析运行抽象语法树 BerryMath脚本结束? 回收内存 结束 yes no 着手实现 接口文件编写

首先创建BerryMath.h和BerryMath.cpp文件,后期拓展开发等都需要引入该头文件 include/BerryMath.h

#ifndef BERRYMATH_BERRYMATH_H #define BERRYMATH_BERRYMATH_H #include "value.h" #include "lex.h" namespace BM { } #endif //BERRYMATH_BERRYMATH_H

src/BerryMath.cpp

#include "BerryMath.h" 语言内存实现

首先,这门语言我设计是动态类型语言,万物皆对象,所以内存部分设计如下:

Object Number String Null Undefined 派生 派生 派生 派生 proto属性是一个hash map,用于存储key和value(std::map) 拥有doubl e类型用于存储数 拥有std::stri ng类型用于存储字符串 Object Number String Null Undefined

并且,为了内存管理,编写妥善的垃圾回收机制,我们在基类Object中需要用unsigned long类型的linked属性以存储引用次数。这样在执行析构函数时,遍历所以属性,linked减一,尔后linked为0的就是没有任何Object或者后面的Variable类应用,即可删除了。 接着,为了便于调试查看,和后面编写语言拓展库时print等函数的编写,基类Object应当还有toString()成员函数用于返回std::string类型的值, 并有private的print函数用于打印数据,最后重载运算符,使得其可以被ostream类的实例化对象输出,比如cout。 在这时,我们还要考虑一点:toString转换有一个小bug:如果object的属性中有一个属性或属性的属性的值是自己(即循环调用),就会陷入死循环,所以我们还需要一个parent对象存储父节点,并有一个has成员函数用于判断某值是否是祖先节点,如果是,就将那个值toString的值为…。 然后呢,我们开始看一些有关属性读写的成员函数。 首先,我们需要一个插入值的函数,insert,需要接受std::string和Object*,这个函数流程主要是:插入属性值,增加引用计数,将值的parent属性设为自己。 有set函数,用于写属性值,如果没有该属性,就insert进去,有就修改,流程基本同insert 有get函数,用于获得某属性的值,return Object*类型的值。 还有del函数,用于删除某属性,首先找到该属性的值,然后从proto中删除,引用计数减一,如果引用计数为0,delete. 最后,析构函数,也与del函数类似,不过是遍历每一个属性,删除每一个属性罢了。 最后include/value.h、src/value.cpp代码如下: include/value.h

#ifndef BERRYMATH_VALUE_H #define BERRYMATH_VALUE_H #include #include #include #include using std::string; using std::map; typedef unsigned long UL; namespace BM { class Object { public: Object() : linked(0), parent(nullptr) { } bool has(Object*, Object*); void set(const string &key, Object *value); void insert(string, Object*); Object* get(const string &key); void del(const string &key); Object& operator[](const string &key) { return *get(key); } UL links() { return linked; } UL bind() { return ++linked; } UL unbind() { return --linked; } virtual string toString(bool = true, bool = true, string = ""); virtual Object* copy() { auto object = new Object(); for (auto iter = proto.begin(); iter != proto.end(); iter++) { object->set(iter->first, iter->second->copy()); } return object; } virtual ~Object(); friend std::ostream& operatorhas(v, root); return false; } string BM::Object::toString(bool indent, bool hl, string tab) { string o("{"); if (indent) { o += "\n"; tab += "\t"; } auto iter = proto.begin(); auto end = proto.end(); for (; iter != end; iter++) { const string& key = iter->first; if (key[0] == '_' && key[1] == '_') continue;// 双下划线开头的属性不显示 o += tab; if (hl) { o += "\033[32m\"" + key + "\"\033[0m"; } else { o += "\"" + key + "\""; } o += ": "; if (has(iter->second)) o += "..."; else o += iter->second->toString(indent, hl, tab); o += ","; if (indent) o += '\n'; } if (indent) { tab.pop_back(); o += tab; } o += "}"; return o; } void BM::Object::print(std::ostream &o, bool hl) { o linked++; value->parent = this; } void BM::Object::del(const string &key) { auto iter = proto.find(key); if (iter == proto.end()) return;, proto.erase(iter); Object* v = iter->second; v->linked--; if (v->linked second; if (!v || v->linked linked--; if (v->linked second = nullptr; } proto.clear(); }

以上,内存管理部分基本完成。

[未完待续]



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3