// Biota interpreter (original in Smalltalk by Ward Cunningham)
// Copyright 2005 Ian Osgood
// Run from a page having fields with these ids:
// large PRE blocks containing sample code or a textarea for code and data input
// dcdir: data direction 0..7 (0 is to the right, increases clockwise)
// code: empty PRE block for showing the program execution
// Buttons can execute these commands:
// format(id,dcdir): copy program from given element to code & setup PC and DC
// sstep: single step and highlight current position
// uses number from field id "repeat" to step n times
// run: toggle running steps on a timer
// sets milliseconds per step from field id "speed"
var code = ""; // formatted code
// workaround for immutable strings
function setCodeAt(i,c) {
code = code.substring(0,i)
+ c
+ code.substring(i+1);
}
function empty(i) { return code.charAt(i) == ' '; }
function full(i) { return code.charAt(i) != ' '; }
function encode(s) {
return s.replace(/ /g, " ").replace(/\n/g, "
");
}
// program and data counters
function Counter(ip,dir,color) {
this.ip = ip;
this.dir = dir&7;
this.color = color;
}
Counter.prototype = {
cspan: function() {
return '' + encode(this.get()) + '';
},
get: function() { return code.charAt(this.ip) },
put: function(c) { setCodeAt(this.ip, c) },
turn: function(t) { return (this.dir + t) & 7 }, // unsigned % 8;
go: function(dir) { return this.ip + this.offs[dir] },
fwd: function() { this.ip += this.offs[this.dir] },
back: function() { this.ip -= this.offs[this.dir] },
left: function() { this.dir = this.turn(-1) },
right: function() { this.dir = this.turn(1) },
turns: [ 0, 1,-1, 2,-2, 3,-3, 4 ],
next: function(ok) {
// find next acceptable character from this counter
for (var i in this.turns) {
var dir = this.turn(this.turns[i]);
var ip = this.go(dir);
if (ok(ip)) {
this.ip = ip;
this.dir = dir;
return true;
}
}
return false;
}
}
function setCode(c,w) { // offs: offsets indexed by dir
code = c;
Counter.prototype.offs = [ 1, w+1, w, w-1, -1, -w-1, -w, 1-w ];
}
var PC,DC;
function dump() {
// show the formatted code in a PRE block (id=code)
var c1,c2;
if (PC.ip < DC.ip) {
c1 = PC; c2 = DC;
} else {
c1 = DC; c2 = PC;
}
document.getElementById("code").innerHTML
= encode(code.substring(0,c1.ip))
+ c1.cspan()
+ encode(code.substring(c1.ip+1, c2.ip))
+ c2.cspan()
+ encode(code.substring(c2.ip+1));
var dcdir = document.getElementById("dcdir");
if (dcdir) dcdir.value = DC.dir.toString();
}
var spaces = " ";
function format(id,dcdir) {
var el = document.getElementById(id);
var raw = el.value || el.innerHTML;
var lines = raw.split('\n');
var width = 80;
var px=0,py=0,dx=0,dy=0;
for (var i in lines) {
// extract even characters as code
var line = "";
for (var j=0; j=0)
PC.next(executable) || PC.next(full);
dump();
}
var tid = 0;
function run() {
var runButton = document.getElementById("run");
if (tid) {
clearInterval(tid);
tid = 0;
runButton.value = "Run";
} else {
var speed = document.getElementById("speed");
var n = speed.value - 0;
if (n<10) { n=10; speed.value = "10"; }
tid = setInterval(sstep, n);
runButton.value = "Stop";
}
}