[swarthmore cs75] Compiler 1 – Adder
作者:互联网
课程回顾
Swarthmore学院16年开的编译系统课,总共10次大作业。本随笔记录了相关的课堂笔记以及第3次大作业。
- 编译的过程:
- 具体语法树:
- 抽象语法树:
- 汇编代码生成(Add1、Sub1):
- 汇编代码生成(Let、Id):
- 实际案例:
编程作业
本次大作业是为Adder编程语言实现一个小型编译器,将Adder程序编译为X86_32汇编。
具体语法(Concrete Syntax):
<expr> := | <number> | <identifier> | let <bindings> in <expr> | add1(<expr>) | sub1(<expr>) <bindings> := | <identifier> = <expr> | <identifier> = <expr>, <bindings> }
抽象语法 (Abstract Syntax):
type prim1 = | Add1 | Sub1 type expr = | Number of int | Prim1 of prim1 * expr | Let of (string * expr) list * expr | Id of string
程序例子:
Concrete Syntax Abstract Syntax Answer 5 Number(5) 5 let x=(let y=10 in y) in x Let(["x", Let(["y", 10], Id("y"))], Id("x")) 10 let x=100 in let x=1 in x Let(["x", Number(100), Let(["x", 1], Id("x"))]) 1 let x = 5 in add1(x) Let([("x", Number(5))], Prim1(Add1, Id("x"))) 6 let x=1 in let y=add1(x) in y Let(["x", 1], Let(["y", Prim1(Add1, Id("x"))], Id("y"))) 2 sub1(add1(sub1(5))) Prim1(Sub1, Prim1(Add1, Prim1(Sub1, Number(5)))) 4 let x=10 in let y=add1(x) in add1(y) Let(["x", 10], Let(["y", Prime1(Add1, Id("x"))], Prime1(Add1, Id("y")))) 12 let x = 5, y = sub1(x) in sub1(y) Let([("x", Number(5)), ("y", Prim1(Sub1, Id("x")))], Prim1(Sub1, Id("y"))) 3 let x=1 in let x=add1(let x=6 in add1(x)) in x Let(["x", 1], Let(["x", Prim1(Add1, Let(["x", 6], Prim1(Add1, Id("x"))))], Id("x"))) 8 let y=sub1(add1(sub1(let x=5 in x))) in add1(add1(add1(y))) Let(["y", Prim1(Sub1, Prim1(Add1, Prim1(Sub1, Let(["x", Number(5)], Id("x")))))], Prim1(Add1, Prim1(Add1, Prim1(Add1, Id("y"))))) 7 let x=1 in y -
An identifier is unbound (there is no surrounding let binding for y) let x=10, y=20, x=5, y=30, x=40 in x -
There is a binding list containing two or more bindings with the same name 实现一(Let使用尾递归):
分别实现Let、Id、Prim1的代码生成逻辑。因为Adder语法规定,不允许在一个binding list绑定两个相同的变量,所以这里简单的实现了一个has_unique_key方法,判断binding list的长度和去重后的长度是否相同。let rec compile_env (p : expr) (stack_index : int) (env : (string * int) list) : instruction list = match p with | Number(n) -> [ IMov(Reg(EAX), Const(n)) ] | Let(binds, body) -> let rec helper xs si env = match xs with | [] -> compile_env body (si + 1) env | (id, expr)::rest -> let new_env = (id, (-4) * si)::env in (compile_env expr si env) @ [IMov(RegOffset((-4) * si, ESP), Reg(EAX))] @ helper rest (si + 1) new_env in if has_unique_key binds then helper binds stack_index env else failwith "There is a binding list containing two or more bindings with the same name." | Id(x) -> [ match (find env x) with | Some(n) -> IMov(Reg(EAX), RegOffset(n, ESP)) | None -> failwith ("An identifier is unbound (there is no surrounding let binding for " ^ x ^ " )") ] | Prim1(op, e) -> match op with | Add1 -> (compile_env e stack_index env) @ [IAdd(Reg(EAX), Const(1))] | Sub1 -> (compile_env e stack_index env) @ [ISub(Reg(EAX), Const(1))]
实现二(Let使用迭代):
本实现仅改写了Let的模式匹配逻辑,其他类型的pattern mathcing和上述代码一样。let new_env = List.mapi (fun si (x, _) -> x, (-4) * (stack_index + si)) binds @ env in let f si (_, e) -> (compile_env e stack_index new_env) @ [IMov(RegOffset((-4) * (stack_index + si), ESP), Reg(EAX))] in let exc = List.flatten (List.mapi f binds) in let exb = compile_env body (stack_index + 1) (new_env) in exc @ exb
有一个地方需要注意一下,比如在对表达式:
let x=10, y=sub1(x) in y
进行求值的时候,会执行上述代码并会返回:
new_env = [("x", -4); ("y", -8)]
exc = List.flatten (List.mapi f [("x", Number(10)), ("y", Prim1(Sub1, Id("x")))])- 在计算Number(10)的时候,compile_env只需要传入env,而不是 [("x", -4); ("y", -8)] @ env
标签:Add1,swarthmore,let,Adder,env,Let,cs75,Id,Prim1 来源: https://www.cnblogs.com/dm1299/p/10355503.html