903
|
1 |
// Author: Zhuo Ying Jiang Li
|
|
2 |
// Starting code by Dr Christian Urban
|
|
3 |
|
|
4 |
//
|
|
5 |
// Use amm compiler.sc XXX.fun
|
|
6 |
// ./XXX
|
|
7 |
// This will generate XXX.ll, XXX.o as well as the binary program.
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
8 |
//
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
9 |
|
903
|
10 |
// lexer + parser
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
11 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
12 |
import $file.fun_tokens, fun_tokens._
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
13 |
import $file.fun_parser, fun_parser._
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
14 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
15 |
// for generating new labels
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
16 |
var counter = -1
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
17 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
18 |
def Fresh(x: String) = {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
19 |
counter += 1
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
20 |
x ++ "_" ++ counter.toString()
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
21 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
22 |
|
903
|
23 |
// typing
|
|
24 |
type Ty = String
|
|
25 |
type TyEnv = Map[String, Ty]
|
|
26 |
|
|
27 |
// initial typing environment
|
|
28 |
val initialEnv = Map[String, Ty]("skip" -> "Void", "print_int" -> "Void", "print_char" -> "Void",
|
|
29 |
"print_space" -> "Void", "print_star" -> "Void", "new_line" -> "Void")
|
|
30 |
|
|
31 |
val typeConversion = Map("Int" -> "i32", "Double" -> "double", "Void" -> "void")
|
|
32 |
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
33 |
// Internal CPS language for FUN
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
34 |
abstract class KExp
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
35 |
abstract class KVal
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
36 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
37 |
case class KVar(s: String, ty: Ty = "UNDEF") extends KVal
|
903
|
38 |
case class KConst(s: String, ty: Ty = "UNDEF") extends KVal
|
|
39 |
case class KNum(i: Int) extends KVal // known type
|
|
40 |
case class KFNum(d: Float) extends KVal // known type
|
|
41 |
case class KChConst(c: Int) extends KVal // known type
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
42 |
case class Kop(o: String, v1: KVal, v2: KVal, ty: Ty = "UNDEF") extends KVal
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
43 |
case class KCall(o: String, vrs: List[KVal], ty: Ty = "UNDEF") extends KVal
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
44 |
|
903
|
45 |
case class KLet(x: String, e1: KVal, e2: KExp) extends KExp {
|
|
46 |
override def toString = s"LET $x = $e1 in \n$e2"
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
47 |
}
|
903
|
48 |
case class KIf(x1: String, e1: KExp, e2: KExp) extends KExp {
|
|
49 |
def pad(e: KExp) = e.toString.replaceAll("(?m)^", " ")
|
|
50 |
|
|
51 |
override def toString =
|
|
52 |
s"IF $x1\nTHEN\n${pad(e1)}\nELSE\n${pad(e2)}"
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
53 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
54 |
case class KReturn(v: KVal) extends KExp
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
55 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
56 |
// CPS translation from Exps to KExps using a
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
57 |
// continuation k.
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
58 |
def CPS(e: Exp)(k: KVal => KExp) : KExp = e match {
|
903
|
59 |
case Var(s) => {
|
|
60 |
if (s.head.isUpper) { // if this variable is a global
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
61 |
val z = Fresh("tmp")
|
903
|
62 |
KLet(z, KConst(s), k(KVar(z)))
|
|
63 |
} else k(KVar(s))
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
64 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
65 |
case Num(i) => k(KNum(i))
|
903
|
66 |
case FNum(d) => k(KFNum(d))
|
|
67 |
case ChConst(c) => k(KChConst(c))
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
68 |
case Aop(o, e1, e2) => {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
69 |
val z = Fresh("tmp")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
70 |
CPS(e1)(y1 =>
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
71 |
CPS(e2)(y2 => KLet(z, Kop(o, y1, y2), k(KVar(z)))))
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
72 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
73 |
case If(Bop(o, b1, b2), e1, e2) => {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
74 |
val z = Fresh("tmp")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
75 |
CPS(b1)(y1 =>
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
76 |
CPS(b2)(y2 =>
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
77 |
KLet(z, Kop(o, y1, y2), KIf(z, CPS(e1)(k), CPS(e2)(k)))))
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
78 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
79 |
case Call(name, args) => {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
80 |
def aux(args: List[Exp], vs: List[KVal]) : KExp = args match {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
81 |
case Nil => {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
82 |
val z = Fresh("tmp")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
83 |
KLet(z, KCall(name, vs), k(KVar(z)))
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
84 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
85 |
case e::es => CPS(e)(y => aux(es, vs ::: List(y)))
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
86 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
87 |
aux(args, Nil)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
88 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
89 |
case Sequence(e1, e2) =>
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
90 |
CPS(e1)(_ => CPS(e2)(y2 => k(y2)))
|
903
|
91 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
92 |
|
903
|
93 |
// initial continuation
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
94 |
def CPSi(e: Exp) = CPS(e)(KReturn)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
95 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
96 |
|
903
|
97 |
// get type of KVal
|
|
98 |
def get_typ_val(v: KVal) : Ty = v match {
|
|
99 |
case KNum(i) => "Int"
|
|
100 |
case KFNum(d) => "Double"
|
|
101 |
case KChConst(i) => "Int"
|
|
102 |
case KVar(name, ty) => ty
|
|
103 |
case KConst(name, ty) => ty
|
|
104 |
case Kop(o, v1, v2, ty) => ty
|
|
105 |
case KCall(o, vrs, ty) => ty
|
|
106 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
107 |
|
903
|
108 |
// update type information for KValues
|
|
109 |
def typ_val(v: KVal, ts: TyEnv) : KVal = v match {
|
|
110 |
case KVar(name, ty) => {
|
|
111 |
if (ts.contains(name)) {
|
|
112 |
KVar(name, ts(name))
|
|
113 |
} else throw new Exception(s"Compile error: unknown type for $name")
|
|
114 |
}
|
|
115 |
case KConst(name, ty) => {
|
|
116 |
if (ts.contains(name)) {
|
|
117 |
KConst(name, ts(name))
|
|
118 |
} else throw new Exception(s"Compile error: unknown type for $name")
|
|
119 |
}
|
|
120 |
case Kop(o, v1, v2, ty) => {
|
|
121 |
val tv1 = typ_val(v1, ts)
|
|
122 |
val tv2 = typ_val(v2, ts)
|
|
123 |
val t1 = get_typ_val(tv1)
|
|
124 |
val t2 = get_typ_val(tv2)
|
|
125 |
if (t1 != t2) throw new Exception(s"Compile error: cannot compare $t1 with $t2")
|
|
126 |
Kop(o, tv1, tv2, t1)
|
|
127 |
}
|
|
128 |
case KCall(o, vrs, ty) => {
|
|
129 |
val new_vrs = vrs.map(vr => typ_val(vr, ts))
|
|
130 |
if (ts.contains(o)) {
|
|
131 |
KCall(o, new_vrs, ts(o))
|
|
132 |
} else throw new Exception(s"Compile error: unknown type for $o")
|
|
133 |
}
|
|
134 |
case x => x // no changes: KNum, KFNum, KChConst
|
|
135 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
136 |
|
903
|
137 |
// update type information for KExpressions
|
|
138 |
def typ_exp(a: KExp, ts: TyEnv) : KExp = a match {
|
|
139 |
case KLet(x, e1, e2) => {
|
|
140 |
val te1 = typ_val(e1, ts)
|
|
141 |
val env1 = ts + (x -> get_typ_val(te1))
|
|
142 |
val te2 = typ_exp(e2, env1)
|
|
143 |
KLet(x, te1, te2)
|
|
144 |
}
|
|
145 |
case KIf(x1, e1, e2) => KIf(x1, typ_exp(e1, ts), typ_exp(e2, ts))
|
|
146 |
case KReturn(v) => KReturn(typ_val(v, ts))
|
|
147 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
148 |
|
903
|
149 |
// prelude
|
|
150 |
val prelude = """
|
|
151 |
declare i32 @printf(i8*, ...)
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
152 |
|
903
|
153 |
@.str_nl = private constant [2 x i8] c"\0A\00"
|
|
154 |
@.str_star = private constant [2 x i8] c"*\00"
|
|
155 |
@.str_space = private constant [2 x i8] c" \00"
|
|
156 |
@.str_int = private constant [3 x i8] c"%d\00"
|
|
157 |
@.str_c = private constant [3 x i8] c"%c\00"
|
|
158 |
|
|
159 |
define void @new_line() #0 {
|
|
160 |
%t0 = getelementptr [2 x i8], [2 x i8]* @.str_nl, i32 0, i32 0
|
|
161 |
call i32 (i8*, ...) @printf(i8* %t0)
|
|
162 |
ret void
|
|
163 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
164 |
|
903
|
165 |
define void @print_star() #0 {
|
|
166 |
%t0 = getelementptr [2 x i8], [2 x i8]* @.str_star, i32 0, i32 0
|
|
167 |
call i32 (i8*, ...) @printf(i8* %t0)
|
|
168 |
ret void
|
|
169 |
}
|
|
170 |
|
|
171 |
define void @print_space() #0 {
|
|
172 |
%t0 = getelementptr [2 x i8], [2 x i8]* @.str_space, i32 0, i32 0
|
|
173 |
call i32 (i8*, ...) @printf(i8* %t0)
|
|
174 |
ret void
|
|
175 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
176 |
|
903
|
177 |
define void @print_int(i32 %x) {
|
|
178 |
%t0 = getelementptr [3 x i8], [3 x i8]* @.str_int, i32 0, i32 0
|
|
179 |
call i32 (i8*, ...) @printf(i8* %t0, i32 %x)
|
|
180 |
ret void
|
|
181 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
182 |
|
903
|
183 |
define void @print_char(i32 %x) {
|
|
184 |
%t0 = getelementptr [3 x i8], [3 x i8]* @.str_c, i32 0, i32 0
|
|
185 |
call i32 (i8*, ...) @printf(i8* %t0, i32 %x)
|
|
186 |
ret void
|
|
187 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
188 |
|
903
|
189 |
define void @skip() #0 {
|
|
190 |
ret void
|
|
191 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
192 |
|
903
|
193 |
; END OF BUILT-IN FUNCTIONS (prelude)
|
|
194 |
"""
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
195 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
196 |
// convenient string interpolations
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
197 |
// for instructions, labels and methods
|
920
|
198 |
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
199 |
|
920
|
200 |
extension (sc: StringContext) {
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
201 |
def i(args: Any*): String = " " ++ sc.s(args:_*) ++ "\n"
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
202 |
def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
203 |
def m(args: Any*): String = sc.s(args:_*) ++ "\n"
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
204 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
205 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
206 |
// mathematical and boolean operations
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
207 |
def compile_op(op: String) = op match {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
208 |
case "+" => "add i32 "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
209 |
case "*" => "mul i32 "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
210 |
case "-" => "sub i32 "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
211 |
case "/" => "sdiv i32 "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
212 |
case "%" => "srem i32 "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
213 |
case "==" => "icmp eq i32 "
|
903
|
214 |
case "!=" => "icmp ne i32 "
|
|
215 |
case "<=" => "icmp sle i32 "
|
|
216 |
case "<" => "icmp slt i32 "
|
|
217 |
case ">=" => "icmp sge i32 "
|
|
218 |
case ">" => "icmp sgt i32 "
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
219 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
220 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
221 |
def compile_dop(op: String) = op match {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
222 |
case "+" => "fadd double "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
223 |
case "*" => "fmul double "
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
224 |
case "-" => "fsub double "
|
903
|
225 |
case "/" => "fdiv double "
|
|
226 |
case "%" => "frem double "
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
227 |
case "==" => "fcmp oeq double "
|
903
|
228 |
case "!=" => "fcmp one double "
|
|
229 |
case "<=" => "fcmp ole double "
|
|
230 |
case "<" => "fcmp olt double "
|
|
231 |
case ">=" => "icmp sge double "
|
|
232 |
case ">" => "icmp sgt double "
|
|
233 |
}
|
|
234 |
|
|
235 |
def compile_args(vrs: List[KVal]) : List[String] = vrs match {
|
|
236 |
case Nil => Nil
|
|
237 |
case x::xs => s"${typeConversion(get_typ_val(x))} ${compile_val(x)}" :: compile_args(xs)
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
238 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
239 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
240 |
// compile K values
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
241 |
def compile_val(v: KVal) : String = v match {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
242 |
case KNum(i) => s"$i"
|
903
|
243 |
case KFNum(d) => s"$d"
|
|
244 |
case KChConst(i) => s"$i" // as integer
|
|
245 |
case KVar(s, ty) => s"%$s"
|
|
246 |
case KConst(s, ty) => {
|
|
247 |
val t = typeConversion(ty)
|
|
248 |
s"load $t, $t* @$s"
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
249 |
}
|
903
|
250 |
case Kop(op, x1, x2, ty) => {
|
|
251 |
if (ty == "Double") {
|
|
252 |
s"${compile_dop(op)} ${compile_val(x1)}, ${compile_val(x2)}"
|
|
253 |
} else if (ty == "Int") {
|
|
254 |
s"${compile_op(op)} ${compile_val(x1)}, ${compile_val(x2)}"
|
|
255 |
} else throw new Exception("Compile error: unknown type for comparison")
|
|
256 |
}
|
|
257 |
case KCall(x1, args, ty) => {
|
|
258 |
s"call ${typeConversion(ty)} @$x1 (${compile_args(args).mkString(", ")})"
|
|
259 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
260 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
261 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
262 |
// compile K expressions
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
263 |
def compile_exp(a: KExp) : String = a match {
|
903
|
264 |
case KReturn(v) => {
|
|
265 |
val ty = get_typ_val(v)
|
|
266 |
if (ty == "Void") {
|
|
267 |
i"ret void"
|
|
268 |
} else {
|
|
269 |
i"ret ${typeConversion(ty)} ${compile_val(v)}"
|
|
270 |
}
|
|
271 |
}
|
|
272 |
case KLet(x: String, v: KVal, e: KExp) => {
|
|
273 |
val tv = get_typ_val(v)
|
|
274 |
if (tv == "Void") {
|
|
275 |
i"${compile_val(v)}" ++ compile_exp(e)
|
|
276 |
} else i"%$x = ${compile_val(v)}" ++ compile_exp(e)
|
|
277 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
278 |
case KIf(x, e1, e2) => {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
279 |
val if_br = Fresh("if_branch")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
280 |
val else_br = Fresh("else_branch")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
281 |
i"br i1 %$x, label %$if_br, label %$else_br" ++
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
282 |
l"\n$if_br" ++
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
283 |
compile_exp(e1) ++
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
284 |
l"\n$else_br" ++
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
285 |
compile_exp(e2)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
286 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
287 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
288 |
|
903
|
289 |
def compile_def_args(args: List[(String, String)], ts: TyEnv) : (List[String], TyEnv) = args match {
|
|
290 |
case Nil => (Nil, ts)
|
|
291 |
case (n, t)::xs => {
|
|
292 |
if (t == "Void") throw new Exception("Compile error: argument of type void is invalid")
|
|
293 |
val (rest, env) = compile_def_args(xs, ts + (n -> t))
|
|
294 |
(s"${typeConversion(t)} %$n" :: rest, env)
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
295 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
296 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
297 |
|
903
|
298 |
def compile_decl(d: Decl, ts: TyEnv) : (String, TyEnv) = d match {
|
|
299 |
case Const(name, value) => {
|
|
300 |
(m"@$name = global i32 $value\n", ts + (name -> "Int"))
|
|
301 |
}
|
|
302 |
case FConst(name, value) => {
|
|
303 |
(m"@$name = global double $value\n", ts + (name -> "Double"))
|
|
304 |
}
|
|
305 |
case Def(name, args, ty, body) => {
|
|
306 |
val (argList, env1) = compile_def_args(args, ts + (name -> ty))
|
|
307 |
(m"define ${typeConversion(ty)} @$name (${argList.mkString(", ")}) {" ++
|
|
308 |
compile_exp(typ_exp(CPSi(body), env1)) ++
|
|
309 |
m"}\n", ts + (name -> ty)) // don't preserve local variables in environment
|
|
310 |
}
|
|
311 |
case Main(body) => {
|
|
312 |
(m"define i32 @main() {" ++
|
|
313 |
compile_exp(typ_exp(CPS(body)(_ => KReturn(KNum(0))), ts + ("main" -> "Int"))) ++
|
|
314 |
m"}\n", ts + ("main" -> "Int"))
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
315 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
316 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
317 |
|
903
|
318 |
// recursively update the typing environment while compiling
|
|
319 |
def compile_block(prog: List[Decl], ts: TyEnv) : (String, TyEnv) = prog match {
|
|
320 |
case Nil => ("", ts)
|
|
321 |
case x::xs => {
|
|
322 |
val (compiled, env) = compile_decl(x, ts)
|
|
323 |
val (compiled_block, env1) = compile_block(xs, env)
|
|
324 |
(compiled ++ compiled_block, env1)
|
|
325 |
}
|
|
326 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
327 |
|
903
|
328 |
def fun_compile(prog: List[Decl]) : String = {
|
|
329 |
val tyenv = initialEnv
|
|
330 |
val (compiled, _) = compile_block(prog, tyenv)
|
|
331 |
prelude ++ compiled
|
|
332 |
}
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
333 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
334 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
335 |
@main
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
336 |
def main(fname: String) = {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
337 |
val path = os.pwd / fname
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
338 |
val file = fname.stripSuffix("." ++ path.ext)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
339 |
val tks = tokenise(os.read(path))
|
903
|
340 |
val ast = parse_tks(tks).head
|
|
341 |
val code = fun_compile(ast)
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
342 |
println(code)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
343 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
344 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
345 |
@main
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
346 |
def write(fname: String) = {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
347 |
val path = os.pwd / fname
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
348 |
val file = fname.stripSuffix("." ++ path.ext)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
349 |
val tks = tokenise(os.read(path))
|
903
|
350 |
val ast = parse_tks(tks).head
|
|
351 |
val code = fun_compile(ast)
|
864
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
352 |
//println(code)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
353 |
os.write.over(os.pwd / (file ++ ".ll"), code)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
354 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
355 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
356 |
@main
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
357 |
def run(fname: String) = {
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
358 |
val path = os.pwd / fname
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
359 |
val file = fname.stripSuffix("." ++ path.ext)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
360 |
write(fname)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
361 |
os.proc("llc", "-filetype=obj", file ++ ".ll").call()
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
362 |
os.proc("gcc", file ++ ".o", "-o", file ++ ".bin").call()
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
363 |
os.proc(os.pwd / (file ++ ".bin")).call(stdout = os.Inherit)
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
364 |
println(s"done.")
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
365 |
}
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
366 |
|
Christian Urban <christian.urban@kcl.ac.uk>
parents:
diff
changeset
|
367 |
|
921
|
368 |
// for automated testing
|
|
369 |
|
|
370 |
@main
|
|
371 |
def test(fname: String) = write(fname)
|