| 609 |      1 | // A Small Compiler for the WHILE Language
 | 
| 611 |      2 | // (stub for including arrays)
 | 
| 609 |      3 | 
 | 
|  |      4 | // the abstract syntax trees
 | 
|  |      5 | abstract class Stmt
 | 
|  |      6 | abstract class AExp
 | 
|  |      7 | abstract class BExp 
 | 
|  |      8 | type Block = List[Stmt]
 | 
|  |      9 | 
 | 
|  |     10 | // statements
 | 
|  |     11 | case object Skip extends Stmt
 | 
|  |     12 | case class If(a: BExp, bl1: Block, bl2: Block) extends Stmt
 | 
|  |     13 | case class While(b: BExp, bl: Block) extends Stmt
 | 
|  |     14 | case class Assign(s: String, a: AExp) extends Stmt
 | 
| 612 |     15 | case class AssignA(s: String, a1: AExp, a2: AExp) extends Stmt
 | 
| 611 |     16 | case class Write(s: String) extends Stmt  // writes out a variable
 | 
| 612 |     17 | case class Array(s: String, n: Int) extends Stmt 
 | 
| 609 |     18 | 
 | 
|  |     19 | // arithmetic expressions
 | 
|  |     20 | case class Var(s: String) extends AExp
 | 
|  |     21 | case class Num(i: Int) extends AExp
 | 
|  |     22 | case class Aop(o: String, a1: AExp, a2: AExp) extends AExp
 | 
| 612 |     23 | case class Ref(s: String, a1: AExp) extends AExp
 | 
| 609 |     24 | 
 | 
|  |     25 | // boolean expressions
 | 
|  |     26 | case object True extends BExp
 | 
|  |     27 | case object False extends BExp
 | 
|  |     28 | case class Bop(o: String, a1: AExp, a2: AExp) extends BExp
 | 
|  |     29 | 
 | 
|  |     30 | 
 | 
|  |     31 | // compiler headers needed for the JVM
 | 
|  |     32 | // (contains an init method, as well as methods for read and write)
 | 
|  |     33 | val beginning = """
 | 
|  |     34 | .class public XXX.XXX
 | 
|  |     35 | .super java/lang/Object
 | 
|  |     36 | 
 | 
|  |     37 | .method public <init>()V
 | 
|  |     38 |    aload_0
 | 
|  |     39 |    invokenonvirtual java/lang/Object/<init>()V
 | 
|  |     40 |    return
 | 
|  |     41 | .end method
 | 
|  |     42 | 
 | 
|  |     43 | .method public static write(I)V 
 | 
|  |     44 |     .limit locals 1 
 | 
|  |     45 |     .limit stack 2 
 | 
|  |     46 |     getstatic java/lang/System/out Ljava/io/PrintStream; 
 | 
|  |     47 |     iload 0
 | 
| 616 |     48 |     i2c
 | 
|  |     49 |     invokevirtual java/io/PrintStream/print(C)V 
 | 
| 609 |     50 |     return 
 | 
|  |     51 | .end method
 | 
|  |     52 | 
 | 
|  |     53 | .method public static main([Ljava/lang/String;)V
 | 
|  |     54 |    .limit locals 200
 | 
|  |     55 |    .limit stack 200
 | 
|  |     56 | 
 | 
|  |     57 | """
 | 
|  |     58 | 
 | 
|  |     59 | val ending = """
 | 
|  |     60 | 
 | 
|  |     61 |    return
 | 
|  |     62 | 
 | 
|  |     63 | .end method
 | 
|  |     64 | """
 | 
|  |     65 | 
 | 
|  |     66 | println("Start compilation")
 | 
|  |     67 | 
 | 
|  |     68 | 
 | 
|  |     69 | // for generating new labels
 | 
|  |     70 | var counter = -1
 | 
|  |     71 | 
 | 
|  |     72 | def Fresh(x: String) = {
 | 
|  |     73 |   counter += 1
 | 
|  |     74 |   x ++ "_" ++ counter.toString()
 | 
|  |     75 | }
 | 
