# HG changeset patch # User Christian Urban # Date 1790766199 -3600 # Node ID e97cd73231007f732e72f6e91a70ca7adef5b1ba # Parent 499618ecf670593a84b3687d7b62227c28d1105b updated diff -r 499618ecf670 -r e97cd7323100 progs/bf/bfc0.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 <> +// mill bfc0.scala --fname <> // // // Note: An interesting exercise is to call diff -r 499618ecf670 -r e97cd7323100 progs/bf/bfc1.scala --- /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 <> +// + + +// 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 + #include + 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") +} +