实现一个自己的编程语言——准备

美团点评送外卖
美团点评送外卖   编辑于 2018-10-24 17:32
阅读量: 92

实现一个自己的编程语言,应该作为大三或者大二下学期学生必备的实验,毕竟又不用做到工业级别实现。也是掌握I/O和字符串和各种数据结构的一种手段。主要是最近被分配干自研数据库的SQLParser部分,也就是解析SQL语句生成执行计划的工作,捎带手复习一波编译原理

准备

  1. 语言上,选择Rust作为编程语言
  2. 准备好Linux vim 编辑器 和 Rust环境
  3. 描述文法使用巴克斯范式
  4. 需要稍微理解一下AST相关概念
  5. DFA NFA 算法初步即可
  6. 推荐书籍
    1. 编译原理 机械工业版 俗称龙书
    2. 两周自制脚本语言 日本人写的书
    3. 现代编译原理 俗称虎书

 

目标

实现一门可以自定义函数,并支持数据运算的动态语言,好像python 和 lisp一样的语言

 

Rust 特性

基本的if else 还有 while for 循环大家都一样,rust 也不例外,这里只介绍一些不常见比较难理解的特性

所有权

语言从内存管理上来看,大概可以分成两种,一种是动态GC 比如Java Python 当然还有一种就是手工管理内存,比如C/C++,目前来看动态GC语言比较多,毕竟业务系统的话,最快实现就可以了,不用在意内存细节,但是Rust也是一个静态管理内存的语言,只不过手工释放还是没有被采用。

那么这个所有权就是一个Rust的特性,官方文档上叫做ownership

不如从作用域上理解

void demo_func()
{
  for(int i =0;i< 10;i++){
    printf("%d",i)
  }
  printf("%d",i)
}

一个简单的C语言函数,我们会发现,第一个printf可以成功而第二个则不会成功,因为i的作用域仅在for循环的代码块中生效,而后面的话,这个i会自动的失效。相信大家都知道这个i变量是在栈上进行分配的,到了作用域结束的时候,变量自动释放。

Rust 的变量传递是用值来传递的,当然在java中也是值传递,只不过我们仍然可以使用一些函数来处理String类型,但是rust的话,就比较麻烦啦,处理的时候不仅仅要考虑当前传入参数是否可以被改变同时也要考虑一个问题,当出了花括号作用域的时候很有可能就GG了,变量被回收了,自动的释放内存。


#![allow(unused_variables)]
fn main() {
{                      // s 在这里无效, 它尚未声明
    let s = "hello";   // 从此处起,s 是有效的

    // 使用 s
}                      // 此作用域已结束,s 不再有效
}

来看一个简单的Rust代码片段,每一个变量都会有一个所有者,这个所有者其实简单来说就是变量名,当一个变量名离开了他的当前作用域的时候,ok,自动释放了,这些释放操作统统都是编译搞定的。


#![allow(unused_variables)]
fn main() {
{                     
    let s = "hello";   

    delete s
}     
}

可以这么认为编译器在处理的时候自动给加了个delete 关键字然后手工释放了内存,当然这段代码只是一种猜测,事实上编译器确实做了类似的操作。

引用和借用

CPP中有引用的概念,至于引用是什么呢,使用String 类型来做个例子

当一个语言执行的时候动态创建String的时候,一定是在内存中分配了两个空间,一个空间用来存字符串元信息,如s1 和 s2 另外一块存放值,ok,那么这个s1和s2就叫做一个对字符串的指针。

而 s 是引用,因为s只有一个简单的指针指向了指针,也就是我们认为指针的指针叫做引用,**ptr这种东西其实就是引用,当然给了个语法糖&ptr 就变成了引用。

而借用是啥呢?

上文我们知道了当一个变量出了作用域,那么他指向的内存区域就被释放了,为了防止我们操作了一个变量之后因为作用域的擅入而导致的变量消失,所以我们采用了借用的概念,即如果使用了引用,那么ok这个函数返回了以后,引用指向的内存区域并不会发生任何改变,也就是我们仅仅销毁了s对s1 并不会有任何的影响。

模块化

任何的语言想做点工程都是要模块化的,因为为了让更多的工具函数可以被重用,同时优良的代码结构也利于扩展。

rust 提供了 cargo 来做管理

命令还是 很简单的 cargo new project --selections , 这里selections 作为选项,目前可选的是lib 和 bin ,我们认为不同的项目应该是不重名的,so这样的话就可以定义不同的module了。

项目中分为两种类型的库一种是工程下的公共模块,另外一种是第三方库,引用的方式也不一样。

分别为 extern crate rand 和 use mod::submod::function 的方式,很像CPP内的命名空间,只不过被简化了。在Rust中遵守以下的分包策略

  1. 如果一个模块只有函数和其他的什么东西,那么直接在单文件中使用就好了 比如foo模块没有子模块 直接在 src/foo.rs 下定义即可
  2. 如果有子模块,请使用文件夹分包 src/foo/submod.rs

剩下的语法啥的,看官方文档就好了

收藏 转发 评论