# Z编译器(旧版) **Repository Path**: z-lang/zc_old ## Basic Information - **Project Name**: Z编译器(旧版) - **Description**: Z语言的编译器 - **Primary Language**: C - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 1 - **Created**: 2023-03-22 - **Last Updated**: 2024-10-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## Z语言编译器 zc是Z语言的编译器和解释器。 zc的初版用C语言开发,将来会改为用Z语言本身实现。 zc支持暂时只支持Linux,将来会添加其他平台的支持。 本工程有一本配套开源书籍[《脚踏实地Z语言》](https://gitee.com/jiaota/jiaota-z),详细记录Z语言设计与开发的每一个步骤。 本工程还处于非常早期的筹备和尝试阶段。但由于本工程与配套书籍都遵循**增量开发**方法,每一步都是相对完整的。 zc早期代码参考了[ChibiCC](https://github.com/rui314/chibicc) 和[clox](https://github.com/munificent/craftinginterpreters/tree/master/c), 分别用于实现类C的静态特性和类Lox的动态特性。 ## Z语言特性 Z语言是一门~~通用编程语言~~次世代的玩具编程语言,它的主要独特之处在于: - *动静皆宜*。Z语言同时支持静态编译(AOT)、动态解释(Interpreter),未来还会添加及时编译(JIT)功能。Z语言既支持静态类型检查,也支持动态类型数据。 - *编译期脚本*。利用动静结合的特性,将解释器嵌入到编译流程之中,实现编译期脚本执行功能。这个特性可以用来实现泛型、宏和模板等高阶语言特性。 - *面向场景编程*。针对不同的开发场景(Scenario),编译期提供不同的语言特性组合(即支撑集,Support Set)。 - *DSL*。Z语言的前端是插件式可扩展模型,因此可以订制自己的DSL。 - *组合与接口*。Z语言的类型体系由组合(Composition)和行为接口(Duck Typing)配合完成。Z不支持继承,而是利用编译期脚本来实现编译期多态和运行时多态。 - *层次化内存管理*。利用场景编程特性,在不同场景中支持不同的内存管理策略。包括:手动管理、自动释放、智能指针、引用计数、垃圾回收和时间线管理。 - *代码工厂*。将编译器扩展为编译服务器,再扩展为代码工厂,实现增量编译、编译缓存、语法服务、远程编译等功能。适应大规模编译服务场景。 以上几个特性配合起来,让Z语言拥有无限的扩展能力,“想怎么玩,就怎么玩”,是最适合学习编译器技术的玩具语言。 Z语言既可以用于底层的高性能开发,也可以用于应用层的动态应用开发。Z应当同时具备Python+C或Javascript+JIT的组合能力。 Z语言的**驱动应用**有以下几个方向: - IDE。Z语言应当有自己的IDE,完全由Z语言开发。IDE的前提是完善的语言服务器、以及跨平台的图形UI库。 - 游戏引擎。我认为游戏式学习是最好的学习方法,而学习计算机技术最好的方法也是制作自己的游戏。因此Z语言应当有自己的游戏引擎。 - 科学计算。Z语言不但可以用来学习计算机技术,还可以辅助学习其他知识体系。因此科学计算工具也是Z语言的主要目标。 - 机器人和嵌入式。 ## 语法速览 本章用最简单的示例来展示Z语言已经实现的功能。具体可以参看编译器的[测试用例](test.sh)。 Z语言既提供静态编译器(`zc`),也提供动态解释器(`zi`)。 - `zc`编译器的用法和`gcc`、`clang`、`tcc`类似,将源码文件编译为汇编或二进制文件。 - `zi`的用法和Python的解释器类似,既可以直接解释一段语句或一个脚本文件,也可以打开一个交互界面(即REPL)进行交互式开发。 #### 最简示例 Z语言每行语句都有返回值,最后一行的返回值就是整段程序的返回值。 因此,最简单的Z代码就是一个数字: ```z 1 ``` 我们可以用解释器直接运行: ```bash $ zi "1" 1 ``` 也可以用交互式模式(即REPL)运行: ```bash $ zi Z语言解释器,版本号:0.0.1。 >>> 1 1 ``` 将这段代码存入`hello.zs`文件中,我们可以直接运行脚本文件: ```bash $ zi hello.zs 1 ``` 也可以用静态编译器`zc`编译它: ```bash $ zc hello.zs $ ./hello.exe $ echo $? 1 ``` 注意,这里用的`.zs`后缀名意思是(Z Script),编译器会自动识别为脚本文件,用`main`函数包括起来。 如果是`.z`文件,要静态编译的话,就和C语言一样需要定义`main`函数了: ```z // hello.z fn main { 1 } ``` 编译方式和上面一样。 zc编译器会默认生成和源码文件同名的`.exe`文件,可以直接运行。 这里返回值作为程序的结束码返回,因此在`bash`中可以用`echo $?`来查看。 #### 加减乘除 比单个数字再复杂一点的示例是加减乘除的四则运算。 Z也支持负号`-`和括号`()`。 ```z 1+1 // = 2 15 - 10 // = 5 -2 + 3 // = 1 (1+2)*(5-3) // = 6 (1+2+3+4+5)/3 // = 5 ``` 因此,我们可以把zi解释器当做一个简单的计算器来使用: - 直接计算 ```bash $ zi "1+2*3" 7 ``` - 交互式计算 ```bash $ zi Z语言解释器,版本号:0.0.1。 >>> 1+2*3 7 >>> (5+10)/3 5 ``` #### 值量 Z语言中,用来表示量的东西有4种,我们统称为`值量`(Value Identifier),即它是一种“量”,背后代表了一个值。 - 常量(Constant):用`const`关键字定义,直接写死在代码中,不可改变,一般用来定义有特殊意义的恒定值,例如`PI`。 - 标量(Scalar):用`let`关键字定义,定义之后不能改变。一般用作计算过程的初始量或中间结果。标量和常量还有一个区别,标量是有作用域的,而常量是全局的,他们在汇编中的实现也完全不同。 - 变量(Mutable):用`mut`关键字定义,定义之后可以改变值,但不能改变类型。和C语言的变量一致,用法也一致。 - 幻量(Variable):用`var`关键字定义,值和类型都能变化,相当于动态语言中的变量。用于更适合动态类型的场景,如脚本、主函数等。 现在Z只实现了最基本的标量和变量,其他两种量的实现还在计划中。 ```z // 标量 let a = 1 a = 2 // 错误!a是标量,不能改变值 let b = a + 1 // OK,标量可以作为计算的一部分 // 变量 mut c = 1 c = 10 // OK // ``` 注:为什么要这么取名? 因为C语言中只有常量和变量的区分,但Z语言借鉴了不少现代语言(如swift和Kotlin),把不可变的标量作为主要推荐使用的量,因此“变量”这个词就不合适了。 我发现其他的地方都把这种不可变的量叫做“不可变变量”(Immutable Variable),这样既拗口又冗长,而且英文版里还包含了`Variable`,直接就有歧义了。 所以我得给它取个简单又合适的名字,于是就有了`标量`(Scalar)这个词。 “标量”的“标”字,既有数学里的标量的意思,也有“标准的量”的意思。作为数学意义上的标量,它往往代表一个值,而不是一个“向量”(即一组值);而作为“标准的量”,它是Z语言官方推荐使用的量,比变量更加“标准”。 再考虑变量。C的变量和JavaScript或Python的变量本质上并不一样,C的变量只有值是可以变的,但类型不能改变;而动态语言中的变量值和类型都可以变。 Z语言作为动静皆宜的语言,需要在编译级别(即语言级别)区分这两种不同的变量。因此我就参照“变幻莫测”的成语,把“更能变”的动态变量简称为“幻量”了。 这样,我们就有了4个长度相同的简称:常量、标量、变量、幻量,分别对应4种不同的量。 在文档中,经常需要统称这4种不同类型的量,而只用一个“量”字,在现代汉语中比较突兀,因此我就把它们统称为“值量”了,意思是他们都是一种“量”,背后代表了一个值。 #### 语句与表达式 Z语言源码的最高层单元是语句。 Z语言所有语句都有返回值。因此,Z语言的语句相当于C语言中“语句”和“表达式”的结合。 我们可以这么说:所有的Z语句都是一个表达式,我们叫“语句”时,关注的是它的结构和功能;而叫“表达式”时,关注的是它有个返回值。 ```z // 数值是最简单的语句 1 // 值量定义也是一个语句,它的值就是变量的值 let a = 5 // 这个表达式的值也是5 // 值量的名称 a // 值量与数值的混合计算,即运算语句 (a+5)/(a-3) ``` #### 代码块 和C语言一样,`{...}`表示一个代码块,里面可以有多个语句 但和C语言不同的是,Z的代码块也是一个表达式,它的值就是代码块中最后一行语句的返回值 ```z let a = {1; 2; 3} // a == 3 ``` 这样,我们就能用代码块来组织复杂的逻辑了。 比如下面介绍的函数,它要执行的内容就是一个代码块,叫做“函数体”。 #### 函数 Z语言的函数定义语法类似于Go: ```z fn add(a int, b int) int { a + b } ``` 函数定义有几点值得注意: - 函数的关键字是`fn`,即`function`的简称。 - 参数的类型是放在名字后面的,用空格区分。 - 返回类型放在')'和'{'之间。 - 函数体是由'{'和'}'包裹的代码块。 - 代码块的返回值默认是最后一行表达式的值。 如果函数没有参数,那么'()'需要省略。 ```z fn pi {3.14} ``` 函数调用的方式和C语言一致: ```z add(1, 2) // 3 pi() // 3.14 ``` #### 数值类型 现在Z实现的类型有: - int,有符号整数,32位,即i32 - char, 无符号整数,8位,即u8 其他数据类型会逐步添加。 #### `if-else`语句 和C基本一致: ```z let r = if a > 0 { 10 } else if a < 0 { -10 } else { 0 } ``` 注意: - 和Go语言一样,条件不需要括号 - 分支代码块必须用大括号。 - `if`和`else`可以连续嵌套 - `if-else`语句的返回值就是对应分支代码块的返回值 由于`if-else`语句有返回值,我们可以用它来替代C语言的三目运算符: ```z let r = if a>0 {1} else {-1} ``` #### `for`语句 Z的`for`语句和C的`while`语句基本一致: ```z let a = 0 for a < 10 { a = a + 1 } a // 10 ``` 我暂时不打算实现C的三段式`for`语句,而是用类似`while`语句的方式来替代它。 我的思路是:三段式`for`语句往往用来遍历某种数据结构,而用类似D或Go的`range`概念来遍历它们会更方便且安全。 由于基于`range`的`for`语句还没实现,这里就不展示了。请参考[语法设计中的相关内容](#控制流)。 #### 数组 Z语言的静态数组类型声明为`[元素类型|长度]`,初始化可以利用数组字面量: ```z let a = [1, 2, 3] a[2] // 3 // 指定类型 let b [int|5] = [1, 2, 3, 4, 5] b[3] // 4 ``` 动态数组需要实现自定义类型之后才能做出来。 切片功能则可能还需要实现了接口特性之后才能做出来。 #### 字符和字符串 Z语言的字符类型是`char`,是一个8位无符号整数,用来表示ASCII字符。 字符的字面量和C语言一致,用单引号括起来:`'a'`。 字符串本质上是字符的数组,由于现在只实现了静态数组,因此字符串实现的也是不可更改的常量字符串。 未来实现了动态数组之后,就可以实现可变的字符串(即strbuf)了。 ```z // 单个字符 let a char = 'a' let b = 'b' // 字符串 let s str = "Hello" s[0] // H ``` #### 小结 以上是至今位置已经实现的所有Z语言特性。这部分内容会随着Z编译器的发展不断完善,敬请期待。 ## 语法设计 上面展示的是已经实现的Z语言示例。除了这些特性之外,Z语言还设计了不少功能,有待未来逐步实现。 如果要了解详情,可以参看Z语言的[设计文档](docs/%E8%AE%BE%E8%AE%A1.md)。 ## 开发计划 1. v0.1:参考ChibiCC和clox,实现一个和C/lox差不多的语言子集。除了C的基本功能外,需要初步实现模块化、自定义类型与方法。 1. v0.2:学习LCC、TCC和Mir,根据需求修改或添加静态特性。实现Z->C的翻译功能 1. v0.3:学习QuickJS、Wren和Lua,根据需求添加动态特性。实现Z->JS的翻译功能。初步探索动静结合的编译模式。 1. v0.4:实现C->Z的翻译功能。建立Z的标准库。 1. v0.5:实现自举,用Z语言实现zc编译器。 1. v0.6~v1.0:实现Z语言的部分高阶功能:接口、组合、泛型、函数模板等。 1. v1.0:完整的Z语言语法。 1. v1.x:完善生态,稳定语言特性和标准库。实现应用库:嵌入式编程、图形和UI库、游戏引擎等。 在这个粗略的计划中,v0.5版本实现Z语言的自举,之后的编译器都用Z语言开发。 v0.5之后的详细规划现在做还太早了,要等到完成自举之后再仔细讨论。 v0.1~v0.5的详细规划如下: ### v0.1:基本的静态编译器和动态解释器 v0.1之前的主要目标是**学习**。我选取了最适合入手的ChibiCC和clox两个工程,分别参照他们来实现一个Z语言的静态编译器(zc)和动态解释器(zi)。 参照ChibiCC的增量开发模式,我把Z语言分成简单到复杂的多个步骤,每一步都同时开发zc和zi的功能,做到同时通过测试用例。 每一次Git提交,基本对应一个开发步骤。参考ChibiCC,大概有300个左右的步骤才能达到完善的C语言支持,而Z语言不需要处理C语言那么多细节,因此初步估计大概需要200个左右的步骤。 到现在为止(2023/5/8),我已经完成了76此提交,除去初始阶段的反复横跳,有效的提交大概有50个,对应40多个步骤。也就是说,v0.1的开发大概已经完成了20%。 下面列出v0.1需要完成的特性,以及大致的完成状况。这些特性是由简单到复杂排序的,实际开发顺序基本一致: - [x] 简单数字 - [x] 加减法 - [x] 负数 - [x] 四则运算 - [x] Z语言基本设计 - [x] 比较运算 - [x] 变量定义 - [x] 多个局部变量 - [x] if语句 - [x] 基本for语句 - [x] 代码块 - [x] 错误输出 - [x] 多条语句 - [x] 打印词符和语法树 - [x] 无参数函数 - [x] 基本函数调用 - [x] 多参数函数(最多支持6个) - [x] 函数传参 - [x] 函数局部变量 - [x] 基本的类型信息,以及最简单的类型推导 - [x] 变量声明中添加类型信息 - [x] 指针 - [x] 指针运算 - [x] 函数参数指定类型 - [x] 解释器:用Value类来表示运行时的动态类型 - [x] 数组字面量 - [x] 数组下标访问 - [x] 字符类型char - [x] 字符串字面量 - [x] 字符串下标访问 这个列表我会随着开发持续更新,具体情况还可以参考代码的提交历史。 v0.1还待实现的功能点: - [ ] 作用域 - [ ] 全局常量 - [ ] 基本模块化:基于文件 - [ ] C函数的调用 - [ ] REPL - [ ] 区分标量和变量 - [ ] 引用类型(替代指针) - [ ] 基本自定义类型(即结构体) - [ ] 更多的基本数据类型(bool、short、long、f32、f64等) - [ ] 方法 - [ ] 基本的组合模式 - [ ] 最简单的编译期脚本:在编译期调用解释器执行简单函数 - [ ] 最简标准库:I/O、字符串、内存等 - [ ] 切片(如果切片需要接口之类的高端特性,那就放到后面的版本去实现 - [ ] 更多的示例 - [ ] 《脚踏实地Z语言》的相关章节 这些功能在实现时还会细分成更小的步骤,并动态加入到上面的已完成列表中。敬请期待。 v0.1完成之时,zc已经有了一个良好的基础: - 静态编译和动态解释都可以使用,且可以做到基本的交互 - 语法基本完善,表达力大致相当于C语言。 - 可以调用C语言的函数,实现基本的标准库。可以用来做一些简单的编程工作,例如刷题。 v0.2之后的暂时只做大块需求的规划,等完成v0.1之后,再细化v0.2的规划。 ### v0.2:完善的静态功能 v0.2的主要目标是完善静态编译器的功能: - 学习LCC、TCC、Mir,以及GCC和CLANG的C语言部分,博采众长,重构zc静态编译器,形成Z语言独有的风格 - 完善所有静态特性的细节,包括类型推导、类型检查、作用域、模块化、编译期脚本等。 - 完整的Z->C翻译。Z语言可以编译成C代码,再借助Clang等现有的C编译器,可以接入更多的生态。 - V0.2的C生态完成标志是通过翻译的C接入到STM32嵌入式平台中。 - 初步的性能调优,引入最简单的几种优化手段,例如常量折叠、常量传播、死代码消除等。 - 基本的内存管理。 ### v0.3:完善的动态功能 v0.3的主要目标是完善动态解释器的功能: - 学习QuickJS、Lua和Wren,细化动态解释器的功能 - 添加更多的动态类型支持,以及动态静态类型的无缝转化功能 - 选择Javascript作为输出目标,实现Z->JS的翻译功能 - 建立最简单的ZJ标准库,可以调用Javascript函数,实现DOM操作等常见前端操作。 - v0.3的生态完成标志是开发一套简单的前端页面,展示Z语言的官网。 ### v0.4:C->Z/JS->Z与标准库 v0.4的主要目标是完善Z语言标准库。Z语言的标准库中,后端库主要参考或封装C的实现,前端主要引用JS的实现。 - 实现C->Z的翻译功能。将C语言标准库的部分功能转化为Z语言标准库。 - 实现JS->Z的翻译功能,完善Z的前端标准库。 - 实现基本的面向场景编程,方便前后端不同场景的开发。 - 包管理 - 第三方库生态 v0.4的应用标志: - 实现类似go/http的http库,并实现类似gin的WEB服务框架。 - 实现类似Vue.js的前端框架。 - 利用上述的前后端开发一套Web应用框架。 - 将我之前用Vue.js+Go实现的衍星书院项目移植到Z语言来。 ### v0.5:自举 - 用Z语言重写zc编译器和解释器,实现自举。 - 如果有可能,考虑实现zc编译器的全自动C->Z翻译功能,方便未来在更多的平台上实现自动自举。 - 进一步探讨编译器和解释器的完美融合。初步实现增量编译的编译服务器。 - 完善Z语言的开发工具: - REPL - Notebook - Language Server - VSCode插件 - 静态分析 - LLVM后端 - WASM后端 ### v0.6:社区化 v0.5实现自举之后,Z语言已经有了一个良好的基础,因此从v0.6开始Z语言的开发模式将会转向社区化。 未来的Z语言功能和规划,将由社区讨论决定。 ## 感谢 - [ChibiCC](https://github.com/rui314/chibicc) - 本工程的灵感来源,也是本工程的代码基础。参看[引用License](#引用License)。 - [LCC](https://github.com/drh/lcc) - 一个开源的C编译器,并配套了的书籍《A Retargetable C Compiler: Design and Implementation》。我会学习这本书,并参考它的实现。 - [TCC](https://www.bellard.org/tcc) - 大神Bellard开发的C编译器。本来我打算用它作为实现基础,但可惜没找到相对完整的文档。我打算学习完chibicc和LCC之后,再自己阅读TCC的源码,并融入它的部分特性。 - [Mir](https://github.com/vnmakarov/mir) - 一个C语言解释器和JIT编译期。 - [QuickJS](https://bellard.org/quickjs/) - 一个开源的Javascript引擎。我会学习它的实现,用来实现Z语言动态子系统以及与Javascript的互操作。 - [Wren](wren.io) - clox作者Bob Nystrom开发的一个小巧精致的动态嵌入式语言。 - [Lua](lua.org) - 设计精巧的脚本语言,在游戏编程行业很常用。 ### 引用License - [ChibiCC](https://github.com/rui314/chibicc):[MIT License](LICENSE.chibicc) - [clox](https://github.com/munificent/craftinginterpreters/tree/master/c):[MIT License](LICENSE.clox)