|  |     76 | 
 | 
|  |     77 | // environments and instructions
 | 
|  |     78 | type Env = Map[String, String]
 | 
|  |     79 | type Instrs = List[String]
 | 
|  |     80 | 
 | 
|  |     81 | // arithmetic expression compilation
 | 
|  |     82 | def compile_aexp(a: AExp, env : Env) : Instrs = a match {
 | 
|  |     83 |   case Num(i) => List("ldc " + i.toString + "\n")
 | 
|  |     84 |   case Var(s) => List("iload " + env(s) + "\n")
 | 
|  |     85 |   case Aop("+", a1, a2) => 
 | 
|  |     86 |     compile_aexp(a1, env) ++ 
 | 
|  |     87 |     compile_aexp(a2, env) ++ List("iadd\n")
 | 
|  |     88 |   case Aop("-", a1, a2) => 
 | 
|  |     89 |     compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("isub\n")
 | 
|  |     90 |   case Aop("*", a1, a2) => 
 | 
|  |     91 |     compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ List("imul\n")
 | 
| 616 |     92 |   case Ref(s, a1) => 
 | 
|  |     93 |     List("aload " + env(s) + "\n") ++ compile_aexp(a1, env) ++ List("iaload \n")
 | 
| 609 |     94 | }
 | 
|  |     95 | 
 | 
|  |     96 | // boolean expression compilation
 | 
|  |     97 | def compile_bexp(b: BExp, env : Env, jmp: String) : Instrs = b match {
 | 
|  |     98 |   case True => Nil
 | 
|  |     99 |   case False => List("goto " + jmp + "\n")
 | 
|  |    100 |   case Bop("=", a1, a2) => 
 | 
|  |    101 |     compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ 
 | 
|  |    102 |     List("if_icmpne " + jmp + "\n")
 | 
|  |    103 |   case Bop("!=", a1, a2) => 
 | 
|  |    104 |     compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ 
 | 
|  |    105 |     List("if_icmpeq " + jmp + "\n")
 | 
|  |    106 |   case Bop("<", a1, a2) => 
 | 
|  |    107 |     compile_aexp(a1, env) ++ compile_aexp(a2, env) ++ 
 | 
|  |    108 |     List("if_icmpge " + jmp + "\n")
 | 
|  |    109 | }
 | 
|  |    110 | 
 | 
|  |    111 | // statement compilation
 | 
