本文是 VMBC / D# 项目 的 系列文章,html
有关 VMBC / D# , 见 《我发起并创立了一个 VMBC 的 子项目 D#》(如下简称 《D#》) http://www.javashuo.com/article/p-zziqptgy-s.html 。java
VMBC 须要一个 内置 的 C 编译器, 想来想去, 以为仍是本身写一个,数组
计划用 C 语言 写, 由于 VMBC 的 C 编译器 要求是一个 本地库, 若是不要求是 本地库, 我就用 C# 写了, 呵呵呵 。函数
为何 是 库 呢 ? 由于这是一个 内置编译器, 是由 ILBC 运行时 来 调用的 (ILBC 见 《D#》), 因此 是一个 库 。spa
这个 库 最好 能 尽量的 小 。操作系统
C 语言 写的 代码 是 最贴近 底层(汇编) 的, 因此 C 语言 写的 库 应该是 最紧凑 的, 因此用 C 语言 来写 。指针
还有一个 缘由 是, 我会的 语言 很少, C 算是 相对 更熟一点的, So 。htm
有 网友 说 C 语言 不适合 写 编译器, C 的抽象过低了, 建议用 函数式 语言写,对象
又举例 Rust 最先是用 OCaml 写的, 而后又用 Rust 写了一遍 。blog
好吧, 但 Rust 、OCaml 这些语言 的 名字 我都 没怎么听过, 仍是用 C 吧 。
另外用 C 的话, 应该不用担忧 操做系统 的 支持 的 问题 。
这个 项目 我只 实现 语法分析 和 类型检查 的 部分, 语法分析 包含了 语法检查 。
生成目标代码 连接(连接外部库) 这 2 个 部分 你们 若是有兴趣, 对 汇编 和 操做系统 了解 的话, 能够来补充 。
InnerC 是 ansi C 的 子集 + 扩展, 只支持 ansi C 的 部分特性, 同时还会加入一些 新特性 。
总的来讲, InnerC 会 比 ansi C 简单 。
好比, InnerC 不支持 结构体(Struct), 由于 InnerC 是 做为 中间语言, 只须要是一种 “高级汇编语言” 就能够 。
不用 Struct, 那用什么 ?
用 数组, 包括 静态数组 和 从 堆 里 分配 的 数组 。
根据 偏移量 向 数组 的 相应位置 写入 字段 的 值, 这就是 Struct, 也是 对象 。
去掉 Struct 能够 省掉 很多 语法分析 的 开销 和 人力上的 研发成本 。
但 C 语言 里好像没有 按值 传递 数组 的 特性, 因此 InnerC 须要 加入 按值传递数组(拷贝传递数组) 的 特性 。
好比, InnerC 应该 增长 T [ n ] 类型, 用于 参数 和 返回值,
T [ n ] 类型 表示 按值传递数组(拷贝传递数组),
假设 A() 方法 调用 B() 方法, B() 方法有一个 T [ n ] arr 参数, 那么 A() 方法 传给 T [ n ] arr 参数 的 是一个 数组的 首地址 arr, 编译器会处理成 把 A() 里的 arr 数组 以 长度 n 拷贝到 B() 的 arr 里, 因此 B() 的 arr 也是 数组 的 首地址, 可是是 拷贝到 B() 的 堆栈 里的 数组 的 首地址 。
T [ n ] arr 表示 arr 参数 是 长度 为 n 的 数组, 编译器 会为 arr 在 B 的 堆栈 里 分配 长度为 n * sizeof(T) 的 内存空间 。 这个空间是 编译器 分配的, 是 静态分配 的, 等价于 声明一个 T arr[ n ] 这样的 静态数组 。
同理, 假设 B() 的 返回值 是 T [ n ] 类型, B() 实际返回的是一个 数组 的 首地址 arr, A() 里 用来 接收 B() 的 返回值 的 是一个 T arr[ n ] arr ; 静态数组 变量, 编译器会处理成 把 B() 里的 arr 数组 以 长度 n 拷贝到 A() 的 arr 里 。
InnerC 也不支持 对 函数指针 进行 类型检查,
不对 函数指针 类型检查 是指 函数指针 能够调用 任意 的 参数列表, 固然, 出了错 是 调用者 本身 负责 。^^
不过 对于 中间语言 来讲, 基本上 不用担忧 这个问题 。
InnerC 的 语法分析 能够 生成一个 表达式对象树, 把 表达式对象树 序列化 获得一个 byte [] (byte 数组),
这个 byte[] 就至关于 .Net 的 Op Code, 或者 java 的 Byte Code, 咱们能够把 这个 byte[] 称为 ILBC Byte Code (简称 Byte Code) 。
这样一来, 问题就明朗了,
若是 开发期 编译 生成的 目标代码 就是 ILBC Byte Code, 那 JIT 速度 较慢 的 问题 就 解决了 。
这就是说, 能够把 C 语言 做为 第一级 中间代码, Byte Code 做为 第二级 中间代码 。
这样, InnerC 就能够由 2 个模块 组成:
1 InnerC to Byte Code
2 Byte Code to Native Code
固然, 能够在 开发期 编译 直接 生成 Native Code (本地代码), 这是 AOT 。