Lox In Rust - 开坑

Lox In Rust - 开坑

写在前面的屁话

无聊,用 Rust 实现一下 craftinginterpreters,这本书后面的垃圾回收使用得是标记清除算法,最近也在看 垃圾回收算法手册:自动内在管理的艺术,刚好可以尝试把里面的理论知识应用到这上面。

我的本地环境

  • x86_64
  • macOS
  • rustc 1.57.0-nightly (73422130e 2021-09-24)

语言处理器

随便补充点基础,虽然后续会详细讲。
语言处理器,通常分为解释器跟编译器。 拿 JavaScript 来讲,我们把代码保存到文本文件中,然后浏览器或者其他的 JavaScript 解释器读取我们写好的文本之后执行出结果。 拿 C 语言来讲, 我们把代码写好保存成相关的文本文件, 然后编译器读取文件中代码将其生成一个可执行文件. 然后你可能会发现不管是解释器还是编译器, 它好像都要经过这么几个一样的过程

  • 词法分析 (scanning)
  • 语法分析 (parsing)
  • 静态分析 (static analysis)

其实这些基本是一个编译器(或解释器)的前端

词法分析 (scanning)

现在来举一个例子,下面有这么一段东西

8 + 2 * 3
复制代码

你的思路肯定是 2 乘以 3 再加 8。 然后现在来写段程序解析这一行为。把 "8", "+", "2", "*", "3" 分出一个一个词,这一过程其实就是一个扫描的过程,所以词法分析的程序,我们可以称为 lexical analyzer(词法分析器) 或者 scanner(扫描器)。
按照我的理解,其实就是一个分词的行为,然后要考虑语言中的关键字(或保留字, 譬如 JavaScriptvar while 这种)。
词法分析这部分其实有很多工具,甚至你可以用正则表达式来做这桩事,主要是一个自动机的理论实践,我选择手工处理。

语法分析 (parsing)

经过之前的分词阶段,我们就要面临如何处理这些分好的词。一般情况下, 会根据预先定义好的语法把之前分好的词描述成一个树形结构,很多人看 babel 相关的文章肯定听说过抽象语法树(abstract syntax tree, ast),其实就是语法分析阶段处理的。

8 + 2 * 3;
复制代码

这段代码,我们会把它解析成一个树形结构

|--- + ---|
8    |--- * ---|
     2         3
复制代码

一般这部分也是有工具的,不过我还是决定手写。

静态分析

其实这阶段是语义分析,譬如我们现在有这段代码

var a = 10;
var b = 11;
a + b
复制代码

这里看到 a 加上 b,我们可以很快地知道 a 代表 10b 代表 11,然后把 1011 相加,算出结果 21。人是这样思考的,我们也得让程序知道 ab 代表什么,是全局变量还是局部变量,得知晓它们的值,然后计算。如果是静态类型的,需要进行类型检查,如果是动态类型,只要在运行时进行类型检查。

Lox 语言简介

Lox 语言看起来有点像 JavaScript,动态类型带 GC 的高级语言,基础类型有 Booleans Numbers Strings Nil ,还支持 class 风格的面向对象复合类型,当然 if else 这些语法肯定也有,还有函数闭包之类的特性。

var a = 10;
var b = true;
var c = 12.34;
var d = "test";
01 / c;
-a;
if (b) {
  print d;
} else {
  while (a < 10) {
    a = a + 1;
  }
}

for (var i = 1; i < 10; i = i + 1) {
  print i;
}

fun sum(a, b) {
  return a + b;
}

fun fn() {
  var outside = "outside";
  fun inner() {
    print outside;
  }
  return inner;
}

var f = fn();
f();

class Breakfast {
  cook() {
    print "Eggs a-fryin'!";
  }

  serve(who) {
    print "Enjoy your breakfast, " + who + ".";
  }
}

var someVariable = Breakfast;
someFunction(Breakfast);
var breakfast = Breakfast();
print breakfast; // "Breakfast instance".

breakfast.meat = "sausage";
breakfast.bread = "sourdough";

// 使用 this 来获取对象的属性
class Breakfast {
  serve(who) {
    print "Enjoy your " + this.meat + " and " +
        this.bread + ", " + who + ".";
  }

  // ...
}

// 带参数的构造器
class Breakfast {
  init(meat, bread) {
    this.meat = meat;
    this.bread = bread;
  }

  // ...
}
var baconAndToast = Breakfast("bacon", "toast");
baconAndToast.serve("Dear Reader");

// 通过 < 表达继承
class Brunch < Breakfast {
  drink() {
    print "How about a Bloody Mary?";
  }
}
var benedict = Brunch("ham", "English muffin");
benedict.serve("Noble Reader");

// 展示 super
class Brunch < Breakfast {
  init(meat, bread, drink) {
    super.init(meat, bread);
    this.drink = drink;
  }
}
复制代码

基本上语法就这些,后续可以自己拓展一下数组跟字典类型的支持,或者其他语法

var arr = [1, 2, "test"];
var keyValue = { "a": 1, "b": "b" };
复制代码

因为我们不使用自带 GC 的语言来实现 Lox,所以还得自己给编译器实现一下 GC 算法,如果不想自己实现 GC,可以用 Go 来实现编译器。

分类:
后端