|  |    112 | def compile_stmt(s: Stmt, env: Env) : (Instrs, Env) = s match {
 | 
|  |    113 |   case Skip => (Nil, env)
 | 
|  |    114 |   case Assign(x, a) => {
 | 
|  |    115 |     val index = if (env.isDefinedAt(x)) env(x) else 
 | 
|  |    116 |                     env.keys.size.toString
 | 
|  |    117 |     (compile_aexp(a, env) ++ 
 | 
|  |    118 |      List("istore " + index + "\n"), env + (x -> index))
 | 
|  |    119 |   } 
 | 
|  |    120 |   case If(b, bl1, bl2) => {
 | 
|  |    121 |     val if_else = Fresh("If_else")
 | 
|  |    122 |     val if_end = Fresh("If_end")
 | 
|  |    123 |     val (instrs1, env1) = compile_block(bl1, env)
 | 
|  |    124 |     val (instrs2, env2) = compile_block(bl2, env1)
 | 
|  |    125 |     (compile_bexp(b, env, if_else) ++
 | 
|  |    126 |      instrs1 ++
 | 
|  |    127 |      List("goto " + if_end + "\n") ++
 | 
|  |    128 |      List("\n" + if_else + ":\n\n") ++
 | 
|  |    129 |      instrs2 ++
 | 
|  |    130 |      List("\n" + if_end + ":\n\n"), env2)
 | 
|  |    131 |   }
 | 
|  |    132 |   case While(b, bl) => {
 | 
|  |    133 |     val loop_begin = Fresh("Loop_begin")
 | 
|  |    134 |     val loop_end = Fresh("Loop_end")
 | 
|  |    135 |     val (instrs1, env1) = compile_block(bl, env)
 | 
|  |    136 |     (List("\n" + loop_begin + ":\n\n") ++
 | 
|  |    137 |      compile_bexp(b, env, loop_end) ++
 | 
|  |    138 |      instrs1 ++
 | 
|  |    139 |      List("goto " + loop_begin + "\n") ++
 | 
|  |    140 |      List("\n" + loop_end + ":\n\n"), env1)
 | 
|  |    141 |   }
 | 
|  |    142 |   case Write(x) => 
 | 
|  |    143 |     (List("iload " + env(x) + "\n" + 
 | 
|  |    144 |            "invokestatic XXX/XXX/write(I)V\n"), env)
 | 
| 612 |    145 |   case Array(s, n) => {
 | 
|  |    146 |     val index = if (env.isDefinedAt(s)) throw new Exception("Array already defined") else 
 | 
|  |    147 |                     env.keys.size.toString
 | 
|  |    148 |     (List("ldc " ++ n.toString ++ "\n",
 | 
|  |    149 |           "newarray int \n",
 | 
|  |    150 |           "astore " ++ index ++ "\n"), env + (s -> index))
 | 
|  |    151 |   } 
 | 
|  |    152 |   case AssignA(s, a1, a2) => {
 | 
|  |    153 |     val index = if (env.isDefinedAt(s)) env(s) else 
 | 
|  |    154 |                     throw new Exception("Array not yet defined")
 | 
| 616 |    155 |     (List("aload " + index + "\n") ++
 | 
| 612 |    156 |      compile_aexp(a1, env) ++
 | 
|  |    157 |      compile_aexp(a2, env) ++
 | 
| 616 |    158 |      List("iastore \n"), env)
 | 
| 612 |    159 |   } 
 | 
| 609 |    160 | }
 | 
|  |    161 | 
 | 
|  |    162 | // compilation of a block (i.e. list of instructions)
 | 
|  |    163 | def compile_block(bl: Block, env: Env) : (Instrs, Env) = bl match {
 | 
|  |    164 |   case Nil => (Nil, env)
 | 
|  |    165 |   case s::bl => {
 | 
|  |    166 |     val (instrs1, env1) = compile_stmt(s, env)
 | 
|  |    167 |     val (instrs2, env2) = compile_block(bl, env1)
 | 
|  |    168 |     (instrs1 ++ instrs2, env2)
 | 
|  |    169 |   }
 | 
|  |    170 | }
 | 
|  |    171 | 
 | 
|  |    172 | // main compilation function for blocks
 | 
|  |    173 | def compile(bl: Block, class_name: String) : String = {
 | 
|  |    174 |   val instructions = compile_block(bl, Map.empty)._1
 | 
|  |    175 |   (beginning ++ instructions.mkString ++ ending).replaceAllLiterally("XXX", class_name)
 | 
|  |    176 | }
 | 
|  |    177 | 
 | 
|  |    178 | 
 | 
|  |    179 | // compiling and running files
 | 
|  |    180 | //
 | 
|  |    181 | // JVM files can be assembled with 
 | 
|  |    182 | //
 | 
|  |    183 | //    java -jar jvm/jasmin-2.4/jasmin.jar fib.j
 | 
|  |    184 | //
 | 
|  |    185 | // and started with
 | 
|  |    186 | //
 | 
|  |    187 | //    java fib/fib
 | 
|  |    188 | 
 | 
|  |    189 | 
 | 
|  |    190 | 
 | 
|  |    191 | import scala.util._
 | 
|  |    192 | import scala.sys.process._
 | 
|  |    193 | import scala.io
 | 
|  |    194 | 
 | 
|  |    195 | def compile_tofile(bl: Block, class_name: String) = {
 | 
|  |    196 |   val output = compile(bl, class_name)
 | 
|  |    197 |   val fw = new java.io.FileWriter(class_name + ".j") 
 | 
|  |    198 |   fw.write(output) 
 | 
|  |    199 |   fw.close()
 | 
|  |    200 | }
 | 
