| author | Christian Urban <christian.urban@kcl.ac.uk> | 
| Tue, 29 Sep 2020 00:51:43 +0100 | |
| changeset 765 | b66602e0b42d | 
| parent 743 | f063a6cd5d91 | 
| child 825 | fb9f63a22114 | 
| permissions | -rw-r--r-- | 
| 
737
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
1  | 
// A simple Interpreter for BF*** Programs  | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
2  | 
//=========================================  | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
3  | 
//  | 
| 
738
 
03f46065ef04
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
737 
diff
changeset
 | 
4  | 
// Call with  | 
| 
737
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
5  | 
//  | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
6  | 
// amm bfi.sc <<bf_program.bf>>  | 
| 
738
 
03f46065ef04
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
737 
diff
changeset
 | 
7  | 
//  | 
| 632 | 8  | 
|
| 
737
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
9  | 
|
| 742 | 10  | 
// BF memory is represented as a Map  | 
| 632 | 11  | 
type Mem = Map[Int, Int]  | 
12  | 
||
| 742 | 13  | 
// safe reading and writing of BF memory  | 
| 632 | 14  | 
def sread(mem: Mem, mp: Int) : Int =  | 
15  | 
mem.getOrElse(mp, 0)  | 
|
16  | 
||
17  | 
def write(mem: Mem, mp: Int, v: Int) : Mem =  | 
|
18  | 
mem.updated(mp, v)  | 
|
19  | 
||
20  | 
// Right and Left Jumps in BF loops  | 
|
21  | 
def jumpRight(prog: String, pc: Int, level: Int) : Int = {
 | 
|
22  | 
if (prog.length <= pc) pc  | 
|
23  | 
  else (prog(pc), level) match {
 | 
|
24  | 
    case (']', 0) => pc + 1
 | 
|
25  | 
    case (']', l) => jumpRight(prog, pc + 1, l - 1)
 | 
|
26  | 
    case ('[', l) => jumpRight(prog, pc + 1, l + 1)
 | 
|
27  | 
case (_, l) => jumpRight(prog, pc + 1, l)  | 
|
28  | 
}  | 
|
29  | 
}  | 
|
30  | 
||
31  | 
def jumpLeft(prog: String, pc: Int, level: Int) : Int = {
 | 
|
32  | 
if (pc < 0) pc  | 
|
33  | 
  else (prog(pc), level) match {
 | 
|
34  | 
    case ('[', 0) => pc + 1
 | 
|
35  | 
    case ('[', l) => jumpLeft(prog, pc - 1, l - 1)
 | 
|
36  | 
    case (']', l) => jumpLeft(prog, pc - 1, l + 1)
 | 
|
37  | 
case (_, l) => jumpLeft(prog, pc - 1, l)  | 
|
38  | 
}  | 
|
39  | 
}  | 
|
40  | 
||
| 636 | 41  | 
// main interpreter loop  | 
| 632 | 42  | 
def compute(prog: String, pc: Int, mp: Int, mem: Mem) : Mem = {
 | 
43  | 
  if (0 <= pc && pc < prog.length) { 
 | 
|
44  | 
    val (new_pc, new_mp, new_mem) = prog(pc) match {
 | 
|
45  | 
case '>' => (pc + 1, mp + 1, mem)  | 
|
46  | 
case '<' => (pc + 1, mp - 1, mem)  | 
|
47  | 
case '+' => (pc + 1, mp, write(mem, mp, sread(mem, mp) + 1))  | 
|
48  | 
case '-' => (pc + 1, mp, write(mem, mp, sread(mem, mp) - 1))  | 
|
49  | 
      case '.' => { print(sread(mem, mp).toChar); (pc + 1, mp, mem) }
 | 
|
| 743 | 50  | 
case ',' => (pc + 1, mp, write(mem, mp, Console.in.read().toByte)) //scala.io.StdIn.readLine()  | 
| 632 | 51  | 
case '[' => if (sread(mem, mp) == 0)  | 
52  | 
(jumpRight(prog, pc + 1, 0), mp, mem)  | 
|
53  | 
else (pc + 1, mp, mem)  | 
|
54  | 
case ']' => if (sread(mem, mp) != 0)  | 
|
55  | 
(jumpLeft(prog, pc - 1, 0), mp, mem)  | 
|
56  | 
else (pc + 1, mp, mem)  | 
|
57  | 
case _ => (pc + 1, mp, mem)  | 
|
58  | 
}  | 
|
59  | 
compute(prog, new_pc, new_mp, new_mem)  | 
|
60  | 
}  | 
|
61  | 
else mem  | 
|
62  | 
}  | 
|
63  | 
||
64  | 
def run(prog: String, m: Mem = Map()) = compute(prog, 0, 0, m)  | 
|
65  | 
||
| 636 | 66  | 
// helper code for timing information  | 
| 632 | 67  | 
def time_needed[T](n: Int, code: => T) = {
 | 
68  | 
val start = System.nanoTime()  | 
|
69  | 
for (i <- 0 until n) code  | 
|
70  | 
val end = System.nanoTime()  | 
|
71  | 
(end - start)/(n * 1.0e9)  | 
|
72  | 
}  | 
|
73  | 
||
| 
738
 
03f46065ef04
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
737 
diff
changeset
 | 
74  | 
// Running Testcases  | 
| 
 
03f46065ef04
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
737 
diff
changeset
 | 
75  | 
//===================  | 
| 632 | 76  | 
|
| 
737
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
77  | 
@doc(" the argument should be a BF program ")
 | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
78  | 
@main  | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
79  | 
def main(fname: String) = {
 | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
80  | 
val bf_str = os.read(os.pwd / fname)  | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
81  | 
  println(s"${time_needed(1, run(bf_str))} secs")
 | 
| 
 
826c2bec403b
updated
 
Christian Urban <christian dot urban at kcl dot ac dot uk> 
parents: 
736 
diff
changeset
 | 
82  | 
}  | 
| 632 | 83  |