// A Transpiler for the Brainf*** language//=========================================//// This version "optimises" the code by replacing // for example +++ by (*ptr) += 3, instead of three// separate (*ptr)++, (*ptr)++, (*ptr)++// // Call with//// amm bfc1.sc <<bf_program.bf>>//// generate "compound" c-instructions def instr2(c: Char, n: Int) : String = c match { case '>' => s"ptr += $n ;" case '<' => s"ptr -= $n ;" case '+' => s"(*ptr) += $n ;" case '-' => s"(*ptr) -= $n ;" case '.' => "putchar(*ptr);" * n case ',' => "*ptr = getchar(); " * n case '[' => "while(*ptr){" * n case ']' => "}" * n case _ => ""}// "splicing" a BF program into "spans" // and counting the number of occurrences in// each span; then generate the new intruction// accordinglydef splice(cs: List[Char], acc: List[String]) : List[String] = cs match { case Nil => acc case hd :: _ => { val (hds, rest) = cs.span(_ == hd) splice(rest, instr2(hd, hds.length) :: acc) }}def instrs2(prog: String) : String = splice(prog.toList, Nil).reverse.mkString// adding boilerplatedef compile(prog: String) : String = s"""#include <string.h> #include <stdio.h> int field[30000]; int *ptr = &field[15000]; int main() { memset(field, '\\0', 30000); ${instrs2(prog)} return 0;}"""def compile_to_file(name: String, prog: String) = os.write.over(os.pwd / name, compile(prog))// running the c-compiler over the transpiled// BF program and running the resulting binarydef compile_and_run(prog: String) = { val tn = "tmp" compile_to_file(s"${tn}.c", prog) os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc os.proc("./tmp").call(stdout = os.Inherit) // run binary}// Running Testcases//===================def time_needed[T](n: Int, code: => T) = { val start = System.nanoTime() for (i <- 0 until n) code val end = System.nanoTime() (end - start)/(n * 1.0e9)}//@doc(" the argument should be a BF program ")@maindef main(fname: String) = { val bf_str = os.read(os.pwd / fname) println(s"${time_needed(1, compile_and_run(bf_str))} secs")}