Attic/fun-orig.scala
changeset 789 f0696713177b
parent 735 fc2e3609d5e5
equal deleted inserted replaced
788:3b1136fb6bee 789:f0696713177b
       
     1 // A Small Compiler for a Simple Functional Language
       
     2 // (includes an external lexer and parser)
       
     3 //
       
     4 // call with 
       
     5 //
       
     6 //     amm fun.scala fact
       
     7 //
       
     8 //     amm fun.scala defs
       
     9 //
       
    10 // this will generate a .j file and run the jasmin
       
    11 // assembler (installed at jvm/jasmin-2.4/jasmin.jar)
       
    12 // it runs the resulting JVM file twice for timing 
       
    13 // purposes.
       
    14 
       
    15 
       
    16 import java.io._  
       
    17 import scala.util._
       
    18 import scala.sys.process._
       
    19 
       
    20 // Abstract syntax trees for the Fun language
       
    21 abstract class Exp extends Serializable 
       
    22 abstract class BExp extends Serializable 
       
    23 abstract class Decl extends Serializable 
       
    24 
       
    25 case class Def(name: String, args: List[String], body: Exp) extends Decl
       
    26 case class Main(e: Exp) extends Decl
       
    27 
       
    28 case class Call(name: String, args: List[Exp]) extends Exp
       
    29 case class If(a: BExp, e1: Exp, e2: Exp) extends Exp
       
    30 case class Write(e: Exp) extends Exp
       
    31 case class Var(s: String) extends Exp
       
    32 case class Num(i: Int) extends Exp
       
    33 case class Aop(o: String, a1: Exp, a2: Exp) extends Exp
       
    34 case class Sequence(e1: Exp, e2: Exp) extends Exp
       
    35 case class Bop(o: String, a1: Exp, a2: Exp) extends BExp
       
    36 
       
    37 
       
    38 // compiler - built-in functions 
       
    39 // copied from http://www.ceng.metu.edu.tr/courses/ceng444/link/jvm-cpm.html
       
    40 //
       
    41 
       
    42 val library = """
       
    43 .class public XXX.XXX
       
    44 .super java/lang/Object
       
    45 
       
    46 .method public static write(I)V 
       
    47         .limit locals 1 
       
    48         .limit stack 2 
       
    49         getstatic java/lang/System/out Ljava/io/PrintStream; 
       
    50         iload 0
       
    51         invokevirtual java/io/PrintStream/println(I)V 
       
    52         return 
       
    53 .end method
       
    54 
       
    55 """
       
    56 
       
    57 // calculating the maximal needed stack size
       
    58 def max_stack_exp(e: Exp): Int = e match {
       
    59   case Call(_, args) => args.map(max_stack_exp).sum
       
    60   case If(a, e1, e2) => 
       
    61     max_stack_bexp(a) + (List(max_stack_exp(e1), max_stack_exp(e2)).max)
       
    62   case Write(e) => max_stack_exp(e) + 1
       
    63   case Var(_) => 1
       
    64   case Num(_) => 1
       
    65   case Aop(_, a1, a2) => max_stack_exp(a1) + max_stack_exp(a2)
       
    66   case Sequence(e1, e2) => List(max_stack_exp(e1), max_stack_exp(e2)).max
       
    67 }
       
    68 
       
    69 def max_stack_bexp(e: BExp): Int = e match {
       
    70   case Bop(_, a1, a2) => max_stack_exp(a1) + max_stack_exp(a2)
       
    71 }
       
    72 
       
    73 
       
    74 // for generating new labels
       
    75 var counter = -1
       
    76 
       
    77 def Fresh(x: String) = {
       
    78   counter += 1
       
    79   x ++ "_" ++ counter.toString()
       
    80 }
       
    81 
       
    82 // convenient string interpolations 
       
    83 // for instructions, labels and methods
       
    84 import scala.language.implicitConversions
       
    85 import scala.language.reflectiveCalls
       
    86 
       
    87 implicit def sring_inters(sc: StringContext) = new {
       
    88     def i(args: Any*): String = "   " ++ sc.s(args:_*) ++ "\n"
       
    89     def l(args: Any*): String = sc.s(args:_*) ++ ":\n"
       
    90     def m(args: Any*): String = sc.s(args:_*) ++ "\n"
       
    91 }
       
    92 
       
    93 
       
    94 type Env = Map[String, Int]
       
    95 
       
    96 // compile expressions
       
    97 def compile_exp(a: Exp, env : Env) : String = a match {
       
    98   case Num(i) => i"ldc $i"
       
    99   case Var(s) => i"iload ${env(s)}"
       
   100   case Aop("+", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"iadd"
       
   101   case Aop("-", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"isub"
       
   102   case Aop("*", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"imul"
       
   103   case Aop("/", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"idiv"
       
   104   case Aop("%", a1, a2) => compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"irem"
       
   105   case If(b, a1, a2) => {
       
   106     val if_else = Fresh("If_else")
       
   107     val if_end = Fresh("If_end")
       
   108     compile_bexp(b, env, if_else) ++
       
   109     compile_exp(a1, env) ++
       
   110     i"goto $if_end" ++
       
   111     l"$if_else" ++
       
   112     compile_exp(a2, env) ++
       
   113     l"$if_end"
       
   114   }
       
   115   case Call(name, args) => {
       
   116     val is = "I" * args.length
       
   117     args.map(a => compile_exp(a, env)).mkString ++
       
   118     i"invokestatic XXX/XXX/$name($is)I"
       
   119   }
       
   120   case Sequence(a1, a2) => {
       
   121     compile_exp(a1, env) ++ i"pop" ++ compile_exp(a2, env)
       
   122   }
       
   123   case Write(a1) => {
       
   124     compile_exp(a1, env) ++
       
   125     i"dup" ++
       
   126     i"invokestatic XXX/XXX/write(I)V"
       
   127   }
       
   128 }
       
   129 
       
   130 // compile boolean expressions
       
   131 def compile_bexp(b: BExp, env : Env, jmp: String) : String = b match {
       
   132   case Bop("==", a1, a2) => 
       
   133     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpne $jmp"
       
   134   case Bop("!=", a1, a2) => 
       
   135     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpeq $jmp"
       
   136   case Bop("<", a1, a2) => 
       
   137     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpge $jmp"
       
   138   case Bop("<=", a1, a2) => 
       
   139     compile_exp(a1, env) ++ compile_exp(a2, env) ++ i"if_icmpgt $jmp"
       
   140 }
       
   141 
       
   142 // compile function for declarations and main
       
   143 def compile_decl(d: Decl) : String = d match {
       
   144   case Def(name, args, a) => { 
       
   145     val env = args.zipWithIndex.toMap
       
   146     val is = "I" * args.length
       
   147     m".method public static $name($is)I" ++
       
   148     m".limit locals ${args.length}" ++
       
   149     m".limit stack ${1 + max_stack_exp(a)}" ++
       
   150     l"${name}_Start" ++   
       
   151     compile_exp(a, env) ++
       
   152     i"ireturn" ++
       
   153     m".end method\n"
       
   154   }
       
   155   case Main(a) => {
       
   156     m".method public static main([Ljava/lang/String;)V" ++
       
   157     m".limit locals 200" ++
       
   158     m".limit stack 200" ++
       
   159     compile_exp(a, Map()) ++
       
   160     i"invokestatic XXX/XXX/write(I)V" ++
       
   161     i"return" ++
       
   162     m".end method\n"
       
   163   }
       
   164 }
       
   165 
       
   166 // main compiler functions
       
   167 
       
   168 def time_needed[T](i: Int, code: => T) = {
       
   169   val start = System.nanoTime()
       
   170   for (j <- 1 to i) code
       
   171   val end = System.nanoTime()
       
   172   (end - start)/(i * 1.0e9)
       
   173 }
       
   174 
       
   175 def deserialise[T](fname: String) : Try[T] = {
       
   176   import scala.util.Using
       
   177   Using(new ObjectInputStream(new FileInputStream(fname))) {
       
   178     in => in.readObject.asInstanceOf[T]
       
   179   }
       
   180 }
       
   181 
       
   182 def compile(class_name: String) : String = {
       
   183   val ast = deserialise[List[Decl]](class_name ++ ".prs").getOrElse(Nil) 
       
   184   val instructions = ast.map(compile_decl).mkString
       
   185   (library + instructions).replaceAllLiterally("XXX", class_name)
       
   186 }
       
   187 
       
   188 def compile_to_file(class_name: String) = {
       
   189   val output = compile(class_name)
       
   190   scala.tools.nsc.io.File(s"${class_name}.j").writeAll(output)
       
   191 }
       
   192 
       
   193 def compile_and_run(class_name: String) : Unit = {
       
   194   compile_to_file(class_name)
       
   195   (s"java -jar jvm/jasmin-2.4/jasmin.jar ${class_name}.j").!!
       
   196   println("Time: " + time_needed(1, (s"java ${class_name}/${class_name}").!))
       
   197 }
       
   198 
       
   199 
       
   200 // some examples of .fun files
       
   201 //compile_to_file("fact")
       
   202 //compile_and_run("fact")
       
   203 //compile_and_run("defs")
       
   204 
       
   205 
       
   206 def main(args: Array[String]) : Unit = 
       
   207    compile_and_run(args(0))
       
   208 
       
   209 
       
   210 }