|
1 // A Transpiler for the Brainf*** language to C |
|
2 //=============================================== |
|
3 // |
|
4 // Call with |
|
5 // |
|
6 // amm bfc0.sc <<bf_program.bf>> |
|
7 // |
|
8 // |
|
9 // Note: An interesting exercise is to call |
|
10 // gcc with -O3 instead of -O0 |
|
11 |
|
12 // simple instructions |
|
13 def instr(c: Char) : String = c match { |
|
14 case '>' => "ptr++;" |
|
15 case '<' => "ptr--;" |
|
16 case '+' => "(*ptr)++;" |
|
17 case '-' => "(*ptr)--;" |
|
18 case '.' => "putchar(*ptr);" |
|
19 case ',' => "*ptr = getchar();" |
|
20 case '[' => "while(*ptr){" |
|
21 case ']' => "}" |
|
22 case _ => "" |
|
23 } |
|
24 |
|
25 def instrs(prog: String) : String = |
|
26 prog.toList.map(instr(_)).mkString |
|
27 |
|
28 // adding the boilerplate |
|
29 def compile(prog: String) : String = |
|
30 s"""#include <string.h> |
|
31 #include <stdio.h> |
|
32 char field[30000]; |
|
33 char *ptr = &field[15000]; |
|
34 int main() { |
|
35 memset(field, '\\0', 30000); |
|
36 ${instrs(prog)} |
|
37 return 0;}""" |
|
38 |
|
39 def compile_file(name: String, prog: String) = |
|
40 os.write.over(os.pwd / name, compile(prog)) |
|
41 |
|
42 |
|
43 // running the c-compiler over the transpiled |
|
44 // BF program and running the resulting binary |
|
45 |
|
46 def compile_run(prog: String) = { |
|
47 val tn = "tmp" |
|
48 compile_file(s"${tn}.c", prog) |
|
49 os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc |
|
50 os.proc("./tmp").call(stdout = os.Inherit) // run binary |
|
51 } |
|
52 |
|
53 // Running Testcases |
|
54 //=================== |
|
55 |
|
56 def time_needed[T](n: Int, code: => T) = { |
|
57 val start = System.nanoTime() |
|
58 for (i <- 0 until n) code |
|
59 val end = System.nanoTime() |
|
60 (end - start)/(n * 1.0e9) |
|
61 } |
|
62 |
|
63 @doc(" the argument should be a BF program ") |
|
64 @main |
|
65 def main(fname: String) = { |
|
66 val bf_str = os.read(os.pwd / fname) |
|
67 println(s"${time_needed(1, compile_run(bf_str))} secs") |
|
68 } |
|
69 |
|
70 |
|
71 |