|  |    201 | 
 | 
|  |    202 | def compile_all(bl: Block, class_name: String) : Unit = {
 | 
|  |    203 |   compile_tofile(bl, class_name)
 | 
|  |    204 |   println("compiled ")
 | 
|  |    205 |   val test = ("java -jar jvm/jasmin-2.4/jasmin.jar " + class_name + ".j").!!
 | 
|  |    206 |   println("assembled ")
 | 
|  |    207 | }
 | 
|  |    208 | 
 | 
|  |    209 | def time_needed[T](i: Int, code: => T) = {
 | 
|  |    210 |   val start = System.nanoTime()
 | 
|  |    211 |   for (j <- 1 to i) code
 | 
|  |    212 |   val end = System.nanoTime()
 | 
|  |    213 |   (end - start)/(i * 1.0e9)
 | 
|  |    214 | }
 | 
|  |    215 | 
 | 
|  |    216 | 
 | 
|  |    217 | def compile_run(bl: Block, class_name: String) : Unit = {
 | 
|  |    218 |   println("Start compilation")
 | 
|  |    219 |   compile_all(bl, class_name)
 | 
|  |    220 |   println("Time: " + time_needed(1, ("java " + class_name + "/" + class_name).!))
 | 
|  |    221 | }
 | 
|  |    222 | 
 | 
|  |    223 | 
 | 
|  |    224 | // Fibonacci numbers as a test-case
 | 
|  |    225 | val fib_test = 
 | 
|  |    226 |   List(Assign("n", Num(10)),            //  n := 10;                     
 | 
|  |    227 |        Assign("minus1",Num(0)),         //  minus1 := 0;
 | 
|  |    228 |        Assign("minus2",Num(1)),         //  minus2 := 1;
 | 
|  |    229 |        Assign("temp",Num(0)),           //  temp := 0;
 | 
|  |    230 |        While(Bop("<",Num(0),Var("n")),  //  while n > 0 do  {
 | 
|  |    231 |           List(Assign("temp",Var("minus2")),    //  temp := minus2;
 | 
|  |    232 |                Assign("minus2",Aop("+",Var("minus1"),Var("minus2"))), 
 | 
|  |    233 |                                         //  minus2 := minus1 + minus2;
 | 
|  |    234 |                Assign("minus1",Var("temp")), //  minus1 := temp;
 | 
|  |    235 |                Assign("n",Aop("-",Var("n"),Num(1))))), //  n := n - 1 };
 | 
|  |    236 |        Write("minus1"))                 //  write minus1
 | 
|  |    237 | 
 | 
|  |    238 | 
 | 
|  |    239 | compile_run(fib_test, "fib")
 | 
|  |    240 | 
 | 
| 611 |    241 | 
 | 
|  |    242 | val arr_test = 
 | 
| 616 |    243 |   List(Array("a", 10),                 // a[10]
 | 
|  |    244 |        Array("b", 2),                  // b[2]
 | 
|  |    245 |        AssignA("a", Num(0), Num(10)),  // a[0] := 10
 | 
|  |    246 |        Assign("x", Ref("a", Num(0))),  // x := a[0]
 | 
|  |    247 |        Write("x"),                     // write x
 | 
|  |    248 |        AssignA("b", Num(1), Num(5)),   // b[1] := 5
 | 
|  |    249 |        Assign("x", Ref("b", Num(1))),  // x := b[1]
 | 
|  |    250 |        Write("x"))                     // write x
 | 
|  |    251 | 
 | 
|  |    252 | compile_run(arr_test, "a")
 | 
|  |    253 | 
 | 
|  |    254 | 
 | 
|  |    255 | //====================
 | 
|  |    256 | // Parser Combinators
 | 
|  |    257 | //====================
 | 
|  |    258 | 
 | 
|  |    259 | import scala.language.implicitConversions
 | 
