Repository navigation
Expand file tree
/
Copy pathexample.js
More file actions
63 lines (59 loc) · 1.97 KB
/
Copy pathexample.js
File metadata and controls
63 lines (59 loc) · 1.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
class Forth {
constructor(){
this.stack = [];
this.commands = Forth.basicCommands();
}
evaluate(program) {
const words = program.toLowerCase().split(' ');
for (let t = 0; t < words.length; t++) {
const word = words[t];
if (/^-?\d+$/.test(word)) {
// numbers
this.stack.push(Number(word));
} else if (word === ':') {
// word definition
const semicolon = words.indexOf(';', t);
if (semicolon === -1) throw new Error('Unterminated definition');
this.defineCommand(words[t + 1], words.slice(t + 2, semicolon).join(' '));
t = semicolon;
} else {
// commands
const command = this.commands[word];
if (!command) throw new Error('Unknown command');
this.performCommand(command);
}
}
}
defineCommand(word, subprogram){
if (Forth.isKeyword(word)) throw new Error('Invalid definition');
this.commands[word] = {
arity: 0, // handled inside the call
execute: this.evaluate.bind(this, subprogram)
}
}
performCommand(command) {
if (command.arity > this.stack.length) throw new Error('Stack empty');
const args = this.stack.splice(this.stack.length - command.arity);
const vals = command.execute.apply(this, args);
this.stack.push.apply(this.stack, vals);
}
static isKeyword(word) {
return word === ':' || word === ';' || /^-?\d+$/.test(word);
}
static basicCommands() {
return {
'+': { arity: 2, execute: (a, b) => [a + b] },
'-': { arity: 2, execute: (a, b) => [a - b] },
'*': { arity: 2, execute: (a, b) => [a * b] },
'/': { arity: 2, execute: (a, b) => {
if (b === 0) throw new Error('Division by zero');
return [Math.floor(a / b)];
} },
dup: { arity: 1, execute: a => [a, a] },
drop: { arity: 1, execute: () => {} },
swap: { arity: 2, execute: (a, b) => [b, a] },
over: { arity: 2, execute: (a, b) => [a, b, a] }
};
}
}
export default Forth;