|
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 |