以鶸ice为例,手撸一个解释器明确目标

Posted Mu001999

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了以鶸ice为例,手撸一个解释器明确目标相关的知识,希望对你有一定的参考价值。

代码地址

# HelloWorld.ice
print("hello, world")

前言(废话)

其实从开始学习编译原理到现在已经有快半年的时间了,但是其间常常不能坚持看下去龙书(经常三天打鱼两天晒网,更何况每次打鱼不到半小时就累得不行又会放下书(笑)),截至到现在只勉强看完了前六章的部分,半年间其它事也没有做,其实想想上大学已经快两年了还是一事无成,知识也没有学到,不免觉得很羞愧。

暑假也要到了,这个学期马上也要结束了,临近大二结束之际,还是尝试着写一下以前想写的玩具吧,而本系列就是对这段过程的记录,也算是对龙书前六部分的一个小实践&总结(后面的部分可能看完了我也写不出什么东西来)。

其实再写这个解释器之前,我是拿lex + yacc + llvm照着tutorial试着拼过一个编译器,但是llvm对我来说可能有些太难了(苦笑)。残破不堪的代码在lyli分支上。

这个系列的教程(如果可以算作教程的话),其实主要还是实现了前端部分(一样有很多bug),而parser早就被研究透了,所以本教程基本上没有什么价值,可能唯一具有优势的地方就是我跟愿意看这几篇文章的读者大概是相同的入门(或者还未入门)的水平。

本教程分为四章

  1. 明确目标 & 设计语言
  2. 实现词法分析器
  3. 实现语法分析器
  4. 实现基础数据类型

且希望能达到在读者阅读完本系列后,能完成一个支持以下几项的解释器语言

  1. 整型、浮点型以及字符串类型
  2. 常见双目运算符
  3. 变量定义
  4. 函数定义及调用
  5. 基本控制流语句
  6. lambda表达式

适合读者

  1. 对编译原理感兴趣,但是还尚未正式的开始学习
  2. 尝试完成一个玩具解释器但不知道如何下手

正文

在正式手撸之前,我们要先确立我们要撸的是个什么玩意儿(你这不是废话吗摔)。毕竟在后期想要增加一些新的骚操作(新特性)的时候,若没有在一开始进行设计,难免会出现各种重构上令人烦躁的问题(虽然如果你按照本教程撸出来的解释器必然会带来重构上各种糟糕的问题,但是重构本身就是一件会带来各种糟糕的问题的事情(所以就不要介意了)),但是在正式写代码之前进行设计,总是一件应该做的事。

解释什么

从解释器角度来说,我们解释的是字符串,在验证字符串满足规则后进行解释,在解释完之后将其按照语义正确执行,而这个规则就是我们Ice的语法规则。


从词法分析器的角度来说,我们解释是字符串,只需要输入的字符串满足我们为词素指定的规则,然后根据输入的字符串返回token给语法分析器就可以了。


从语法分析的角度来说,我们解释的是token序列,且通过预测分析法依据token序列选择正确的产生式并返回抽象语法树(Abstract Syntax Tree)。

输入形式

只考虑交互式输入(即一行一行的输入)

如何解释

本项目中主要包含以下几个类:

  • Token:实例化的Token对象包含一个词素的类型以及词素值
  • LexicalAnalyzer:解析输入字符串,返回token序列
  • Node:实例化的Node及其派生类对象包含AST中一个节点所应具有的信息
  • SyntaxAnalyzer:根据token序列预测分析,返回AST(实质是一个Node或其派生类实例)
  • IceObject:包括自身类型信息,以及实现相关运算
  • Env:符号表,存储Ice运行时的对象信息
  • Interpreter:只提供run()接口供main函数调用,隐藏内部逻辑

好了,基本上结构就是这样,下面可以着手考虑Ice具备怎么样的语法了。

Ice 语法

整形、浮点型以及字符串类型
1
1.0
"hello, world"
常见双目运算符
1 + 1
(100 + 20) * 6 / 3
10 = 10
5 <= 3
变量定义
@a: 1
函数定义及调用
@add(a, b): a + b

@mul(a, b)
{
    return a * b
}

mul(mul(2, 3), add(2, 3))
基本控制流语句
@fib(n)
{
    if (n = 0) + (n = 1)
    {
        return 1
    }
    else
    {
        return fib(n-1) + fib(n-2)
    }
}

fib(10) # 89

@a: 3
while a
{
    print(a)
    @a: a - 1
}

@a: 0
do {
    @a: a + 1
    if a = 3
    {
        break
    }
    print(a)
} while a < 5

for 1 to 5
{
    @a: a + 1
    if a = 3
    {
        continue
    }
    print(a)
}
lambda表达式
@add(a, b): a + b
@mul: @(a, b){
    return a * b
}
@(a, b){ return a / b }(9, 3)

@quadraticSum: @(a, b){
    @sqrt: @(n){ return n * n }
    return @(a, b){ return a + b }(sqrt(a), sqrt(b))
}

基本上就是这样,那么如果你还继续打算看的话,下一章将会开始手撸Ice的词法分析器。

以上是关于以鶸ice为例,手撸一个解释器明确目标的主要内容,如果未能解决你的问题,请参考以下文章

机器学习模型可解释性进行到底 ——PDP&ICE图

什么是码表,什么是维度表?

纯手工打造一个 a.out (不使用编译器,就硬写)

纯手工打造一个 a.out (不使用编译器,就硬写)

有趣的 C++ 代码片段,有啥解释吗? [复制]

手撸golang GO与微服务 ChatServer之4 内存泄漏