|  |    260 | import scala.language.reflectiveCalls
 | 
|  |    261 | 
 | 
|  |    262 | 
 | 
|  |    263 | abstract class Parser[I <% Seq[_], T] {
 | 
|  |    264 |   def parse(ts: I): Set[(T, I)]
 | 
|  |    265 | 
 | 
|  |    266 |   def parse_all(ts: I) : Set[T] =
 | 
|  |    267 |     for ((head, tail) <- parse(ts); if (tail.isEmpty)) yield head
 | 
|  |    268 | }
 | 
|  |    269 | 
 | 
|  |    270 | class SeqParser[I <% Seq[_], T, S](p: => Parser[I, T], q: => Parser[I, S]) extends Parser[I, (T, S)] {
 | 
|  |    271 |   def parse(sb: I) = 
 | 
|  |    272 |     for ((head1, tail1) <- p.parse(sb); 
 | 
|  |    273 |          (head2, tail2) <- q.parse(tail1)) yield ((head1, head2), tail2)
 | 
|  |    274 | }
 | 
|  |    275 | 
 | 
|  |    276 | class AltParser[I <% Seq[_], T](p: => Parser[I, T], q: => Parser[I, T]) extends Parser[I, T] {
 | 
|  |    277 |   def parse(sb: I) = p.parse(sb) ++ q.parse(sb)   
 | 
|  |    278 | }
 | 
|  |    279 | 
 | 
|  |    280 | class FunParser[I <% Seq[_], T, S](p: => Parser[I, T], f: T => S) extends Parser[I, S] {
 | 
|  |    281 |   def parse(sb: I) = 
 | 
|  |    282 |     for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
 | 
|  |    283 | }
 | 
|  |    284 | 
 | 
|  |    285 | 
 | 
|  |    286 | import scala.util.matching.Regex
 | 
|  |    287 | case class RegexParser(reg: Regex) extends Parser[String, String] {
 | 
|  |    288 |   def parse(sb: String) = reg.findPrefixMatchOf(sb) match {
 | 
|  |    289 |     case None => Set()
 | 
|  |    290 |     case Some(m) => Set((m.matched, m.after.toString))  
 | 
|  |    291 |   }
 | 
|  |    292 | }
 | 
|  |    293 | 
 | 
|  |    294 | def StringParser(s: String) = RegexParser(Regex.quote(s).r)
 | 
|  |    295 | 
 | 
|  |    296 | 
 | 
|  |    297 | implicit def string2parser(s : String) = StringParser(s)
 | 
|  |    298 | 
 | 
|  |    299 | implicit def ParserOps[I<% Seq[_], T](p: Parser[I, T]) = new {
 | 
|  |    300 |   def | (q : => Parser[I, T]) = new AltParser[I, T](p, q)
 | 
|  |    301 |   def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
 | 
|  |    302 |   def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
 | 
|  |    303 | }
 | 
|  |    304 | 
 | 
|  |    305 | implicit def StringOps(s: String) = new {
 | 
|  |    306 |   def | (q : => Parser[String, String]) = new AltParser[String, String](s, q)
 | 
|  |    307 |   def | (r: String) = new AltParser[String, String](s, r)
 | 
|  |    308 |   def ==>[S] (f: => String => S) = new FunParser[String, String, S](s, f)
 | 
|  |    309 |   def ~[S](q : => Parser[String, S]) = 
 | 
|  |    310 |     new SeqParser[String, String, S](s, q)
 | 
|  |    311 |   def ~ (r: String) = 
 | 
|  |    312 |     new SeqParser[String, String, String](s, r)
 | 
|  |    313 | }
 | 
|  |    314 | 
 | 
|  |    315 | 
 | 
|  |    316 | val NumParser = RegexParser("[0-9]+".r) ==> (s => s.toInt : Int)
 | 
|  |    317 | val IdParser = RegexParser("[a-z][a-z,0-9]*".r)
 | 
|  |    318 | 
 | 
|  |    319 | 
 | 
|  |    320 | 
 | 
|  |    321 | // Grammar Rules
 | 
|  |    322 | 
 | 
|  |    323 | lazy val AExp: Parser[String, AExp] = 
 | 
|  |    324 |   (Te ~ "+" ~ AExp) ==> { case ((x, _), z) => Aop("+", x, z):AExp } |
 | 
|  |    325 |   (Te ~ "-" ~ AExp) ==> { case ((x, _), z) => Aop("-", x, z):AExp } | Te
 | 
|  |    326 | lazy val Te: Parser[String, AExp] = 
 | 
|  |    327 |   (Fa ~ "*" ~ Te) ==> { case ((x, _), z) => Aop("*", x, z):AExp } | Fa
 | 
|  |    328 | lazy val Fa: Parser[String, AExp] = 
 | 
|  |    329 |    ("(" ~ AExp ~ ")") ==> { case ((_, y), _) => y } | 
 | 
|  |    330 |    (IdParser ~ "[" ~ AExp ~ "]") ==> { case (((x, _), y), _) => Ref(x, y) } |
 | 
|  |    331 |    IdParser ==> Var | 
 | 
|  |    332 |    NumParser ==> Num
 | 
|  |    333 | 
 | 
|  |    334 | // boolean expressions
 | 
|  |    335 | lazy val BExp: Parser[String, BExp] = 
 | 
|  |    336 |    (AExp ~ "=" ~ AExp) ==> { case ((x, y), z) => Bop("=", x, z):BExp } | 
 | 
|  |    337 |    (AExp ~ "!=" ~ AExp) ==> { case ((x, y), z) => Bop("!=", x, z):BExp } | 
 | 
|  |    338 |    (AExp ~ "<" ~ AExp) ==> { case ((x, y), z) => Bop("<", x, z):BExp } | 
 | 
|  |    339 |    (AExp ~ ">" ~ AExp) ==> { case ((x, y), z) => Bop("<", z, x):BExp } | 
 | 
|  |    340 |    ("true" ==> ((_) => True:BExp )) | 
 | 
|  |    341 |    ("false" ==> ((_) => False:BExp )) |
 | 
|  |    342 |    ("(" ~ BExp ~ ")") ==> { case ((x, y), z) => y}
 | 
|  |    343 | 
 | 
|  |    344 | lazy val Stmt: Parser[String, Stmt] =
 | 
|  |    345 |    ("skip" ==> (_ => Skip: Stmt)) |
 | 
|  |    346 |    (IdParser ~ ":=" ~ AExp) ==> { case ((x, y), z) => Assign(x, z): Stmt } |
 | 
|  |    347 |    (IdParser ~ "[" ~ AExp ~ "]" ~ ":=" ~ AExp) ==> { 
 | 
|  |    348 |      case (((((x, y), z), v), w), u) => AssignA(x, z, u): Stmt } |
 | 
|  |    349 |    ("if" ~ BExp ~ "then" ~ Block ~ "else" ~ Block) ==>
 | 
|  |    350 |     { case (((((x,y),z),u),v),w) => If(y, u, w): Stmt } |
 | 
|  |    351 |    ("while" ~ BExp ~ "do" ~ Block) ==> { case (((x, y), z), w) => While(y, w) } |
 | 
|  |    352 |    ("new" ~ IdParser ~ "[" ~ NumParser ~ "]") ==> { case ((((x, y), z), u), v) => Array(y, u) } |
 | 
|  |    353 |    ("write" ~ IdParser) ==> { case (_, y) => Write(y) } 
 | 
|  |    354 |  
 | 
|  |    355 | lazy val Stmts: Parser[String, Block] =
 | 
|  |    356 |   (Stmt ~ ";" ~ Stmts) ==> { case ((x, y), z) => x :: z : Block } |
 | 
|  |    357 |   (Stmt ==> ((s) => List(s) : Block)) 
 | 
| 611 |    358 | 
 | 
| 616 |    359 | 
 | 
|  |    360 | lazy val Block: Parser[String, Block] =
 | 
|  |    361 |    ("{" ~ Stmts ~ "}") ==> { case ((x, y), z) => y} | 
 | 
|  |    362 |    (Stmt ==> (s => List(s)))
 | 
|  |    363 | 
 | 
|  |    364 | Stmts.parse_all("x2:=5+a")
 | 
|  |    365 | Stmts.parse_all("x2:=5+a[3+a]")
 | 
|  |    366 | Stmts.parse_all("a[x2+3]:=5+a[3+a]")
 | 
|  |    367 | Block.parse_all("{x:=5;y:=8}")
 | 
|  |    368 | Block.parse_all("if(false)then{x:=5}else{x:=10}")
 | 
|  |    369 | 
 | 
|  |    370 | 
 | 
|  |    371 | 
 | 
|  |    372 | val fib = """
 | 
|  |    373 |    n := 10;
 | 
|  |    374 |    minus1 := 0;
 | 
|  |    375 |    minus2 := 1;
 | 
|  |    376 |    temp:=0;
 | 
|  |    377 |    while (n > 0) do {
 | 
|  |    378 |      temp := minus2; 
 | 
|  |    379 |      minus2 := minus1 + minus2;
 | 
|  |    380 |      minus1 := temp;
 | 
|  |    381 |      n := n - 1};
 | 
|  |    382 |    result := minus2;
 | 
|  |    383 |    write result
 | 
|  |    384 |    """.replaceAll("\\s+", "")
 | 
|  |    385 | 
 | 
|  |    386 | val fib_prog = Stmts.parse_all(fib).toList
 | 
|  |    387 | //compile_run(fib_prog.head, "fib")
 | 
|  |    388 | 
 | 
|  |    389 | 
 | 
|  |    390 | // BF 
 | 
|  |    391 | 
 | 
|  |    392 | // simple instructions
 | 
|  |    393 | def instr(c: Char) : String = c match {
 | 
|  |    394 |   case '>' => "ptr := ptr + 1;"
 | 
|  |    395 |   case '<' => "ptr := ptr - 1;"
 | 
|  |    396 |   case '+' => "field[ptr] := field[ptr] + 1;"
 | 
|  |    397 |   case '-' => "field[ptr] := field[ptr] - 1;"
 | 
|  |    398 |   case '.' => "x := field[ptr]; write x;"
 | 
|  |    399 |   //case ',' => "XXX" // "ptr = getchar();\n"
 | 
|  |    400 |   case '['  => "while (field[ptr] != 0) do {"
 | 
|  |    401 |   case ']'  => "skip};"
 | 
|  |    402 |   case _ => ""
 | 
|  |    403 | }
 | 
|  |    404 | 
 | 
|  |    405 | def instrs(prog: String) : String =
 | 
|  |    406 |   prog.toList.map(instr).mkString
 | 
|  |    407 | 
 | 
|  |    408 | 
 | 
|  |    409 | def splice(cs: List[Char], acc: List[(Char, Int)]) : List[(Char, Int)] = (cs, acc) match {
 | 
|  |    410 |   case (Nil, acc) => acc
 | 
|  |    411 |   case (c :: cs, Nil) => splice(cs, List((c, 1)))
 | 
|  |    412 |   case (c :: cs, (d, n) :: acc) => 
 | 
|  |    413 |     if (c == d) splice(cs, (c, n + 1) :: acc)
 | 
|  |    414 |     else splice(cs, (c, 1) :: (d, n) :: acc)
 | 
|  |    415 | }
 | 
|  |    416 | 
 | 
|  |    417 | def spl(s: String) = splice(s.toList, Nil).reverse
 | 
|  |    418 | 
 | 
