js-forth
Version:
An implementation of [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) in Javascript
199 lines (167 loc) • 5.86 kB
JavaScript
function Header(link, name, immediate, hidden, executionToken) {
this.link = link;
this.name = name;
this.immediate = immediate || false;
this.hidden = hidden || false;
this.executionToken = executionToken;
}
function Definitions(f) {
// Temporary definition until latest is defined as a variable
var latest = function latest() {
return null;
};
function defheader(name, immediate, hidden) {
f.dataSpace.push(new Header(latest(), name, immediate, hidden, f.dataSpace.length + 1));
latest(f.dataSpace.length - 1);
}
f.defjs = function defjs(name, fn, immediate, displayName) {
defheader(displayName || name, immediate);
f.dataSpace.push(fn);
return fn;
};
f.defvar = function defvar(name, initial) {
defheader(name);
var varAddress = f.dataSpace.length + 1;
f.dataSpace.push(function variable() {
f.stack.push(varAddress);
});
f.dataSpace.push(initial);
return function(value) {
if (value !== undefined)
f.dataSpace[varAddress] = value;
else
return f.dataSpace[varAddress];
};
};
latest = f.defvar("latest", f.dataSpace.length); // Replace existing function definition
f.compiling = f.defvar("state", 0);
f.compileEnter = function compileEnter(name) {
var instruction = f.dataSpace.length + 1;
var enter;
try {
enter = eval(`(
function ${name}() {
f.returnStack.push(f.instructionPointer);
f.instructionPointer = instruction;
})
`);
} catch (e) {
// Failback for names that are invalid identifiers
enter = function enter() {
f.returnStack.push(f.instructionPointer);
f.instructionPointer = instruction;
};
}
f.dataSpace.push(enter);
return enter;
};
f.findDefinition = function findDefinition(word) {
var current = latest();
while (current !== null) {
var wordDefinition = f.dataSpace[current];
// Case insensitive
if (wordDefinition.name && wordDefinition.name.toLowerCase() == word.toLowerCase() && !wordDefinition.hidden)
return wordDefinition;
current = wordDefinition.link;
}
return current;
};
f.defjs(":", function colon() {
var name = f._readWord();
defheader(name, false, true);
f.compileEnter(name);
f.compiling(true);
});
f.defjs(":noname", function noname() {
defheader(null, false, true);
f.stack.push(f.dataSpace.length);
f.compileEnter("_noname_");
f.compiling(true);
});
var exit = f.defjs("exit", function exit() {
f.instructionPointer = f.returnStack.pop();
});
f.defjs(";", function semicolon() {
f.dataSpace.push(exit);
f.dataSpace[latest()].hidden = false;
f.compiling(false);
}, true); // Immediate
f.defjs("find", function find() {
var input = f.stack.pop();
var word = input;
if (typeof input === "number") {
var startPosition = input;
var length = f._getAddress(startPosition);
word = "";
for (var i = 1; i <= length; i++) {
word += String.fromCharCode(f._getAddress(startPosition + i));
}
}
var definition = f.findDefinition(word);
if (definition) {
f.stack.push(definition.executionToken);
f.stack.push(definition.immediate ? 1 : -1);
} else {
f.stack.push(input);
f.stack.push(0);
}
});
// Converts an execution token into the data field address
f.defjs(">body", function dataFieldAddress() {
f.stack.push(f.stack.pop() + 1);
});
f.defjs("create", function create() {
defheader(f._readWord());
var dataFieldAddress = f.dataSpace.length + 1;
f.dataSpace.push(function pushDataFieldAddress() {
f.stack.push(dataFieldAddress);
});
});
f.defjs("allot", function allot() {
f.dataSpace.length += f.stack.pop();
});
f.defjs(",", function comma() {
f.dataSpace.push(f.stack.pop());
});
f.defjs("compile,", function compileComma() {
f.dataSpace.push(f.dataSpace[f.stack.pop()]);
});
f.defjs("[", function lbrac() {
f.compiling(false); // Immediate
}, true); // Immediate
f.defjs("]", function rbrac() {
f.compiling(true); // Compile
});
f.defjs("immediate", function immediate() {
var wordDefinition = f.dataSpace[latest()];
wordDefinition.immediate = true;
});
f.defjs("hidden", function hidden() {
var wordDefinition = f.dataSpace[f.stack.pop()];
wordDefinition.hidden = !wordDefinition.hidden;
});
f.defjs("'", function tick() {
f.stack.push(f.findDefinition(f._readWord()).executionToken);
});
var _lit = f.defjs("lit", function lit() {
f.stack.push(f.dataSpace[f.instructionPointer]);
f.instructionPointer++;
});
f.defjs("[']", function bracketTick() {
f.dataSpace.push(f._lit);
f.dataSpace.push(f.findDefinition(f._readWord()).executionToken);
}, true);
f.defjs("marker", function marker() {
var savedLatest = latest();
var savedLength = f.dataSpace.length;
defheader(f._readWord());
f.dataSpace.push(function marker() {
latest(savedLatest);
f.dataSpace.length = savedLength;
});
});
f._latest = latest;
f._lit = _lit;
return f;
}
module.exports = Definitions;