| 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)
 |