updated
authorChristian Urban <christian.urban@kcl.ac.uk>
Wed, 30 Sep 2026 12:03:19 +0100
changeset 1047 e97cd7323100
parent 1046 499618ecf670
child 1048 34cbdb1178b7
updated
progs/bf/bfc0.scala
progs/bf/bfc1.scala
--- a/progs/bf/bfc0.scala	Wed Sep 30 12:01:12 2026 +0100
+++ b/progs/bf/bfc0.scala	Wed Sep 30 12:03:19 2026 +0100
@@ -3,7 +3,7 @@
 //
 // Call with
 //
-//  mill bfc0.sc --fname <<bf_program.bf>>
+//  mill bfc0.scala --fname <<bf_program.bf>>
 //
 //
 // Note: An interesting exercise is to call
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/progs/bf/bfc1.scala	Wed Sep 30 12:03:19 2026 +0100
@@ -0,0 +1,85 @@
+// 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
+//
+//  mill bfc1.scala --fname <<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 hash = java.util.UUID.randomUUID().toString.take(4)
+  val tn = s"tmp_$hash"
+  compile_to_file(s"${tn}.c", prog)
+  os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc
+  os.proc(os.pwd / s"${tn}").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")
+}  
+