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

导语

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

取名记

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

使用语言

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

项目链接

https://github.com/BerryMathDevelopmentTeam/BerryMath

项目架构

项目架构图

词法分析器
抽象语法树构建器
语言解释器
语言内存结构

项目文件结构

  • include
    • BerryMath.h
    • interpreter.h
    • value.h
    • ast.h
    • lex.h
  • include
    • BerryMath.cpp
    • interpreter.cpp
    • value.cpp
    • ast.cpp
    • lex.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<std::string, BM::Object*>) 拥有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 <iostream>
#include <string>
#include <map>
#include <sstream>
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& operator<<(std::ostream& o, Object& v) {
            v.print(o);
            return o;
        }
    protected:
        void print(std::ostream&, bool = true);
        UL linked;
        map<string, Object*> proto;
        Object* parent;
    };
    class Number : public Object {
    public:
        Number() : Object(), v(0) { }
        Number(double t) : Object(), v(t) { }
        string toString(bool = true, bool hl = true, string tab = "") {
            string o("");
            std::ostringstream oss;
            oss << v;
            if (hl) o += "\033[33m";
            o += oss.str();
            if (hl) o += "\033[0m";
            return o;
        }
        double& value() { return v; }
        Object* copy() {
            return new Number(v);
        }
        ~Number() { }
    private:
        double v;
    };
    class String : public Object {
    public:
        String() : Object(), v("") { }
        String(const string& t) : Object(), v(t) { }
        string toString(bool = true, bool hl = true, string tab = "") {
            string o("");
            if (hl) o += "\033[32m";
            o += "\"" + v + "\"";
            if (hl) o += "\033[0m";
            return o;
        }
        string& value() { return v; }
        Object* copy() {
            return new String(v);
        }
        ~String() { }
    private:
        string v;
    };
    class Null : public Object {
    public:
        Null() : Object() { }
        string toString(bool = true, bool hl = true, string tab = "") {
            string o("");
            if (hl) o += "\033[36m";
            o += "null";
            if (hl) o += "\033[0m";
            return o;
        }
        Object* copy() {
            return new Null;
        }
        ~Null() { }
    };
    class Undefined : public Object {
    public:
        Undefined() : Object() { }
        string toString(bool = true, bool hl = true, string tab = "") {
            string o("");
            if (hl) o += "\033[35m";
            o += "undefined";
            if (hl) o += "\033[0m";
            return o;
        }
        Object* copy() {
            return new Undefined;
        }
        ~Undefined() { }
    };
}


#endif //BERRYMATH_VALUE_H

src/value.cpp

#include "../include/value.h"

bool BM::Object::has(Object *v, Object* root = nullptr) {
    if (!root) root = this;
    if (this == v || parent == v) return true;
    if (parent == root) return false;
    if (parent && parent != this) return parent->has(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 << toString(true, hl) << std::endl;
}
void BM::Object::set(const string &key, Object *value) {
    if (proto.count(key) == 0) {
        insert(key, value);
    } else {
        proto[key] = value;
        if (this != value) value->linked++;
        value->parent = this;
    }
}
BM::Object* BM::Object::get(const string &key) {
    auto iter = proto.find(key);
    if (iter == proto.end()) return nullptr;
    return iter->second;
}
void BM::Object::insert(string key, Object *value) {
    proto.insert(std::pair<string, Object*>(key, value));
    if (this != value) value->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 < 1) delete v;
}
BM::Object::~Object() {
    for (auto iter = proto.begin(); iter != proto.end(); iter++) {
        Object* v = iter->second;
        if (!v || v->linked < 1 || has(v)) continue;
        v->linked--;
        if (v->linked < 1) delete v;
        iter->second = nullptr;
    }
    proto.clear();
}

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

[未完待续]

  • 23
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在测绘领域,学习Linux和C++语言以及Qt编程可以用来开发以下类型的应用: 1. 地理信息系统(GIS)软件:Linux和C++语言是广泛应用于开发GIS软件的技术栈。通过使用Qt编程框架,可以构建功能强大的GIS应用程序,用于地图显示、地理数据处理、空间分析等功能。 2. 测量和测绘工具:使用C++和Qt可以开发各种测量和测绘工具,用于采集地理数据、进行测量计算和定位等任务。这些工具可以与测量设备(如全站仪、GPS等)进行集成。 3. 地图绘制和编辑工具:利用C++和Qt编程,可以开发地图绘制和编辑工具,用于创建和编辑地图数据。这些工具可以支持矢量数据和栅格数据的编辑,并提供丰富的地图符号和标注功能。 4. 空间数据分析软件:通过利用C++和Qt编程,可以实现各种空间数据分析算法和模型,例如地理坐标转换、地图配准、地物提取、空间插值等。这些分析结果可以用于测绘、规划、环境分析等领域。 5. 三维可视化应用:利用C++和Qt可以开发具有三维可视化功能的应用程序,用于呈现地理数据的三维模型、地形图、倾斜摄影等。这些应用可以提供交互式的导航和观察功能。 6. 数据库管理和查询工具:通过使用C++和Qt编程,可以开发用于管理和查询地理数据库的工具。这些工具可以支持常见的数据库系统,并提供地理数据的高效存储和查询功能。 总之,Linux和C++语言以及Qt编程在测绘领域中被广泛应用于开发各种测绘软件和地理信息系统应用。它们提供了丰富的功能和灵活性,能够满足测绘领域的复杂需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值