progs/bf/bfc1.sc
author Christian Urban <christian.urban@kcl.ac.uk>
Sat, 15 Aug 2020 14:18:37 +0100
changeset 743 6acabeecdf75
parent 742 b5b5583a3a08
child 746 6916229b817b
permissions -rw-r--r--
updated
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
     1
// A Transpiler for the Brainf*** language
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
     2
//=========================================
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
     3
//
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
     4
// This version "optimises" the code by replacing 
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
     5
// for example +++ by (*ptr) += 3, instead of three
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
     6
// separate (*ptr)++, (*ptr)++, (*ptr)++
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
     7
// 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
     8
// Call with
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
     9
//
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    10
//  amm bfc1.sc <<bf_program.bf>>
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    11
//
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    12
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    13
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    14
// generate "compound" c-instructions 
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    15
def instr2(c: Char, n: Int) : String = c match {
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    16
  case '>' => s"ptr += $n ;"
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    17
  case '<' => s"ptr -= $n ;"
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    18
  case '+' => s"(*ptr) += $n ;"
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    19
  case '-' => s"(*ptr) -= $n ;"
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    20
  case '.' => "putchar(*ptr);" * n
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    21
  case ',' => "*ptr = getchar(); " * n
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    22
  case '['  => "while(*ptr){" * n
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    23
  case ']'  => "}" * n
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    24
  case _ => ""
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    25
}
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    26
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    27
// "splicing" a BF program into "spans" 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    28
// and counting the number of occurrences in
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    29
// each span; then generate the new intruction
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    30
// accordingly
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    31
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    32
def splice(cs: List[Char], acc: List[String]) : List[String] = cs match {
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    33
  case Nil => acc
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    34
  case hd :: _ => {
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    35
    val (hds, rest) = cs.span(_ == hd)
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    36
    splice(rest, instr2(hd, hds.length) :: acc) 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    37
  }
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    38
}
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    39
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    40
def instrs2(prog: String) : String =
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    41
  splice(prog.toList, Nil).reverse.mkString
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    42
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    43
// adding boilerplate
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    44
def compile(prog: String) : String = 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    45
  s"""#include <string.h> 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    46
      #include <stdio.h> 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    47
      char field[30000]; 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    48
      char *ptr = &field[15000]; 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    49
      int main() { 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    50
      memset(field, '\\0', 30000); 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    51
      ${instrs2(prog)} 
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    52
      return 0;}"""
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    53
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    54
def compile_to_file(name: String, prog: String) = 
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    55
  os.write.over(os.pwd / name, compile(prog))
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    56
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    57
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    58
// running the c-compiler over the transpiled
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    59
// BF program and running the resulting binary
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    60
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    61
def compile_and_run(prog: String) = {
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    62
  val tn = "tmp"
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    63
  compile_to_file(s"${tn}.c", prog)
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    64
  os.proc("gcc", "-O0", "-o", tn, s"${tn}.c").call() // call gcc
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    65
  os.proc("./tmp").call(stdout = os.Inherit)         // run binary
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    66
}
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    67
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    68
// Running Testcases
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    69
//===================
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    70
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    71
def time_needed[T](n: Int, code: => T) = {
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    72
  val start = System.nanoTime()
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    73
  for (i <- 0 until n) code
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    74
  val end = System.nanoTime()
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    75
  (end - start)/(n * 1.0e9)
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    76
}
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    77
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    78
@doc(" the argument should be a BF program ")
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    79
@main
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    80
def main(fname: String) = {
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    81
  val bf_str = os.read(os.pwd / fname)
742
b5b5583a3a08 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 740
diff changeset
    82
  println(s"${time_needed(1, compile_and_run(bf_str))} secs")
740
923b946347e6 updated
Christian Urban <christian.urban@kcl.ac.uk>
parents: 736
diff changeset
    83
}  
633
e4889da2fe29 updated
Christian Urban <urbanc@in.tum.de>
parents:
diff changeset
    84