- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Feedback for Brainf*ck Interpreter
Sun, 2009-01-18, 20:43
Hello,
I wrote a little Brainf*ck Interpreter with an infinite bidirectional Tape
and a "customizable" alphabet. I know there are Parser Generators, but
before I use these, I have to become more fluent in Scala. The code seems to
work fine, but the design looks a little bit bulky for such an easy task.
Any hints?
Cheers,
Daniel
trait Func[T] {
val zero:T
def inc(t:T):T
def dec(t:T):T
def in: T
def out(t: T):Unit
}
trait ByteFunc extends Func[Byte] {
override val zero:Byte = 0
override def inc(t:Byte) = ((t + 1) & 0xFF).toByte
override def dec(t:Byte) = ((t - 1) & 0xFF).toByte
override def in:Byte = readByte
override def out(t: Byte) { print(t.toChar) }
}
class Tape[T](val cell:T, private val left:List[T], private val
right:List[T], val func:Func[T]) {
def inc = new Tape(func.inc(cell),left, right, func )
def dec = new Tape(func.dec(cell),left, right, func)
def goLeft = left match {
case Nil => new Tape(func.zero,Nil, cell :: right, func)
case head :: tail => new Tape(head, tail, cell :: right, func )
}
def goRight = right match {
case Nil => new Tape(func.zero, cell:: left, Nil, func)
case head :: tail => new Tape(head, cell :: left, tail, func)
}
def output = {
func.out(cell)
this
}
def input() = new Tape(func.in, left, right, func)
def isZero = cell == func.zero
}
object Tape {
def apply[T](func:Func[T]) = new Tape(func.zero, Nil, Nil, func)
}
object Brainfuck {
def main(args:Array[String]) {
execute(""">+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++++>-]
<.#>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>++++++++[
<++++>-]<+.[-]++++++++++.""");
}
def execute(prog: String) {
println("---running---")
execute(prog.replaceAll("[^\\+\\-\\[\\]\\.\\,\\>\\<]", ""), 0,
Tape(new ByteFunc{}))
}
def execute(prog: String, pos:Int, tape:Tape[Byte]) {
if(pos < prog.length) prog.charAt(pos) match {
case '+' => execute(prog, pos + 1, tape.inc)
case '-' => execute(prog, pos + 1, tape.dec)
case '>' => execute(prog, pos + 1, tape.goRight)
case '<' => execute(prog, pos + 1, tape.goLeft)
case '.' => execute(prog, pos + 1, tape.output)
case ',' => execute(prog, pos + 1, tape.input)
case '[' => if (tape.isZero) {
val endPos = prog.indexOf("]", pos + 1)
if (endPos < 0) error("Error: No matching ] for [ at
pos " + pos)
else execute(prog, endPos + 1, tape)
} else execute(prog, pos + 1, tape)
case ']' => if (! tape.isZero) {
val startPos = prog.lastIndexOf("[", pos - 1)
if (startPos < 0) error("Error: No matching [ for ]
at pos " + pos)
else execute(prog, startPos + 1, tape)
} else execute(prog, pos + 1, tape)
} else {
println("---done---")
}
}
}
Landei wrote:
>
> Hello,
>
> I wrote a little Brainf*ck Interpreter with an infinite bidirectional Tape
> and a "customizable" alphabet. I know there are Parser Generators, but
> before I use these, I have to become more fluent in Scala. The code seems
> to work fine, but the design looks a little bit bulky for such an easy
> task. Any hints?
>
> Cheers,
> Daniel
>
I'm sorry, I got the nested loops wrong (or better said, I wasn't even aware
of them). I shouldn't code so late...
Here is the corrected version (which looks now even more clumsy):
def execute(prog: String, pos:Int, tape:Tape[Byte]) {
def closedBracePos(bPos:Int, count:Int):Int =
if (count < 0 || bPos == prog.length)
error ("Unbalanced braces")
else prog.charAt(bPos) match {
case '[' => closedBracePos(bPos + 1, count + 1)
case ']' => if (count == 0) bPos
else closedBracePos(bPos + 1, count - 1)
case _ => closedBracePos(bPos + 1, count)
}
def openBracePos(bPos:Int, count:Int):Int =
if (count < 0 || bPos < 0)
error ("Unbalanced braces")
else prog.charAt(bPos) match {
case ']' => openBracePos(bPos - 1, count + 1)
case '[' => if (count == 0) bPos
else openBracePos(bPos - 1, count - 1)
case _ => openBracePos(bPos - 1, count)
}
if(pos < prog.length) prog.charAt(pos) match {
case '+' => execute(prog, pos + 1, tape.inc)
case '-' => execute(prog, pos + 1, tape.dec)
case '>' => execute(prog, pos + 1, tape.goRight)
case '<' => execute(prog, pos + 1, tape.goLeft)
case '.' => execute(prog, pos + 1, tape.output)
case ',' => execute(prog, pos + 1, tape.input)
case '[' => if (tape.isZero) execute(prog, closedBracePos(pos + 1,
0) + 1, tape)
else execute(prog, pos + 1, tape)
case ']' => if (! tape.isZero) execute(prog, openBracePos(pos - 1,
0), tape)
else execute(prog, pos + 1, tape)
} else {
println("\n---done---")
}
}
Cheers,
Daniel