|  |    419 | def instr2(c: Char, n: Int) : String = c match {
 | 
|  |    420 |   case '>' => "ptr := ptr + " + n.toString + ";"
 | 
|  |    421 |   case '<' => "ptr := ptr - " + n.toString + ";"
 | 
|  |    422 |   case '+' => "field[ptr] := field[ptr] + " + n.toString + ";"
 | 
|  |    423 |   case '-' => "field[ptr] := field[ptr] - " + n.toString + ";"
 | 
|  |    424 |   case '.' => "x := field[ptr]; write x;" //* n
 | 
|  |    425 |   //case ',' => "*ptr = getchar();\n" * n
 | 
|  |    426 |   case '['  => "while (field[ptr] != 0) do {" * n 
 | 
|  |    427 |   case ']'  => "skip};" * n
 | 
|  |    428 |   case _ => ""
 | 
|  |    429 | }
 | 
|  |    430 | 
 | 
|  |    431 | def instrs2(prog: String) : String =
 | 
|  |    432 |   spl(prog).map{ case (c, n) => instr2(c, n) }.mkString
 | 
|  |    433 | 
 | 
|  |    434 | 
 | 
|  |    435 | def bf_str(prog: String) : String = {
 | 
|  |    436 |   "\n" ++
 | 
|  |    437 |   //"new field[30000];\n" ++
 | 
|  |    438 |   "ptr := 15000;" ++
 | 
|  |    439 |   instrs2(prog) ++
 | 
|  |    440 |   "skip"
 | 
|  |    441 | }
 | 
|  |    442 | 
 | 
|  |    443 | def bf_run(prog: String, name: String) = {
 | 
|  |    444 |   println("BF processing start")
 | 
|  |    445 |   val bf_string = bf_str(prog).replaceAll("\\s", "")
 | 
|  |    446 |   println(s"BF parsing start (string length ${bf_string.length})")
 | 
|  |    447 |   val bf_prog = Stmts.parse_all(bf_string).toList.head
 | 
|  |    448 |   println("BF Compile start")
 | 
|  |    449 |   compile_run(Array("field", 30000) :: bf_prog, name)
 | 
|  |    450 | }
 | 
|  |    451 | 
 | 
|  |    452 | 
 | 
|  |    453 | 
 | 
|  |    454 | val bf1 = """++++++++[>+>++++<<-]>++>>+<[-[>>+<<-]+>>]>+[-<<<[
 | 
|  |    455 |       ->[+[-]+>++>>>-<<]<[<]>>++++++[<<+++++>>-]+<<++.[-]<<
 | 
|  |    456 |       ]>.>+[>>]>+]"""
 | 
|  |    457 | 
 | 
|  |    458 | bf_run(bf1, "sier")
 | 
|  |    459 | 
 | 
|  |    460 | bf_run("""++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++
 | 
|  |    461 |        ..+++.>>.<-.<.+++.------.--------.>>+.>++.""", "hello")
 | 
|  |    462 | 
 | 
|  |    463 | bf_run("""+++++++++++
 | 
|  |    464 |       >+>>>>++++++++++++++++++++++++++++++++++++++++++++
 | 
|  |    465 |       >++++++++++++++++++++++++++++++++<<<<<<[>[>>>>>>+>
 | 
|  |    466 |       +<<<<<<<-]>>>>>>>[<<<<<<<+>>>>>>>-]<[>++++++++++[-
 | 
|  |    467 |       <-[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]>[<<[>>>+<<<
 | 
|  |    468 |       -]>>[-]]<<]>>>[>>+>+<<<-]>>>[<<<+>>>-]+<[>[-]<[-]]
 | 
|  |    469 |       >[<<+>>[-]]<<<<<<<]>>>>>[+++++++++++++++++++++++++
 | 
|  |    470 |       +++++++++++++++++++++++.[-]]++++++++++<[->-<]>++++
 | 
|  |    471 |       ++++++++++++++++++++++++++++++++++++++++++++.[-]<<
 | 
|  |    472 |       <<<<<<<<<<[>>>+>+<<<<-]>>>>[<<<<+>>>>-]<-[>>.>.<<<
 | 
|  |    473 |       [-]]<<[>>+>+<<<-]>>>[<<<+>>>-]<<[<+>-]>[<+>-]<<<-]""", "fibs")
 |