| 633 |      1 | // A Transpiler for the Brainf*** language
 | 
|  |      2 | //=========================================
 | 
| 740 |      3 | //
 | 
|  |      4 | // This version "optimises" the code by replacing 
 | 
| 742 |      5 | // for example +++ by (*ptr) += 3, instead of three
 | 
|  |      6 | // separate (*ptr)++, (*ptr)++, (*ptr)++
 | 
| 740 |      7 | // 
 | 
|  |      8 | // Call with
 | 
|  |      9 | //
 | 
|  |     10 | //  amm bfc1.sc <<bf_program.bf>>
 | 
|  |     11 | //
 | 
| 633 |     12 | 
 | 
|  |     13 | 
 | 
| 742 |     14 | // generate "compound" c-instructions 
 | 
| 633 |     15 | def instr2(c: Char, n: Int) : String = c match {
 | 
| 740 |     16 |   case '>' => s"ptr += $n ;"
 | 
|  |     17 |   case '<' => s"ptr -= $n ;"
 | 
|  |     18 |   case '+' => s"(*ptr) += $n ;"
 | 
|  |     19 |   case '-' => s"(*ptr) -= $n ;"
 | 
| 633 |     20 |   case '.' => "putchar(*ptr);" * n
 | 
| 740 |     21 |   case ',' => "*ptr = getchar(); " * n
 | 
| 633 |     22 |   case '['  => "while(*ptr){" * n
 | 
|  |     23 |   case ']'  => "}" * n
 | 
|  |     24 |   case _ => ""
 | 
|  |     25 | }
 | 
|  |     26 | 
 | 
| 740 |     27 | // "splicing" a BF program into "spans" 
 | 
|  |     28 | // and counting the number of occurrences in
 | 
|  |     29 | // each span; then generate the new intruction
 | 
|  |     30 | // accordingly
 | 
| 633 |     31 | 
 | 
| 740 |     32 | def splice(cs: List[Char], acc: List[String]) : List[String] = cs match {
 | 
|  |     33 |   case Nil => acc
 | 
|  |     34 |   case hd :: _ => {
 | 
|  |     35 |     val (hds, rest) = cs.span(_ == hd)
 | 
|  |     36 |     splice(rest, instr2(hd, hds.length) :: acc) 
 | 
|  |     37 |   }
 | 
| 633 |     38 | }
 | 
|  |     39 | 
 | 
| 740 |     40 | def instrs2(prog: String) : String =
 | 
|  |     41 |   splice(prog.toList, Nil).reverse.mkString
 | 
|  |     42 | 
 | 
| 742 |     43 | // adding boilerplate
 | 
| 740 |     44 | def compile(prog: String) : String = 
 | 
|  |     45 |   s"""#include <string.h> 
 | 
|  |     46 |       #include <stdio.h> 
 | 
| 746 |     47 |       int field[30000]; 
 | 
|  |     48 |       int *ptr = &field[15000]; 
 | 
| 740 |     49 |       int main() { 
 | 
|  |     50 |       memset(field, '\\0', 30000); 
 | 
|  |     51 |       ${instrs2(prog)} 
 | 
|  |     52 |       return 0;}"""
 | 
|  |     53 | 
 | 
| 742 |     54 | def compile_to_file(name: String, prog: String) = 
 | 
| 740 |     55 |   os.write.over(os.pwd / name, compile(prog))
 | 
|  |     56 | 
 | 
|  |     57 | 
 | 
|  |     58 | // running the c-compiler over the transpiled
 | 
|  |     59 | // BF program and running the resulting binary
 | 
|  |     60 | 
 | 
| 742 |     61 | def compile_and_run(prog: String) = {
 | 
| 990 |     62 |   val hash = java.util.UUID.randomUUID().toString.take(4)
 | 
|  |     63 |   val tn = s"tmp_$hash"
 | 
| 742 |     64 |   compile_to_file(s"${tn}.c", prog)
 | 
| 740 |     65 |   os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc
 | 
| 990 |     66 |   os.proc(os.pwd / s"${tn}").call(stdout = os.Inherit)         // run binary
 | 
| 633 |     67 | }
 | 
|  |     68 | 
 | 
| 740 |     69 | // Running Testcases
 | 
|  |     70 | //===================
 | 
| 633 |     71 | 
 | 
|  |     72 | def time_needed[T](n: Int, code: => T) = {
 | 
|  |     73 |   val start = System.nanoTime()
 | 
|  |     74 |   for (i <- 0 until n) code
 | 
|  |     75 |   val end = System.nanoTime()
 | 
| 740 |     76 |   (end - start)/(n * 1.0e9)
 | 
| 633 |     77 | }
 | 
|  |     78 | 
 | 
| 825 |     79 | //@doc(" the argument should be a BF program ")
 | 
| 740 |     80 | @main
 | 
|  |     81 | def main(fname: String) = {
 | 
|  |     82 |   val bf_str = os.read(os.pwd / fname)
 | 
| 742 |     83 |   println(s"${time_needed(1, compile_and_run(bf_str))} secs")
 | 
| 740 |     84 | }  
 | 
| 633 |     85 | 
 |