{"id":26709600,"url":"https://github.com/siriusdemon/memorandum","last_synced_at":"2025-03-27T08:17:31.780Z","repository":{"id":59869895,"uuid":"504101269","full_name":"siriusdemon/memorandum","owner":"siriusdemon","description":"memorandum of Chibicc and prototype of Manda","archived":false,"fork":false,"pushed_at":"2022-10-13T10:25:25.000Z","size":529,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2023-03-08T14:48:24.915Z","etag":null,"topics":["compiler","parser"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/siriusdemon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-16T09:55:29.000Z","updated_at":"2023-01-31T01:10:57.000Z","dependencies_parsed_at":"2023-01-19T21:58:40.381Z","dependency_job_id":null,"html_url":"https://github.com/siriusdemon/memorandum","commit_stats":null,"previous_names":[],"tags_count":null,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siriusdemon%2Fmemorandum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siriusdemon%2Fmemorandum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siriusdemon%2Fmemorandum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/siriusdemon%2Fmemorandum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/siriusdemon","download_url":"https://codeload.github.com/siriusdemon/memorandum/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245806439,"owners_count":20675298,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","parser"],"created_at":"2025-03-27T08:17:31.223Z","updated_at":"2025-03-27T08:17:31.768Z","avatar_url":"https://github.com/siriusdemon.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# memorandum\n\n2022.10.13 \n\nminimanda 是因缘和合之下的产物。本来我只想研习 chibicc 顺便做一点笔记，但是 chibicc 的 parser 越来越复杂，到后面读起来十分费劲，所以我才开始尝试将它的 parser 改成 S-表达式，于是就有了 minimanda。\n\n但是不得不说，chibicc 的 parser 实现是非常工整的，作为非 S-表达式的 parser 非常值得模仿。只是由于 C 的语法设计问题，拆解出来的层级就比较多，有的层级只有词法而没有语义，理解就要比较费劲。S-表达式就不存在这样的问题。\n\n因而，minimanda 最初被设计成一个有着 S-表达式语法的 C 语言。\n\n这样一路跟着作者一点点添加功能，没什么问题。直到作者将他的测试用例从 BASH 转移到 C 语言，他使用了 #define 宏来打印输出。但是 minimanda 没有宏，所以我依旧使用 BASH 写测试用例。但是，用 BASH 有个约束，它的返回值只有一个字节。这意味着你无法测试像 long 这种多字节的类型。我思考良久，在 lexer 到 parser 之间增加了一个 S-表达式的 parser，并在这个层面上实现了一个小型的 LISP 解释器，从而实现了 LISP 风格的元编程（LISP宏）。\n\n项目的结束同样是巧合，因为我突然发现真正需要在意这些东西的人并不在意。当然不只是技术，还有其他很重要的东西他们也不在意。这都是私心作怪。但无论如何，对于那些想要学习编译器的，正被困在家中、学校里、野外等地方的学生来说，chibicc 绝对值得消化咀嚼，也希望 minimanda 会有一定的启发意义。\n\n最后我想罗列下 minimanda 的 commit 历史和语法实现：\n\n[提交历史入口](https://github.com/siriusdemon/memorandum/commits/main/minimanda)\n[第一次提交](https://github.com/siriusdemon/memorandum/commit/cd9bef22b70be8be65a132bbdb51963cba445ce6)\n\n#### 01: manda journey\n基本上和 chibicc 一样，支持简单的整数。\n```sh\nassert 0 0\nassert 42 42\n```\n\n#### 02: Support + - operation\n实现加减运算\n```sh\nassert 42 \"(+ 20 22)\"\nassert 42 \"(- 50 8)\"\n```\n\n这一节引入了很多东西，tokenize，parse，codegen，整个编译框架原型显现。\n\n#### 03: add [] as alternative list expression\n像 scheme 一样，支持 `[]` 作为括号提升可读性。\n```sh\nassert 42 \"[+ 20 22]\"\nassert 42 \"[- 50 8]\"\n```\n\n#### 04: Add * /; use stack machine\n添加了乘除运算，并将后端改成栈机来实现，就像 JVM 一样。不过这个栈机非常简易。\n```sh\nassert 42 \"(* 6 7)\"\nassert 42 \"(/ 84 2)\"\n```\n\n#### 05: add nested expression\n允许表达式嵌套。\n```sh\nassert 42 \"(* (/ 42 (+ 1 6)) (- 9 2))\"\n```\n\n#### 06: split into several files\n重构代码，将编译器的各个部分拆开。\n```sh\ncodegen.c  manda.h  main.c  parse.c  tokenize.c\n```\n\n#### 07: improve error report\n重构代码，改进错误提示。这些都是 chibicc 作者写的代码。\n\n#### 08: support more operands for some operation\n模仿 Lisp，支持无限操作数。\n```sh\nassert 42 \"(* 2 3 7)\"\nassert 42 \"(+ 1 2 3 4 5 6 7 (* 1 2 7))\"\n```\n#### 09: [update README]\n今天是个纪念日。\n\n#### 10: support multi-expression\n支持输入为多个表达式，以最后一个表达式的值作为返回值。\n```sh\nassert 3 \"1 2 3\"\nassert 42 \"(+ 1 2 3) (* 6 7)\"\n```\n\n#### 11: support local variables\n支持变量定义，这个提交信息量比较多。我提前添加了许多以后要用到的语法关键词。minimanda 是用 let 语句来定义变量的。\n\n```sh\nassert 42 \"(let a 42) a\"\nassert 32 \"(let a 42) (let b 10) (- a b)\"\n```\n\n#### 12: add \u003c \u003e = \u003c= \u003e=\n支持比较运算。一股 C 味扑面而来。\n\n```sh\nassert 1 \"(\u003e 2 1)\"\nassert 0 \"(\u003c 2 1)\"\nassert 0 \"(\u003c= 2 1)\"\nassert 1 \"(\u003e= 2 1)\"\nassert 1 \"(= 1 1)\"\n```\n\n#### 13: add if\nif 语句的实现。非测试的代码增量有 50 行。codegen 使用一个静态变量来生成不同的标签。\n\n```sh\nassert 1 \"(if (\u003c 1 2) 1 2)\"\nassert 2 \"(if (\u003e 1 2) 1 2)\"\nassert 42 \"(if (\u003e 1 2) 1 (* 6 7))\"\n```\n\n#### 14: support set\n支持修改变量的值。\n```sh\nassert 42 \"(let i 1) (set i (* 6 7)) i\"\n```\n\n#### 15: add while\nminimanda 有 while 循环！（好像没什么奇怪的。）\n```sh\nassert 10 \"(let i 1) (while (\u003c i 10) (set i (+ i 1))) i\"\n```\n\n#### 16: addr (\u0026a) and deref (a.*)\n这指针如你所愿。\n\n```sh\nassert 42 \"(let i 42) (let b i) b\"\nassert 42 \"(let i 42) (let b \u0026i) b.*\"\nassert 42 \"(let i 42) (let b \u0026i) (let c \u0026b) c.*.*\"\n```\n\n#### 17: limit operator \u0026\n限制了 \u0026 的使用范围。只能用于变量。至于为什么，我忘记了。\n\n#### 18: add type\n添加了 int 类型和对应的指针类型。熟悉的 C 味。\n\n```sh\nassert 42 \"(let i :int 1) (set i (* 6 7)) i\"\nassert 10 \"(let i :int 1) (while (\u003c i 10) (set i (+ i 1))) i\"\nassert 42 \"(let i :int 42) (let b :int i) b\"\nassert 42 \"(let i :int 42) (let b :*int \u0026i) b.*\"\nassert 42 \"(let i :int 42) (let b :*int \u0026i) (let c :**int \u0026b) c.*.*\"\n```\n\n#### 19: add deref addr\n新增 deref addr 两个操作符作为 a.* \u0026a 的替代。后面写 LISP 解释器的时候会有用。\n\n```sh\nassert 42 \"(let i :int 42) (let b :*int (addr i)) (deref b)\"\n```\n\n#### 20: zero-args function call\n支持无参数的函数调用。还不支持定义函数，只能调用外部链接进来的 C 函数。\n```sh\n# 链接函数\nint ret3() { return 3; }\nint ret5() { return 5; }\n\n# 调用函数\nassert 3 \"(ret3)\"\nassert 5 \"(ret3) (ret5)\"\n```\n\n#### 21: support up to 6 args function call\n使用六个寄存器保存调用参数。\n\n```sh\nint add6(int a, int b, int c, int d, int e, int f) {\n    return a + b + c + d + e + f;\n}\n\nassert 136 \"(add6 1 2 3 4 5 (add6 6 7 8 9 10  (add6 11 12 13 14 15 16)))\"\nassert 21 \"(add6 1 2 (ret3) 4 (ret5) 6)\"\n```\n\n#### 22: add zero-arity function definition\n添加无参数的函数定义。\n\n```sh\nassert 42 \"(def main() -\u003e int \n                (ret0)) \n           (def ret0() -\u003e int 8)\"\n```\n\n嗯，minimanda 的函数长得有点随意。\n\n#### 23: up to 6 six args function definition\n支持六个参数的函数定义。\n\n```sh\nassert 42 \"(def main() -\u003e int \n                (add 20 22)) \n           (def add(a int b int) -\u003e int \n                (+ a b))\"\n```\n函数的类型前要不要带冒号，这个问题我想了很久，还是没定下来。\n\n#### 24: allow parsing array\n\n数组的内容比较多，拆成了两个 commit，这是第一个，支持对数组的 parsing。\n\n```sh\nassert 0 \"(def main() -\u003e int (let a: [32 int]) 0)\"\nassert 0 \"(def main() -\u003e int (let a: [32 *int]) 0)\"\nassert 0 \"(def main() -\u003e int (let a: *[32 int]) 0)\"\nassert 0 \"(def main() -\u003e int (let a :[32 int]) (let b :[32 int])  0)\"\nassert 0 \"(def main() -\u003e int \n            (let a :*[32 int]) \n            (let b :[32 *int])  \n            (let c :*[32 *[32 int]])  \n            0)\"\n```\n\n#### 25: add iset iget for array\n\n数据元素的 set 和 get。\n\n```sh\nassert 1 \"(def main() -\u003e int\n            (let a :[32 int])\n            (iset a 0 1)\n            (iset a 1 2)\n            (iget a 0))\"\n\nassert 42 \"(def main() -\u003e int\n            (let a :[32 int])\n            (let p :*[32 int] \u0026a)\n            (iset p.* 0 42)\n            (iget p.* 0))\"\n```\n#### 26: allow multi array\n多维数组。\n\n```sh\nassert 42 \"(def main() -\u003e int \n            (let a :[32 [32 int]]) \n            (iset (iget a 0) 0 42) \n            (iget (iget a 0) 0))\"\n```\n\n#### 27: add sizeof\n编译时计算的 sizeof，由于 minimanda 是类型严格的，sizeof 只支持传类型不支持传表达式。后面会实现 typeof 来获取表达式的类型。\n\n```sh\nassert 42 \"(def main() -\u003e int \n            (let a :[32 32 int]) \n            (iset (iget a 0) 0 42) \n            (iget (iget a 0) 0))\"\n\nassert 8 \"(def main() -\u003e int (sizeof *int))\"\nassert 40 \"(def main() -\u003e int (sizeof [5 *int]))\"\nassert 200 \"(def main() -\u003e int (sizeof [5 5 int]))\"\n```\n\n#### 28: add global variable without init\n全局变量声明，不支持同时初始化。\n\n```sh\nassert 0 \"(let g :int) (def main() -\u003e int g)\"\nassert 42 \"(let g :int) (def main() -\u003e int (set g 42) g)\"\nassert 42 \"(let g :int) (def main() -\u003e int (let g :int 42) g)\"\n```\n\n#### 29: add char \u0026\u0026 fix parse array bug\n支持 char，修bug。我忘记是什么了。\n\n```sh\nassert 1 \"(def main() -\u003e int (let c: char 1) c)\"\nassert 1 \"(def main() -\u003e int (sizeof char))\"\n```\n\n#### 30: add string literal\n支持 C 风格的字符串字面量，被实现为一个匿名全局数组。\n\n```sh\nassert 97 '(def main() -\u003e int (iget \"abc\" 0))'\nassert 98 '(def main() -\u003e int (iget \"abc\" 1))'\nassert 99 '(def main() -\u003e int (iget \"abc\" 2))'\nassert 0 '(def main() -\u003e int (iget \"abc\" 3))'\n```\n\n#### 31: support escape char\n支持转义字符。\n\n```sh\nassert 7 '(def main() -\u003e int (iget \"\\a\" 0))'\nassert 8 '(def main() -\u003e int (iget \"\\b\" 0))'\nassert 9 '(def main() -\u003e int (iget \"\\t\" 0))'\nassert 10 '(def main() -\u003e int (iget \"\\n\" 0))'\nassert 11 '(def main() -\u003e int (iget \"\\v\" 0))'\nassert 12 '(def main() -\u003e int (iget \"\\f\" 0))'\nassert 13 '(def main() -\u003e int (iget \"\\r\" 0))'\nassert 27 '(def main() -\u003e int (iget \"\\e\" 0))'\n```\n\n#### 32: read from file instead of stdin\n重构输入。\n\n#### 33: [refactor]\n重构，将 print 用 println 替代。\n\n#### 34: add -o and --help option\n和 chibicc 一样。\n\n#### 35: add ; comment\n添加 LISP 风格注释。\n```sh\nassert 42 \"(def main() -\u003e int \n            ; (let hahahh: NonExist)\n            42);\"\n\n assert 42 \";;; \n            (def main() -\u003e ;int \n            int\n            ; (let hahahh: NonExist)\n            42;;;;;;\n            );\"\n```\n\n#### 36: precompute line number\n没啥好说的。\n\n#### 37: emit .file .loc assembler directive\ncodegen 带文件名和行数，便于调试。\n\n#### 38: align local variable\n\n对齐局部变量的内存地址，后面如结构体等也需要对齐。\n\n\n#### 39: support defstruct; fix tokenize\n支持结构定义。\n\n```sh\nassert 12 \"(defstruct My gender int age int)\n           (def main() -\u003e int\n            (let a :My) \n            (set a.gender 1)\n            (set a.age 12)\n            a.age)\"\n```\n#### 40: add do\n相当于 scheme 中的 begin。\n```sh\nassert 3 \"(def main() -\u003e int (let a :int (do 1 2 3)) a)\"\nassert 3 \"(def main() -\u003e int (do 1 2 3))\"\n```\n\n#### 41: add variable scope\n添加词法作用域。实现方法与 chibicc 不同。\n\n```sh\nassert 1 \"(def ret (a int) -\u003e int a) (def main() -\u003e int (ret 1))\"\nassert 12 \"(def main() -\u003e int\n            (let a :int 1)\n            (let b :int 2)\n            (+ (do \n                (let c :int 9) \n                (let b :int 8)\n                (let a :int 7)\n                c)\n                a b))\"\n\nassert 3 \"(def main() -\u003e int \n            (let a :int 3)\n            (do (let a: int 42))\n            a)\"\n```\n\n#### 42: add struct scope\n作用域拓展到结构体。\n```sh\nassert 1 \"(defstruct Man age int)\n          (def main() -\u003e int\n            (let a :Man)\n            (set a.age 10)\n            (defstruct Man gender int)\n            (let b: Man)\n            (set b.gender 1)\n            b.gender)\"\n```\n\n#### 43: clean up dead code\n\n#### 44: add union\nC 风格的联合体。\n\n```sh\nassert 10 \"(defstruct S1 age int)\n           (defunion U1 gender int X S1)\n           (def main() -\u003e int\n              (let a: U1) \n              (set a.X.age 10)\n              a.gender)\"\n```\n\n#### 45: support struct/union assignment\n\n#### 46: change int size to 32bit\n\n```sh\nassert 26 \"(defstruct S A int B int C int D int)\n           (def main() -\u003e int\n             (let S :S)\n             (set S.A 10)\n             (+ (sizeof S) S.A))\"\n```\n\n#### 47: add long type\n```sh\nassert 8 \"(def main() -\u003e int (sizeof long))\"\n```\n\n#### 48: add short\n```sh\nassert 2 \"(def main() -\u003e int (sizeof short))\"\nassert 15 \"(def main() -\u003e int \n             (+ (sizeof short)\n                (sizeof int) \n                (sizeof char)\n                (sizeof long)))\"\n```\n\n#### 49: add deftype\n类似 C 的 typedef。\n\n```sh\nassert 28 \"(def main() -\u003e int\n            (defstruct Man gender int age int)\n            (deftype X Man)\n            (let c :X)\n            (set c.age 20)\n            (+ (sizeof X) c.age))\"\n```\n\n#### 50: add typeof\n\n奇妙的 typeof，后面发现 chibicc 也实现了。\n\n```sh\nassert 36 \"(def main() -\u003e int\n            (defstruct X gender int age int)\n            (let x :X)\n            (let y :(typeof x))\n            (set y.age 20)\n            (+ (sizeof X) (sizeof (typeof y)) y.age))\"\n\nassert 20 \"(def main() -\u003e int (* 5 (sizeof (typeof 1))))\"\n```\n\n#### 51: Use 32 bit registers for char, short and int. But why?\n\n现在我知道答案了。那就是——\n\n#### 52: add type cast\n类型转换！\n```sh\nassert 0 \"(def main() -\u003e int \n            (let x :int 256) \n            (cast x char))\"\nassert 1 \"(def main() -\u003e int 8590066177)\"\n```\n\ncodegen.c 的这个表就是上面问题的答案。\n```sh\nstatic char* cast_table[][4] = {\n  {NULL,  NULL,   NULL, i32i64}, // i8\n  {i32i8, NULL,   NULL, i32i64}, // i16\n  {i32i8, i32i16, NULL, i32i64}, // i32\n  {i32i8, i32i16, NULL, NULL},   // i64\n};\n```\n\n#### 53: a lossy boolean type\nminimanda 有原生的 bool 类型。\n\n```sh\nassert 2 \"(def main() -\u003e int (let x :bool true) (+ (sizeof bool) (cast x bool)))\"\nassert 0 \"(def main() -\u003e int (let x :bool false) (cast x bool))\"\n```\n\n说得跟真的一样。\n\n#### 54: support 16-base 8-base 2-base integer literal\n\nLisp 风格的二进制、八进制和十六进制常量。\n\n```sh\nassert 42 \"(def main() -\u003e int (+ #x10 #o17 #b1000 #b11))\"\nassert 4 \"(def main() -\u003e int #b100)\"\nassert 97 \"(def main() -\u003e int (let x :char 'a') x)\"\n```\n\n#### 55: add not\n```sh\nassert 0 \"(def main() -\u003e int (not true))\"\n```\n\n#### 56: add bitnot\n```sh\nassert 250 \"(def main() -\u003e int (bitnot #b101))\"\n```\n之所以是 250，是因为 BASH 返回值只有一个字节。如果是 4 个字节的 int 则不是这个结果。\n\n#### 57: add mod bitand bitor bitxor\n\n```sh\nassert 4 \"(def main() -\u003e int (bitxor #b010 #b110))\"\nassert 0 \"(def main() -\u003e int (bitand #b1111 #b111 0))\"\nassert 11 \"(def main() -\u003e int (bitor #b1010 #b0010 #b10 #b1))\"\nassert 1 \"(def main() -\u003e int (bitand #b1111 #b111 #b11 #b1))\"\nassert 0 \"(def main() -\u003e int (mod 18 6))\"\nassert 5 \"(def main() -\u003e int (mod 17 6))\"\nassert 1 \"(def main() -\u003e int (mod 3 2))\"\n```\n\n#### 58: add logand logor\n```sh\nassert 0 \"(def main() -\u003e int (and true false true))\"\nassert 1 \"(def main() -\u003e int (or false false true))\"\n```\n\n#### 59: support near compose for compare operators\n对 \u003e \u003c 这种操作也添加无限参数支持。\n\n```sh\nassert 1 \"(def main() -\u003e int \n            (let age :int 20)\n            (\u003c 0 age 100))\"\nassert 1 \"(def main() -\u003e int (\u003e 10 2 1 0))\"\nassert 0 \"(def main() -\u003e int (\u003c 10 2 1 0))\"\n```\n\n#### 60: add sra srl sll\n\n```sh\nassert 248 \"(def main() -\u003e int (sra (- 0 32) 2))\"\nassert 42 \"(def main() -\u003e int (- (sll 2 5) (srl 44 1)))\"\nassert 32 \"(def main() -\u003e int (sll 1 5))\"\n```\n\n#### 61: support simple array literal\n支持数据字面量。相当于 C 的 {1, 2, 3}。\n\n```sh\nassert 42 \"(def main() -\u003e int\n              (let a :[2 int] #a[22 20])\n              (let b :[1 int] #a[0])\n              (+ (iget a 0) (iget a 1) (iget b 0)))\"\n```\n\n#### 62: set support simple array literal\nset 操作也支持字面量\n\n```sh\nassert 5 \"(def main() -\u003e int\n             (let c :[2 2 int])\n             (set (iget c 0) #a[1 2])\n             (set (iget c 1) #a[3 4])\n             (+ (iget (iget c 0) 1)\n                (iget (iget c 1) 0)))\"\n```\n\n#### 63: iget support left compose\n相当甜的糖。\n```sh\nassert 5 \"(def main() -\u003e int\n             (let c :[2 2 int])\n             (set (iget c 0) #a[1 2])\n             (set (iget c 1) #a[3 4])\n             (+ (iget c 0 1)\n                (iget c 1 0)))\"\n```\n\n#### 64: refactor\n\n#### 65: support compose literal\n支持字面量嵌套。\n\n```sh\nassert 5 \"(def main() -\u003e int\n             (let c :[5 5 int] \n              #a[#a[1 0 0 0 0]\n                 #a[0 1 0 0 0]\n                 #a[0 0 1 0 0]\n                 #a[0 0 0 1 0]\n                 #a[0 0 0 0 1]])\n             (+ (iget c 0 0)\n                (iget c 1 1)\n                (iget c 2 2)\n                (iget c 3 3)\n                (iget c 4 4)))\"\n```\n\n#### 66: support string literal\n\n```sh\nassert 97 '(def main() -\u003e int\n            (let a :[5 char] \"abcd\")\n            (iget a 0))'\n```\n\n\n#### 67: support string compose literal; it's time to test in C\n\n支持字符串和数据字面量组合。\n\n```sh\nassert 147 '(def main() -\u003e int\n            (let a :[2 5 char] #a(\"ab\" \"cd\"))\n            (let b :[2 5 char] #a(\"ef\" \"gh\"))\n            (+ (iget a 0 0) (iget a 1 1)\n               (iget b 0 1) (iget b 1 1)\n               ))'\n```\n\n开始着手把测试搬到 C 上，chibicc 很早就实现了，我拖着是因为我解决不了宏的问题。现在，要解决它了！\n\n#### 68: test0: simple test glue\n由于没有宏，写一个测试是如此繁琐。我必须把输入的表达式写两遍。\n\n```sh\n(def main() -\u003e int\n  (assert 0 0 \"0\")\n  (assert 42 42 \"42\")\n  (assert 42 (+ 20 22) \"(+ 20 22)\")\n  0\n)\n```\n\n#### 69: macro system start\n\n经过一周的思考和实验，宏系统有了雏形。这次提交代码增量为 473+，102-。\n\n宏系统的输入的 S 表达式，输出为 Node。作用在 lexer 和 parser 之间。\n\n```lisp\n(defmacro ASSERT (actual expected)\n  (assert actual expected (str expected)))\n\n(def main() -\u003e int\n  (ASSERT 0 0)\n  (ASSERT 42 42)\n  0\n)\n```\n\n宏系统包含了一个 S-表达式的 parser，以及一个小型的类 LISP 解释器。恐繁不述。\n\n从第 70 到第 99 次提交，都是逐渐拓展宏系统，使之支持我们上述所涉及的所有的表达式。\n\n解释器方式实现的宏系统比 C 语言的字符器替换的宏系统更加灵活，不易出错。不过我只实现了一个宏 str 就是了。\n\n#### 100: merge macro.c and parse.c\n\n第 100 个提交，将原来的 parser 完全抛弃，不管使用不使用宏，都是先 lexer 转 S-表达式，再转到 Node。\n\n这次提交的代码增量为: 725 additions and 1,372 deletions.\n\n我也不知道为什么刚好提交的次数是 100。","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiriusdemon%2Fmemorandum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsiriusdemon%2Fmemorandum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsiriusdemon%2Fmemorandum/lists"}