// 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+ −
// accordingly+ −
+ −
def 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 boilerplate+ −
def 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 binary+ −
+ −
def 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 ")+ −
@main+ −
def main(fname: String) = {+ −
val bf_str = os.read(os.pwd / fname)+ −
println(s"${time_needed(1, compile_and_run(bf_str))} secs")+ −
} + −
+ −