处理错误
要说 Bug , 其实基本上没有 。有一个函数 die , 当发生任何奇怪的事情时就会调用它并转储编译器堆栈跟踪 - 如果幸运的话 , 会得到一个行号和一条有点模糊的错误消息 。
文章插图
该舍弃什么?
最后 , 我必须决定不支持哪些内容 , 因为将所有 C 语言放入 500 行是不可行的 。我决定想要一个真正像样的功能样本 , 以测试一般实现方法的能力 。例如 , 如果我跳过了指针 , 我就可以通过 WASM 参数堆栈并摆脱很多复杂性 , 但这感觉就像作弊 。
我最终实现了以下功能:
- 算术运算和二元运算符 , 具有适当的优先级
- int、short 和 char 类型
- 字符串常量(带转义)
- 指针(无论多少层) , 包括正确的指针算术(递增 int* 加 4)
- 数组(仅单级 , 不是 int[][])
- 功能
- typedefs(以及词法分析器 hack!)
- 结构 , 可以使用更多代码 , 基础知识就在那里 , 我只是无法将其压缩
- 枚举(enum)/联合(union)
- 预处理器指令(这本身可能有 500 行......)
- 浮点 。也有可能 , wasm_type的东西在里面 , 又无法将其挤进去
- 8 字节类型(long/long long 或 double)
- 其他一些小事情 , 例如就地初始化 , 这些都不太合适
- 任何类型的标准库或 i/o 不从 main 返回整数
- Casting 表达式
文章插图
好了 , 决定都做完了 , 让我们来看看代码吧!
Helper 类型
【挑战用 500 行 Python 写一个 C 编译器】编译器使用一小部分辅助类型和类 。它们都不是特别奇怪 , 所以我会快速地略过它们 。
Emitter 类
这是一个单程帮助器 , 用于发出格式良好的 WebAssembly 代码 。
WebAssembly , 至少文本格式 , 被格式化为 s 表达式 , 但单个指令不需要加括号:
文章插图
Emitter 只是帮助发出具有良好缩进的代码 , 以便更容易阅读 。它还有一个 no_emit 方法 , 稍后将用于丑陋的黑客攻击 - 请继续关注!
StringPool 类
StringPool 类用于保存所有字符串常量 , 以便将它们排列在连续的内存区域中 , 并将地址分配给代码生成器使用 。当你在 c500 中写 char *s = "abc" 时 , 真正发生的是:
- StringPool 附加一个空终止符
- StringPool 检查是否已经存储了“abc” , 如果是 , 则将地址返回
- 否则 , StringPool 将其与基地址 + 到目前为止存储的总字节长度一起添加到字典中 - 这个新字符串在池中的地址
- StringPool 手回地址
- 当所有代码完成编译后 , 我们使用 StringPool 生成的巨大串联字符串创建一个 rodata 部分 , 存储在字符串池基地址处(追溯性地使 StringPool 分发的所有地址都有效)
Lexer 类很复杂 , 因为词法 C 很复杂 ((\([\abfnrtv'"?]|[0-7]{1,3}|x[A-Fa-f0-9]{1,2 })) 是该代码中用于字符转义的真正正则表达式) , 但概念上很简单:词法分析器继续识别当前位置的token是什么 。调用者不仅可以查看该 token , 也可以使用 next 告诉词法分析器前进 , “消耗”该 token 。它还可以使用 try_next 仅当下一个 token 是某种类型时有条件地前进 - 基本上 , try_next 是 if self.peek.kind == token: return self.next( ) 。
由于所谓的“Lexer Hack (词法分析器黑客)” , 还有一些额外的复杂性 。本质上 , 在解析 C 时 , 你想知道某个东西是类型名称还是变量名称(因为上下文对于编译某些表达式很重要) , 但它们之间没有语法区别:int int_t = 0; 是完全有效的 C , typedef int int_t 也是如此;int_t x = 0; 。
要知道任意标记 int_t 是类型名称还是变量名称 , 我们需要将类型信息从解析/代码生成阶段反馈回词法分析器 。对于想要保持其词法分析器、解析器和代码生成模块纯净且独立的常规编译器来说 , 这是一个巨大的痛苦 , 但实际上对我们来说并不难!
推荐阅读
- 曝赵丽颖怀二胎现身,冯绍峰暂停工作陪母子,冯妈送500w珠宝庆贺
- 七子白面膜粉用什么调
- 玉脏了用什么清洗最好 玉怎么清洗和保养方法
- 为什么不能用高压锅炖汤 为什么不能用高压锅压卤味
- 山姆会员卡可以借用吗 山姆会员卡是一年期还是永久
- 年初一拜神用什么供品 年初一拿什么拜神
- 皮衣保养油怎么用 保养油怎么用
- 微信亲属卡绑定信用卡怎么消费 微信亲属卡信用卡怎么开通
- 西兰花榨汁用开水焯几分钟 西兰花榨汁用开水焯多少分钟
- 用淀粉可以做肠粉吗 用淀粉能做肠粉吗