Created
November 2, 2017 16:27
-
-
Save thomaswilburn/8ebd1668c9b5098498b358ba3e6cb268 to your computer and use it in GitHub Desktop.
Terrible Forth in JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var testScript = ` | |
[ double dup + ] | |
[ quadruple double double ] | |
1 2 + print | |
double print | |
quadruple print | |
2 jmp | |
3 + | |
4 + | |
if 255 print 0 then | |
if 10 print then | |
`; | |
var Forth = function() { | |
this.stack = []; | |
this.dictionary = Object.create(Forth.BASE); | |
this.memory = new Array(100); | |
this.mode = Forth.modes.READING; | |
this.pc = 0; | |
this.code = []; | |
}; | |
Forth.modes = { | |
READING: 0, | |
COMPILING: 1 | |
}; | |
Forth.BASE = { | |
print: function(self) { | |
console.log(self.top()); | |
}, | |
jmp: function(self) { | |
var amount = self.stack.pop(); | |
self.pc += amount; | |
}, | |
"+": function(self) { | |
var [a, b] = self.pop(2); | |
self.stack.push(a + b); | |
}, | |
"-": function(self) { | |
var [a, b] = self.pop(2); | |
self.stack.push(a - b); | |
}, | |
dup: function(self) { | |
var val = self.stack.pop(); | |
self.stack.push(val, val); | |
}, | |
noop: [], | |
then: [], | |
if: function(self) { | |
var then = self.code.indexOf("then", self.pc); | |
var condition = self.stack[self.stack.length - 1]; | |
if (condition === 0) { | |
self.pc = then; | |
} | |
} | |
} | |
Forth.prototype = { | |
top: function() { | |
return this.stack[this.stack.length - 1]; | |
}, | |
pop: function(length) { | |
var vals = []; | |
for (var i = 0; i < length; i++) { | |
vals.unshift(this.stack.pop()); | |
} | |
return vals; | |
}, | |
run: function(word) { | |
if (typeof word == "function") { | |
word(this); | |
} else if (word instanceof Array) { | |
word.forEach(w => this.word(w)); | |
} else { | |
throw word | |
} | |
}, | |
word: function(word) { | |
word = this.cast(word); | |
var lookup = this.dictionary[word]; | |
if (lookup) { | |
this.run(lookup); | |
} else if (this.isLiteral(word)) { | |
this.stack.push(word); | |
} else { | |
throw "Unrecognized word: " + word | |
} | |
}, | |
evaluate: function(code) { | |
this.pc = 0; | |
this.code = code.trim().split(/\s+/g); | |
var buffer = []; | |
for (; this.pc < this.code.length; this.pc++) { | |
var w = this.code[this.pc]; | |
if (w == "[") { | |
this.mode = Forth.modes.COMPILING; | |
} else if (w == "]") { | |
this.mode = Forth.modes.READING; | |
if (buffer.length) { | |
var name = buffer.shift(); | |
this.dictionary[name] = buffer; | |
buffer = []; | |
} | |
} else { | |
if (this.mode == Forth.modes.COMPILING) { | |
buffer.push(w); | |
} else { | |
this.word(w); | |
} | |
} | |
} | |
}, | |
isLiteral: function(word) { | |
return typeof word == "number"; | |
}, | |
cast: function(word) { | |
if (typeof word == "string") { | |
word = word.trim(); | |
if (word.match(/^\d+$/)) { | |
return parseFloat(word); | |
} | |
} | |
return word; | |
} | |
}; | |
var f = new Forth(); | |
f.evaluate(testScript); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment