**
** Interpreter for brainfuck instructions
**
class Interpreter
{
const static Log log := Interpreter#.pod.log
**
** Program to interprete
**
Token[] program { private set }
**
** Array to write
**
Int[] tape { private set }
**
** Data pointer
**
Int dp := 0 { private set }
**
** Program pointer
**
Int pp := 0 { private set }
**
** Matched brackets
**
private Int:Int brackets
**
** Index the matching brackets to speed up the program.
** Instead of move from one instruction to another to find the
** matching bracket it "jumps" directly to the position.
**
static [Int:Int] indexBrackets (Token[] program)
{
result := Int:Int[:]
levels := Int:Int[:]
level := 0
(0..<program.size).each |i|
{
token := program[i]
switch(token.instruction)
{
case Instruction.jumpForward:
++level
levels[level] = i
case Instruction.jumpBackward:
match := levels[level]
result[i] = match
result[match] = i
--level
default:
}
}
return result
}
**
** Creates the interpreter with the program and
** the size of the array
**
new make(Token[] program, Int size := 3000)
{
this.program = program
this.tape = Int[,].fill(0, size)
this.brackets = indexBrackets(program)
}
Bool off()
{
pp >= program.size
}
Void forward()
{
++pp
}
Void run()
{
while(!off)
{
execute
forward
}
}
Void execute()
{
switch(program[pp].instruction)
{
case Instruction.incrementData:
incrementData
case Instruction.decrementData:
decrementData
case Instruction.incrementPointer:
incrementPointer
case Instruction.decrementPointer:
decrementPointer
case Instruction.input:
input
case Instruction.output:
output
case Instruction.jumpForward:
jumpForward
case Instruction.jumpBackward:
jumpBackward
}
}
Void incrementData()
{
tape[dp] = tape[dp] + 1
}
Void decrementData()
{
tape[dp] = tape[dp] - 1
}
Void incrementPointer()
{
++dp
if (dp > tape.size)
{
throw BrainfuckErr(program[pp], "Memory pointer overflow")
}
}
Void decrementPointer()
{
--dp
if (dp < 0)
{
throw BrainfuckErr(program[pp], "Memory pointer underflow")
}
}
Void input()
{
valid := false
while(!valid)
{
try
{
n := Env.cur.in.readLine.toInt
if (n < 0 || n > 255)
{
echo("Invalid input: must be a number between 0 and 255")
}
else
{
tape[dp] = n
valid = true
}
}
catch (Err e)
{
echo("Invalid input: not a number")
}
}
}
Void output()
{
Env.cur.out.print(tape[dp].toChar)
}
Void jumpForward()
{
if (tape[dp] != 0) return
pp = brackets[pp]
}
Void jumpBackward()
{
if (tape[dp] == 0) return
pp = brackets[pp]
}
override Str toStr()
{
return "Program[$pp]=${program[pp]} Tape[$dp]=${tape[dp]}"
}
}