Execute Brain****/JavaScript

From Rosetta Code
Execute Brain****/JavaScript is an implementation of Brainf***. Other implementations of Brainf***.
Execute Brain****/JavaScript is part of RCBF. You may find other members of RCBF at Category:RCBF.

Presented here is the core of a Brainf*** interpreter/debugger written in JavaScript that is designed to run within a web page. The full source can be found at http://www.quirkster.com/iano/js/bf.html by viewing the page source.

var code;           // formatted code
var ip = 0;         // current instruction within code
var nest = 0;       // current bracket nesting (for Out button)
var ahead = [];     // locations of matching brackets

var data = [0];     // data array (mod by +, -)
var dp = 0;         // index into data (mod by <, >)

var inp = 0;        // current input character (fetch with ,)
var quit = 0;       // termination flag

var commands = {
	'>':function() { if (++dp >= data.length) data[dp]=0 },
 	'<':function() { if (--dp < 0) quit++ },
 	'+':function() { ++data[dp] },
	'-':function() { --data[dp] },
 	'[':function() { if (!data[dp]) ip = ahead[ip]; else ++nest },
 	']':function() { if ( data[dp]) ip = ahead[ip]; else --nest },
 	',':function() {
		var c = document.getElementById("input").value.charCodeAt(inp++);
		data[dp] = isNaN(c) ? 0 : c;  // EOF: other options are -1 or no change
 	},
	'.':function() {
		var s = document.getElementById("output").innerHTML
		      + String.fromCharCode(data[dp]);
		s = s.replace(/\n/g,"<br>").replace(/ /g,"&amp;nbsp;");
		document.getElementById("output").innerHTML = s;
	},
};

function format(id) {
	var el = document.getElementById(id);
	code = el.value || el.textContent || el.innerText;
	// scan for matching braces
	var st = [], back, error = -1;
	ahead = [];
	for (ip=0; ip<code.length; ip++) {
		switch(code[ip]) {
		case '[':
			st.push(ip);
			break;
		case ']':
			if (st.length == 0) error = ip;
			back = st.pop();
			ahead[ip] = back;
			ahead[back] = ip;
			break;
		}
	}
	if (st.length > 0) error = st[0];
	init();
	if (error >= 0) {
		ip = error;	// highlight error
		alert("Unmatched '"+code[error]+"' at "+error+"!");
	}
	// show the  code in a PRE block (id=code)
	dump();
	window.scroll(0,document.getElementById("code").offsetTop);
}

function encode(s) {
	var e = s.replace(/&/g, "&amp;amp;");
	    e = e.replace(/</g, "&amp;lt;");
		e = e.replace(/>/g, "&amp;gt;");
		e = e.replace(/ /g, "&amp;nbsp;");
	return  e.replace(/\n/g, "<br>");
}
function dump() {
	document.getElementById("code").innerHTML
		= encode(code.substring(0,ip))
		+ '<span style="background: pink">'
		+ encode(code.charAt(ip))
		+ "</span>"
		+ encode(code.substring(ip+1));
	document.getElementById("mem").value = data.join();
}

function init() {
	inp = 0; quit = 0; dp = 0;
	data = [0]; ip = 0;
	document.getElementById("output").innerHTML = "";
}

function done() {
	return quit || ip >= code.length;
}

function body() {
        var fn;
        do {
          fn = commands[code.charAt(ip)];
	  if (fn) fn();
	  ++ip;
	} while (fn == null && !done());
}

// button handlers

var tid = 0;
function step() {
	var n = document.getElementById("repeat").value - 0;

	if (done()) init();
	while (--n>=0 && !done())
		body();

	if (done() && tid) slow();	// stop timer
	dump();
}

function out() {
	var d = nest;
	while (nest>=d && !done())
		body();
	dump();
}

function slow() {
	if (tid) {
		clearInterval(tid);
		tid = 0;
		document.getElementById("slow").value = "Slow";
	} else {
		var n = document.getElementById("speed").value - 0;
		tid = setInterval(step, Math.max(n,10));
		document.getElementById("slow").value = "Stop";
	}
}

function run() {
	while (!done())
		body();
	dump();
}