diff -r 8e63f9745f46 -r e50096adda15 progs/fun-bare.scala --- a/progs/fun-bare.scala Sun Jul 28 01:00:41 2019 +0100 +++ b/progs/fun-bare.scala Sun Jul 28 14:24:46 2019 +0100 @@ -1,4 +1,5 @@ -// A Small Compiler for a simple functional language +// A Small Compiler for a Simple Functional Language +// (it does not use a parser and lexer) // Abstract syntax trees abstract class Exp @@ -69,83 +70,92 @@ x ++ "_" ++ counter.toString() } +// convenient string interpolations +// for instructions, labels and methods +import scala.language.implicitConversions +import scala.language.reflectiveCalls + +implicit def sring_inters(sc: StringContext) = new { + def i(args: Any*): String = " " ++ sc.s(args:_*) ++ "\n" + def l(args: Any*): String = sc.s(args:_*) ++ ":\n" + def m(args: Any*): String = sc.s(args:_*) ++ "\n" +} type Env = Map[String, Int] -type Instrs = List[String] // compile expressions -def compile_exp(a: Exp, env : Env) : Instrs = a match { - case Num(i) => List("ldc " + i.toString + "\n") - case Var(s) => List("iload " + env(s).toString + "\n") - case Aop("+", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("iadd\n") - case Aop("-", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("isub\n") - case Aop("*", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("imul\n") - case Aop("/", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("idiv\n") - case Aop("%", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("irem\n") +def compile_exp(a: Exp, env : Env) : String = a match { + case Num(i) => i"ldc $i" + case Var(s) => i"iload ${env(s)}" + case Aop("+", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"iadd" + case Aop("-", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"isub" + case Aop("*", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"imul" + case Aop("/", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"idiv" + case Aop("%", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"irem" case If(b, a1, a2) => { val if_else = Fresh("If_else") val if_end = Fresh("If_end") compile_bexp(b, env, if_else) ++ compile_exp(a1, env) ++ - List("goto " + if_end + "\n") ++ - List("\n" + if_else + ":\n\n") ++ + i"goto $if_end" ++ + l"$if_else" ++ compile_exp(a2, env) ++ - List("\n" + if_end + ":\n\n") + l"$if_end" } - case Call(n, args) => { + case Call(name, args) => { val is = "I" * args.length - args.flatMap(a => compile_exp(a, env)) ++ - List ("invokestatic XXX/XXX/" + n + "(" + is + ")I\n") + args.map(a => compile_exp(a, env)).mkString ++ + i"invokestatic XXX/XXX/$name($is)I" } case Sequ(a1, a2) => { - compile_exp(a1, env) ++ List("pop\n") ++ compile_exp(a2, env) + compile_exp(a1, env) ++ i"pop" ++ compile_exp(a2, env) } case Write(a1) => { compile_exp(a1, env) ++ - List("dup\n", - "invokestatic XXX/XXX/write(I)V\n") + i"dup" ++ + i"invokestatic XXX/XXX/write(I)V" } } // compile boolean expressions -def compile_bexp(b: BExp, env : Env, jmp: String) : Instrs = b match { +def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match { case Bop("==", a1, a2) => - compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("if_icmpne " + jmp + "\n") + compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpne $jmp" case Bop("!=", a1, a2) => - compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("if_icmpeq " + jmp + "\n") + compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpeq $jmp" case Bop("<", a1, a2) => - compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("if_icmpge " + jmp + "\n") + compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpge $jmp" case Bop("<=", a1, a2) => - compile_exp(a1, env) ++ compile_exp(a2, env) ++ List("if_icmpgt " + jmp + "\n") + compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpgt $jmp" } // compile function for declarations and main -def compile_decl(d: Decl) : Instrs = d match { +def compile_decl(d: Decl) : String = d match { case Def(name, args, a) => { val env = args.zipWithIndex.toMap val is = "I" * args.length - List(".method public static " + name + "(" + is + ")I \n", - ".limit locals " + args.length.toString + "\n", - ".limit stack " + (1 + max_stack_exp(a)).toString + "\n", - name + "_Start:\n") ++ + m".method public static $name($is)I" ++ + m".limit locals ${args.length.toString}" ++ + m".limit stack ${1 + max_stack_exp(a)}" ++ + l"${name}_Start" ++ compile_exp(a, env) ++ - List("ireturn\n", - ".end method \n\n") + i"ireturn" ++ + m".end method\n" } case Main(a) => { - List(".method public static main([Ljava/lang/String;)V\n", - ".limit locals 200\n", - ".limit stack 200\n") ++ + m".method public static main([Ljava/lang/String;)V" ++ + m".limit locals 200" ++ + m".limit stack 200" ++ compile_exp(a, Map()) ++ - List("invokestatic XXX/XXX/write(I)V\n", - "return\n", - ".end method\n") + i"invokestatic XXX/XXX/write(I)V" ++ + i"return" ++ + m".end method\n" } } // main compilation function def compile(prog: List[Decl], class_name: String) : String = { - val instructions = prog.flatMap(compile_decl).mkString + val instructions = prog.map(compile_decl).mkString (library + instructions).replaceAllLiterally("XXX", class_name) } @@ -171,4 +181,4 @@ Write(Call("facT",List(Num(10), Num(1))))))) // prints out the JVM instructions -println(compile(test, "fact")) +println(compile(test_prog, "fact"))