--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/progs/bf/bfc1.sc Mon Jul 27 01:55:05 2020 +0100
@@ -0,0 +1,84 @@
+// A Transpiler for the Brainf*** language
+//=========================================
+//
+// This version "optimises" the code by replacing
+// for example +++ by (*ptr) += 3, instead of
+// (*ptr)++, (*ptr)++, (*ptr)++
+//
+// Call with
+//
+// amm bfc1.sc <<bf_program.bf>>
+//
+
+
+// generating "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 the boilerplate
+def compile(prog: String) : String =
+ s"""#include <string.h>
+ #include <stdio.h>
+ char field[30000];
+ char *ptr = &field[15000];
+ int main() {
+ memset(field, '\\0', 30000);
+ ${instrs2(prog)}
+ return 0;}"""
+
+def compile_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_run(prog: String) = {
+ val tn = "tmp"
+ compile_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_run(bf_str))} secs")
+}
+