/* Functions in JavaScript have a length property, measuring
the number of named formal parameters. This means that a stack-based language
can tell how many items to pop off the stack to pass to the function.
Here is a very simple stack language, implemented in quasi-literate JavaScript.
(That is, you can copy this post out of your browser into a .js file and
run it in Node.) */
var interpreter = {/* The “Standard Library” is implemented as a dictionary of plain JavaScript functions. */
'+': function(a, b) { return [a + b]; },
'-': function(a, b) { return [a - b]; },
'dup': function(a) { return [a, a]; },
'swap': function(a, b) { return [b, a]; },
/* Must be wrapped. `console.log.length` === 0 */
'.': function(i) { console.log(i); },/* run is the entry point to the interpreter.
It resets the inputBuffer and stack fields. */
'run': function(program) {
this.inputBuffer = program;
this.stack = [];
while (this.inputBuffer) {
// read one word from inputBuffer & interpret it
this.interpret(this.word());
}
},/* word removes one word from the inputBuffer and returns it. */
'word': function(/* operates on inputBuffer, not stack */) {
var word = '';
while (this.inputBuffer) {
var ch = this.inputBuffer.charAt(0);
this.inputBuffer = this.inputBuffer.substr(1);
if (/^\s?$/.test(ch)) {
if (word) {
break;
}
} else {
word += ch;
}
}
return word;
},/* interpret decides how to handle each word.
- If the word is in the dictionary, execute it.
- If it is a number, push the number onto the stack.
- Otherwise throw an exception.
*/
'interpret': function(word) {
if (!word) return;
if (word in this) {
// it's defined, execute it.
this.execute(this[word]);
} else if (isFinite(word)) {
// it's a number
this.stack.push(parseFloat(word));
} else throw new Error('unknown word `' + word + '`');
},/*
execute provides the interoperability with JavaScript Functions.
It provides the top fn.length
items from the stack as arguments to the function.
If fn returns a value, the value is concatenated
back on top of the stack.
Multiple values can be returned in an array;
Array.prototype.concat will correctly concatenate
all of the values to this.stack in the intended order.
*/
'execute': function(fn) {
var len = fn.length;
// pop the top `len` items off the stack, in order
var applies = this.stack.splice(-len, len);
// apply the items to the Function
var results = fn.apply(this, applies);
if (results) {
// put any results back onto the stack
this.stack = this.stack.concat(results);
}
},};/* Unit tests to demonstrate usage */
var assert = require('assert');/* An empty program leaves an empty stack. */
interpreter.run('');
assert.deepEqual(interpreter.stack, []);/* The standard library: dup, -, +, swap */
interpreter.run('3 dup');
assert.deepEqual(interpreter.stack, [3, 3]);
interpreter.run("2 6 -");
assert.deepEqual(interpreter.stack, [-4]);
interpreter.run('5 8 +');
assert.deepEqual(interpreter.stack, [13]);
interpreter.run('10 20');
assert.deepEqual(interpreter.stack, [10, 20]);
interpreter.run('10 20 swap');
assert.deepEqual(interpreter.stack, [20, 10]);/* We can reflect on just about any part of the execution of the interpreter. */
interpreter.run('12 23');
assert.deepEqual(interpreter.stack, [12, 23]);
// continues.../* For example, calling interpret without run or word
can be used to single-step: */
// ...continued
interpreter.interpret('swap');
assert.deepEqual(interpreter.stack, [23, 12]);
// continues.../* We can execute a function that isn’t even in the dictionary: */
// ...continued
interpreter.execute(function(a, b) {
assert.deepEqual([a, b], [23, 12]);
return ['whoa', 'nelly'];
});
assert.deepEqual(interpreter.stack, ['whoa', 'nelly']);/* Interpreting the word 'execute' pops a function off the stack */
interpreter.stack = [function() { return ['WOW'] }];
interpreter.interpret('execute');
assert.deepEqual(interpreter.stack, ['WOW']);/* Use the word tokenizer function to put single-word Strings on the stack */
interpreter.run('word foo');
assert.deepEqual(interpreter.stack, ['foo']);/* The preexisting + operator can work with this new String data type */
interpreter.run('word Hello, word World! +');
assert.deepEqual(interpreter.stack, ['Hello,World!']);/* And interpret can be called on Strings on the stack */
interpreter.run('5 5 word + interpret');
assert.deepEqual(interpreter.stack, [10]);/* Even if the String is a result of some other computation! */
interpreter.run('8 9 word sw word ap + interpret');
assert.deepEqual(interpreter.stack, [9, 8]);