Permalink
Browse files

[BrainfuckParser.scala]

Moved Jump finding code into here
Changed Jump finding code to use real code lengths (ie multiple +,-,>,< stripped out and replaced by a single character) so jumps work properly with optimized code lengths
Added parsing of the loop characters

[BrainfuckInterpreter.scala]
Rewrote to use BrainfuckParser.scala instead of parsing in class

[BrainfuckInterpreterSpec.scala, BrainfuckParserSpec.scala]
Fixed some test cases
  • Loading branch information...
1 parent ef693a7 commit 7d7eebfa189c3ddbfc1fa9c646af262f8db935ea @UberMouse committed May 15, 2012
@@ -1,6 +1,7 @@
package nz.ubermouse.dailyprogrammer.challenge51.intermediate
import collection.immutable.{Stack, HashMap}
+import nz.ubermouse.dailyprogrammer.challenge51.intermediate.BrainfuckParser._
/**
@@ -14,73 +15,50 @@ object BrainfuckInterpreter {
val MEMORY_SIZE = 64000
var cells = Array.fill(MEMORY_SIZE)(0)
- val parserFunctionMappings = Map(">" -> ((ptr: Int, offset: Int) => (ptr + 1, offset)),
- "<" -> ((ptr: Int, offset: Int) => (ptr - 1, offset)),
- "-" -> ((ptr: Int, offset: Int) => {
- cells(ptr) -= 1;
- (ptr, offset)
- }),
- "+" -> ((ptr: Int, offset: Int) => {
- cells(ptr) += 1;
- (ptr, offset)
- }),
- "." -> ((ptr: Int, offset: Int) => {
- print(String.valueOf(cells(ptr).asInstanceOf[Char]));
- (ptr, offset)
- }),
- "," -> ((ptr: Int, offset: Int) => {
- cells(ptr) = readChar().asInstanceOf[Int];
- (ptr, offset)
- }),
- "[" -> ((ptr: Int, offset: Int) => {
- if (cells(ptr) == 0) (ptr, jumps(offset))
- else (ptr, offset)
- }),
- "]" -> ((ptr: Int, offset: Int) => {
- if (cells(ptr) != 0) (ptr, jumps(offset))
- else (ptr, offset)
- }))
- var jumps: Map[Int, Int] = new HashMap[Int, Int]
def main(args: Array[String]) {
apply("++++++++++[>>++++++>+++++++++++>++++++++++>+++++++++>+++>+++++>++++>++++++++>+[<]<-]>>+++++++.>+.-.>+++.<++++.>>+++++++.<<++.+.>+++++.>.<<-.>---.<-----.-.+++++.>>>+++.-.<<-.<+..----.>>>>++++++++.>+++++++..<<<<+.>>>>-.<<<<.++++.------.<+++++.---.>>>>>.<<<++.<<---.>++++++.>>>>+.<<<-.--------.<<+.>>>>>>+++.---.<-.<<<<---.<.>---.>>>>>>.")
}
- def findJumps(code: String): Map[Int, Int] = {
- def recursiveWrapper(code: String, offset: Int = 0,
- map: Map[Int, Int] = new HashMap[Int, Int],
- stack: Stack[Int] = new Stack[Int]): (Map[Int, Int], Stack[Int]) = {
- if (code.length == 1 || offset == code.length - 1) {
- String.valueOf(code.last) match {
- case "[" => (map, stack push offset)
- case "]" => {
- val (jumpPoint, newStack) = (stack.head, stack.pop)
- val backJump = (offset, jumpPoint)
- val forwardJump = (jumpPoint, offset)
- (map + backJump + forwardJump, newStack)
- }
- case _ => (map, stack)
- }
- }
- else {
- val (newMap, newStack) = recursiveWrapper(code.substring(offset, offset + 1), offset, map, stack)
- recursiveWrapper(code, offset + 1, newMap, newStack)
- }
- }
- recursiveWrapper(code)._1
- }
+
def apply(code: String, mode: Int = 0) = {
- jumps = findJumps(code)
cells = Array.fill(MEMORY_SIZE)(0)
- parse(code.replaceAll(" ", ""))
+ interpret(BrainfuckParser(code))
}
- private def parse(code: String, offset: Int = 0, ptr: Int = 0): (Int, Int) = {
- if (code.length == 1 || code.length - 1 == offset) parserFunctionMappings(String.valueOf(code.last))(ptr, offset)
+ private def interpret(code: List[BrainfuckParser.Node], offset: Int = 0, ptr: Int = 0): (Int, Int) = {
+ if (code.length == 1 || code.length - 1 == offset) {
+ code.last match {
+ case node:PtrAdd => (ptr + node.amount, offset)
+ case node:PtrSub => (ptr - node.amount, offset)
+ case node:DataAdd => {
+ cells(ptr) += node.amount
+ (ptr, offset)
+ }
+ case node:DataSub => {
+ cells(ptr) -= node.amount
+ (ptr, offset)
+ }
+ case node:PrintChar => {
+ print(cells(ptr).asInstanceOf[Char])
+ (ptr, offset)
+ }
+ case node:ReadChar => {
+ cells(ptr) = readByte()
+ (ptr, offset)
+ }
+ case node:ForwardJump => {
+ (ptr, if(cells(ptr) == 0) node.point else offset)
+ }
+ case node:BackJump => {
+ (ptr, if(cells(ptr) != 0) node.point else offset)
+ }
+ }
+ }
else {
- val (newPtr, newOffset) = parse(code.substring(offset, offset + 1), offset, ptr)
- parse(code, newOffset + 1, newPtr)
+ val (newPtr, newOffset) = interpret(List(code(offset)), offset, ptr)
+ interpret(code, newOffset + 1, newPtr)
}
}
}
@@ -1,6 +1,7 @@
package nz.ubermouse.dailyprogrammer.challenge51.intermediate
import util.matching.Regex
+import collection.immutable.{Stack, HashMap}
/**
* Created by IntelliJ IDEA.
@@ -35,7 +36,42 @@ object BrainfuckParser {
private def makeRegex(char: String) = new Regex(regexTemplate.replace("{CHAR}", char))
- def parse(code: String, offset: Int = 0, parsedSyntax: List[Node] = List[Node]()): List[Node] = {
+ private def realLength(code: String) = code.replaceAll(">+", ">")
+ .replaceAll("<+", "<")
+ .replaceAll("\\++", "+")
+ .replaceAll("-+", "-").length
+
+ def findJumps(code: String): Map[Int, Int] = {
+
+ //Use a sub function to make the function signature cleaner, only return what is needed. The Jump Map, instead of
+ //both the Map and the Stack
+ def recursiveWrapper(code: String, offset: Int = 0,
+ map: Map[Int, Int] = new HashMap[Int, Int],
+ stack: Stack[Int] = new Stack[Int]): (Map[Int, Int], Stack[Int]) = {
+ if (code.length == 1 || offset == code.length - 1) {
+ String.valueOf(code.last) match {
+ case "[" => (map, stack push offset)
+ case "]" => {
+ val (jumpPoint, newStack) = (stack.head, stack.pop)
+ val backJump = (offset, jumpPoint)
+ val forwardJump = (jumpPoint, offset)
+ (map + backJump + forwardJump, newStack)
+ }
+ case _ => (map, stack)
+ }
+ }
+ else {
+ val (newMap, newStack) = recursiveWrapper(code.substring(offset, offset + 1), offset, map, stack)
+ recursiveWrapper(code, offset + 1, newMap, newStack)
+ }
+ }
+ recursiveWrapper(code)._1
+ }
+
+ def parse(code: String,
+ offset: Int = 0,
+ jumps: Map[Int, Int],
+ parsedSyntax: List[Node] = List[Node]()): List[Node] = {
val (parsedNode, newOffset) = code.substring(offset, offset + 1) match {
case ">" => {
val length = makeRegex(">").findFirstMatchIn(code.substring(offset)).get.group(1).length
@@ -55,15 +91,24 @@ object BrainfuckParser {
}
case "." => (new PrintChar(), offset + 1)
case "," => (new ReadChar(), offset + 1)
+ case "[" => (new ForwardJump(jumps(realLength(code.substring(0, offset)))), offset + 1)
+ case "]" => (new BackJump(jumps(realLength(code.substring(0, offset)))), offset + 1)
}
val updatedNodeList = parsedSyntax ++ List(parsedNode)
- if (code.length - 1 == newOffset-1)
+ if (code.length - 1 == newOffset - 1)
updatedNodeList
else
- parse(code, newOffset, updatedNodeList)
+ parse(code, newOffset, jumps, updatedNodeList)
}
def apply(code: String) = {
- parse(code.replaceAll(" ", ""))
+ val spacesRemoved = code.replaceAll(" ", "")
+ parse(spacesRemoved,
+ 0,
+ findJumps(spacesRemoved
+ .replaceAll(">+", ">")
+ .replaceAll("<+", "<")
+ .replaceAll("\\++", "+")
+ .replaceAll("-+", "-")))
}
}
@@ -3,7 +3,7 @@ package nz.ubermouse.dailyprogrammer.challenge51.intermediate.tests
import org.scalatest.WordSpec
import java.io.{InputStream, OutputStream, PrintStream}
import collection.mutable.{Queue, ListBuffer}
-import nz.ubermouse.dailyprogrammer.challenge51.intermediate.{BrainfuckInterpreter => BrainfuckParser}
+import nz.ubermouse.dailyprogrammer.challenge51.intermediate.BrainfuckInterpreter
/**
* Created by IntelliJ IDEA.
@@ -47,45 +47,45 @@ class BrainfuckInterpreterSpec extends WordSpec {
"The Brainfuck Interpreter" should support {
"incrementing the Data Pointer with >" in {
expect(1) {
- BrainfuckParser(">")._1
+ BrainfuckInterpreter(">")._1
}
expect(5) {
- BrainfuckParser(">>>>>")._1
+ BrainfuckInterpreter(">>>>>")._1
}
}
"decrementing the Data Pointer with <" in {
expect(1) {
- BrainfuckParser(">><")._1
+ BrainfuckInterpreter(">><")._1
}
expect(5) {
- BrainfuckParser(">>>>>>><<")._1
+ BrainfuckInterpreter(">>>>>>><<")._1
}
}
"incrementing the byte at the current Data Pointer with +" in {
expect(1) {
- BrainfuckParser("+")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("+")
+ BrainfuckInterpreter.cells(0)
}
expect(5) {
- BrainfuckParser("+++++")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("+++++")
+ BrainfuckInterpreter.cells(0)
}
}
"decrementing the byte at the current Data Pointer with -" in {
expect(-1) {
- BrainfuckParser("-")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("-")
+ BrainfuckInterpreter.cells(0)
}
expect(-5) {
- BrainfuckParser("-----")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("-----")
+ BrainfuckInterpreter.cells(0)
}
}
@@ -94,64 +94,53 @@ class BrainfuckInterpreterSpec extends WordSpec {
Console.setOut(outStream)
expect("!") {
output.clear()
- BrainfuckParser("+++++++++++++++++++++++++++++++++.")
+ BrainfuckInterpreter("+++++++++++++++++++++++++++++++++.")
output(0)
}
expect("0") {
output.clear()
- BrainfuckParser("++++++++++++++++++++++++++++++++++++++++++++++++.")
+ BrainfuckInterpreter("++++++++++++++++++++++++++++++++++++++++++++++++.")
output(0)
}
Console.setOut(oldOut)
}
"the ability to store a byte of input at the current Data Pointer with ," in {
expect("!") {
- //BrainfuckParser(",.")
+ //BrainfuckInterpreter(",.")
//output(0)
pending
}
expect("0") {
- //BrainfuckParser(",.")
+ //BrainfuckInterpreter(",.")
//output(0)
pending
}
}
- "being able to find all jump points in the code" in {
- expect(Map(6 -> 2, 2 -> 6)) {
- BrainfuckParser.findJumps("++[++-]--")
- }
-
- expect(Map(24 -> 19, 16 -> 8, 11 -> 15, 8 -> 16, 19 -> 24, 15 -> 11)) {
- BrainfuckParser
- .findJumps(">>+<-->+[++[-->]]--[<<<+]")
- }
- }
-
"forward command jumping to after the next ] with [ on a 0 byte pointer" in {
expect(1) {
- BrainfuckParser("[+++++]+")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("[+++++]+")
+ BrainfuckInterpreter.cells(0)
}
- expect(5) {
- BrainfuckParser("[+++++]++[++>]<+")
- BrainfuckParser.cells(0)
+ expect(4) {
+ BrainfuckInterpreter("[+++++]++[++>]+")
+ BrainfuckInterpreter.cells(0)
}
}
"backward command jumping to the previous [ with ] on a positive byte pointer" in {
expect(11) {
- BrainfuckParser("+>++[<+++++>-]+")
- BrainfuckParser.cells(0)
+ BrainfuckInterpreter("+>++[<+++++>-]+")
+ BrainfuckInterpreter.cells(0)
}
expect(8) {
- BrainfuckParser("+>++[<+++++>-]++>+++[<++>-]+")
- BrainfuckParser.cells(1)
+ BrainfuckInterpreter("+>++[<+++++>-]++>+++[<++>-]+")
+ BrainfuckInterpreter.cells(1)
}
}
}
@@ -36,9 +36,13 @@ class BrainfuckParserSpec extends WordSpec {
expect("List(ReadChar())") {
BrainfuckParser(",").toString()
}
+
+ expect("List(ForwardJump(1), BackJump(0))") {
+ BrainfuckParser("[]").toString()
+ }
}
- "optimize away multiples of the same expression into one" in {
+ "optimize away multiples of the same expression into one (where applicable)" in {
expect("List(PtrAdd(5))") {
BrainfuckParser(">>>>>").toString()
}
@@ -65,5 +69,22 @@ class BrainfuckParserSpec extends WordSpec {
BrainfuckParser(">>>>>++-++-+-+-+<<<<>>--+").toString()
}
}
+
+ "find all jump points in the code" in {
+ expect(Map(1 -> 4, 4 -> 1)) {
+ BrainfuckParser.findJumps("++[++-]--".replaceAll(">+", ">")
+ .replaceAll("<+", "<")
+ .replaceAll("\\++", "+")
+ .replaceAll("-+", "-"))
+ }
+
+ expect(Map(14 -> 17, 6 -> 12, 17 -> 14, 12 -> 6, 11 -> 8, 8 -> 11)) {
+ BrainfuckParser
+ .findJumps(">>+<-->+[++[-->]]--[<<<+]".replaceAll(">+", ">")
+ .replaceAll("<+", "<")
+ .replaceAll("\\++", "+")
+ .replaceAll("-+", "-"))
+ }
+ }
}
}

0 comments on commit 7d7eebf

Please sign in to comment.