progs/bf/bfc1.sc
changeset 740 923b946347e6
parent 736 d3e477fe6c66
child 742 b5b5583a3a08
equal deleted inserted replaced
739:220aea0e5517 740:923b946347e6
       
     1 // A Transpiler for the Brainf*** language
       
     2 //=========================================
       
     3 //
       
     4 // This version "optimises" the code by replacing 
       
     5 // for example +++ by (*ptr) += 3, instead of
       
     6 // (*ptr)++, (*ptr)++, (*ptr)++
       
     7 // 
       
     8 // Call with
       
     9 //
       
    10 //  amm bfc1.sc <<bf_program.bf>>
       
    11 //
       
    12 
       
    13 
       
    14 // generating "compound" c-instructions 
       
    15 def instr2(c: Char, n: Int) : String = c match {
       
    16   case '>' => s"ptr += $n ;"
       
    17   case '<' => s"ptr -= $n ;"
       
    18   case '+' => s"(*ptr) += $n ;"
       
    19   case '-' => s"(*ptr) -= $n ;"
       
    20   case '.' => "putchar(*ptr);" * n
       
    21   case ',' => "*ptr = getchar(); " * n
       
    22   case '['  => "while(*ptr){" * n
       
    23   case ']'  => "}" * n
       
    24   case _ => ""
       
    25 }
       
    26 
       
    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
       
    31 
       
    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   }
       
    38 }
       
    39 
       
    40 def instrs2(prog: String) : String =
       
    41   splice(prog.toList, Nil).reverse.mkString
       
    42 
       
    43 // adding the boilerplate
       
    44 def compile(prog: String) : String = 
       
    45   s"""#include <string.h> 
       
    46       #include <stdio.h> 
       
    47       char field[30000]; 
       
    48       char *ptr = &field[15000]; 
       
    49       int main() { 
       
    50       memset(field, '\\0', 30000); 
       
    51       ${instrs2(prog)} 
       
    52       return 0;}"""
       
    53 
       
    54 def compile_file(name: String, prog: String) = 
       
    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 
       
    61 def compile_run(prog: String) = {
       
    62   val tn = "tmp"
       
    63   compile_file(s"${tn}.c", prog)
       
    64   os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc
       
    65   os.proc("./tmp").call(stdout = os.Inherit)         // run binary
       
    66 }
       
    67 
       
    68 // Running Testcases
       
    69 //===================
       
    70 
       
    71 def time_needed[T](n: Int, code: => T) = {
       
    72   val start = System.nanoTime()
       
    73   for (i <- 0 until n) code
       
    74   val end = System.nanoTime()
       
    75   (end - start)/(n * 1.0e9)
       
    76 }
       
    77 
       
    78 @doc(" the argument should be a BF program ")
       
    79 @main
       
    80 def main(fname: String) = {
       
    81   val bf_str = os.read(os.pwd / fname)
       
    82   println(s"${time_needed(1, compile_run(bf_str))} secs")
       
    83 }  
       
    84