UNPKG

js-forth

Version:

An implementation of [Forth](https://en.wikipedia.org/wiki/Forth_(programming_language)) in Javascript

136 lines (118 loc) 4.33 kB
var InputExceptions = require("./input-exceptions.js"); function Interpreter(f) { function run(input, outputCallback, sourceId) { f.outputCallback = outputCallback; f._newInput(input, sourceId || 0); f._output = ""; try { runInterpreter(); } catch (err) { if (err !== InputExceptions.WaitingOnInput) { console.error("Exception " + err + " at:\n" + printStackTrace()); console.error(f._currentInput.inputBuffer()); console.error(f._output); f.currentInstruction = quit; f.stack.clear(); outputCallback(err, f._output); throw err; } } outputCallback(null, f._output); } function runInterpreter() { // Run while there is still input to consume while (f._currentInput) { try { // As js doesn't support tail call optimisation the // run function uses a trampoline to execute forth code while (true) { f.currentInstruction(); f.currentInstruction = f.dataSpace[f.instructionPointer++]; } } catch (err) { if (err === InputExceptions.EndOfInput) { f._popInput(); } else { throw err; } } } } function printStackTrace() { var stackTrace = " " + f.currentInstruction.name + " @ " + (f.instructionPointer - 1); for (var i = f.returnStack.length - 1; i >= 0; i--) { var instruction = f.returnStack[i]; stackTrace += "\n " + f.dataSpace[instruction - 1].name + " @ " + (instruction - 1); } return stackTrace; } f._evaluate = f.defjs("evaluate", function evaluate() { var length = f.stack.pop(); var address = f.stack.pop(); if (address < 0) { var position = address - f._INPUT_SOURCE; f._subInput(position, length); } else { var string = ""; for (var i = 0; i < length; i++) { string += String.fromCharCode(f._getAddress(address + i)); } f._newInput(string, -1); } f.instructionPointer = interpretInstruction; }); function interpretWord() { var word = f._readWord(); while (!word) { if (!f._currentInput.refill()) throw InputExceptions.EndOfInput; word = f._readWord(); } var definition = f.findDefinition(word); if (definition) { if (!f.compiling() || definition.immediate) { f.dataSpace[definition.executionToken](); return; } else { f.dataSpace.push(f.dataSpace[definition.executionToken]); } } else { var num = f._parseFloatInBase(word); if (isNaN(num)) throw "Word not defined: " + word; if (f.compiling()) { f.dataSpace.push(f._lit); f.dataSpace.push(num); } else { f.stack.push(num); } } } var interpretInstruction = f.dataSpace.length + 1; f.defjs("interpret", function interpret() { f.instructionPointer = interpretInstruction; // Loop after interpret word is called interpretWord(); }); var quit = f.defjs("quit", function quit() { f.compiling(false); // Enter interpretation state f.returnStack.clear(); // Clear return stack f.instructionPointer = interpretInstruction; // Run the interpreter }); var abort = f.defjs("abort", function abort(error) { f.stack.clear(); throw error || ""; }); f.defjs('abort"', function abortQuote() { var error = f._currentInput.parse('"'.charCodeAt(0))[2]; f.dataSpace.push(function abortQuote() { if (f.stack.pop()) abort(error); }); }, true); // Immediate f.defjs("execute", function execute() { f.dataSpace[f.stack.pop()](); }); // Set initial instruction f.currentInstruction = quit; f.run = run; return f; } module.exports = Interpreter;