Skip to content

Instantly share code, notes, and snippets.

@dorentus
Last active January 19, 2016 19:14
Show Gist options
  • Save dorentus/9370586 to your computer and use it in GitHub Desktop.
Save dorentus/9370586 to your computer and use it in GitHub Desktop.
yet another befunge interpreter
function interpret(code) {
var machine = (function () {
var stack = [];
return {
push: function (v) {
stack.push(v);
return this;
},
pop: function () {
return stack.pop();
},
top: function () {
return stack[stack.length - 1];
},
add: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(a + b);
return this;
},
sub: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(b - a);
return this;
},
mul: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(a * b);
return this;
},
div: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(a === 0 ? 0 : parseInt(Math.floor(b / a), 10));
return this;
},
mod: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(a === 0 ? 0 : b % a);
return this;
},
not: function () {
stack.push(stack.pop() === 0 ? 1 : 0);
return this;
},
gt: function () {
var a = stack.pop();
var b = stack.pop();
stack.push(b > a ? 1 : 0 );
return this;
},
dup: function () {
stack.push(this.top() || 0);
return this;
},
swp: function () {
var a = stack.pop();
var b = stack.pop() || 0;
stack.push(a);
stack.push(b);
return this;
}
};
}()),
playfield = (function (code) {
const DIRECTIONS = {
'R': [1, 0],
'L': [-1, 0],
'U': [0, -1],
'D': [0, 1]
};
var program = code.split("\n").map(function (s) { return s.split(''); } ),
direction = DIRECTIONS.R,
position = {
x: -1,
y: 0
},
token_at = function (x, y) {
return program[y][x];
},
token_set = function (x, y, v) {
program[y][x] = v;
return v;
},
current_token = function () {
return token_at(position.x, position.y);
},
advance = function () {
position.x += direction[0];
position.y += direction[1];
},
is_numeric = function (n) {
return n.toString() === parseInt(n, 10).toString();
},
turn = function (d) {
direction = DIRECTIONS[d];
};
return {
next: function () {
// returns an instruction and a parameter (optional)
advance();
switch ( current_token() ) {
case undefined:
case '@':
return undefined;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return {
'instruction': 'instant',
'value': parseInt(current_token(), 10)
};
case '>':
this.turn_right();
return this.next();
case '<':
this.turn_left();
return this.next();
case '^':
this.turn_up();
return this.next();
case 'v':
this.turn_down();
return this.next();
case '?':
turn(Object.keys(DIRECTIONS)[ Math.round(Math.random() * 100) % Object.keys(DIRECTIONS).length ]);
return this.next();
case '"':
advance();
var char_stack = [];
while ( current_token() !== '"' ) {
char_stack.push(current_token());
advance();
}
return {
'instruction': 'string',
'value': char_stack
}
case '#':
advance();
return this.next();
case " ":
return {
'instruction': 'noop'
}
break;
default:
return {
'instruction': current_token()
}
}
},
turn_left: function () {
turn('L');
return this;
},
turn_right: function () {
turn('R');
return this;
},
turn_up: function () {
turn('U');
return this;
},
turn_down: function () {
turn('D');
return this;
},
put_char: function (x, y, v) {
return token_set(x, y, String.fromCharCode(v));
},
get_charcode: function (x, y) {
return token_at(x, y).charCodeAt(0);
}
};
}(code)),
output_stream = (function () {
var buffer = [];
return {
put: function (c) {
buffer.push(c);
return this;
},
put_as_int: function (c) {
return this.put(c.toString());
},
put_as_char: function (c) {
return this.put(String.fromCharCode(c));
},
toString: function () {
return buffer.join('');
}
};
}());
while ( true ) {
var ins = playfield.next();
if ( ins === undefined ) break;
switch ( ins.instruction ) {
case 'instant':
machine.push(ins.value);
break;
case 'string':
ins.value.forEach(function (c) {
machine.push(c.charCodeAt(0));
});
break;
case '+':
machine.add();
break;
case '-':
machine.sub();
break;
case '*':
machine.mul();
break;
case '/':
machine.div();
break;
case '%':
machine.mod();
break;
case '!':
machine.not();
break;
case '`':
machine.gt();
break;
case '_':
if ( machine.pop() === 0 ) {
playfield.turn_right();
}
else {
playfield.turn_left();
}
break;
case '|':
if ( machine.pop() === 0 ) {
playfield.turn_down();
}
else {
playfield.turn_up();
}
break;
case ':':
machine.dup();
break;
case '\\':
machine.swp();
break;
case '$':
machine.pop();
break;
case '.':
output_stream.put_as_int(machine.pop());
break;
case ',':
output_stream.put_as_char(machine.pop());
break;
case 'p':
var y = machine.pop();
var x = machine.pop();
var v = machine.pop();
playfield.put_char(x, y, v);
break;
case 'g':
var y = machine.pop();
var x = machine.pop();
var v = playfield.get_charcode(x, y);
machine.push(v);
break;
default:
// no-op
}
}
return output_stream.toString();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment