package bfrc.interpreter;

import bfrc.ast.BlockNode;
import bfrc.ast.ChangeNode;
import bfrc.ast.Node;
import bfrc.ast.RootNode;
import bfrc.backend.Backend;

/**
 * A simple interpreter backend, that directly traverses the AST during
 * execution.
 * 
 * @author oreissig
 */
public class Interpreter implements Backend {

	protected final byte[] mem = new byte[MEM_SIZE];
	protected int p = 0;
	protected final InputOutput io;

	public Interpreter() {
		this(new ConsoleIO());
	}

	public Interpreter(InputOutput io) {
		this.io = io;
	}

	@Override
	public void work(RootNode root) throws InterruptedException {
		for (Node n : root.sub)
			visit(n);
	}

	/**
	 * This method implements a custom traversion mechanism to handle repeated
	 * loop execution.
	 * 
	 * @param n node to visit
	 * @throws InterruptedException 
	 */
	protected void visit(Node n) throws InterruptedException {
		if (Thread.interrupted())
			throw new InterruptedException();
		try {
			switch (n.type) {
				case LOOP:
					BlockNode block = (BlockNode) n;
					while (mem[p] != 0)
						for (Node child : block.sub)
							visit(child);
					break;
				case VALUE:
					ChangeNode cn = (ChangeNode) n;
					if (cn.absolute)
						mem[p] = (byte) cn.change;
					else
						mem[p] += cn.change;
					break;
				case POINTER:
					cn = (ChangeNode) n;
					p += cn.change;
					break;
				case INPUT:
					mem[p] = io.read();
					break;
				case OUTPUT:
					io.write(mem[p]);
					break;
				default:
					throw new RuntimeException("unexpected node type: " + n.type);
			}
		} catch (Exception e) {
			throw new RuntimeException("Error interpreting " + n, e);
		}
	}
}

@@ -20,16 +20,70 @@
 reter;%0A%0A
+import java.util.ArrayDeque;%0Aimport java.util.Deque;%0A%0A
 import b
@@ -97,24 +97,24 @@
 .BlockNode;%0A
-
 import bfrc.
@@ -476,16 +476,143 @@
 tput io;
+%0A%09/**%0A%09 * The execution stack of the program contains loop nodes.%0A%09 */%0A%09protected final Deque%3CNode%3E stack = new ArrayDeque%3C%3E();
 %0A%0A%09publi
@@ -1069,73 +1069,8 @@
 n %7B%0A
-%09%09if (Thread.interrupted())%0A%09%09%09throw new InterruptedException();%0A
 %09%09tr
@@ -1109,16 +1109,36 @@
 e LOOP:%0A
+%09%09%09%09%09stack.push(n);%0A
 %09%09%09%09%09Blo
@@ -1191,16 +1191,91 @@
 p%5D != 0)
+ %7B%0A%09%09%09%09%09%09if (Thread.interrupted())%0A%09%09%09%09%09%09%09throw new InterruptedException();
 %0A%09%09%09%09%09%09f
@@ -1323,16 +1323,41 @@
 child);%0A
+%09%09%09%09%09%7D%0A%09%09%09%09%09stack.pop();%0A
 %09%09%09%09%09bre
@@ -1814,21 +1814,37 @@
 e) %7B%0A%09%09%09
-throw
+RuntimeException ex =
  new Run
@@ -1890,15 +1890,342 @@
 , e);%0A%09%09
-%7D
+%09// set custom stack trace to point to the actual source program%0A%09%09%09ex.setStackTrace(getStack());%0A%09%09%09throw ex;%0A%09%09%7D%0A%09%7D%0A%09%0A%09private StackTraceElement%5B%5D getStack() %7B%0A%09%09return stack.stream().map(n -%3E new StackTraceElement(%22Brainfuck%22, %22main%22,%0A%09%09%09%09%09%09%09%09%09%09Integer.toString(n.line), n.offset))%0A%09%09%09%09%09%09%09 .toArray(StackTraceElement%5B%5D::new);
 %0A%09%7D%0A%7D%0A

implement custom stack don't check interrupted for all operations, just inside loops