UNPKG

js-forth

Version:

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

1,663 lines (1,398 loc) 81.3 kB
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function (global){ var Forth = require("../kernel/forth.js"); function Repl() { "use strict"; var forth = Forth(); function loadForth(file) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { console.log(forth.run(xmlhttp.responseText)); showStack(); } }; xmlhttp.open("GET", file, true); xmlhttp.send(); } loadForth("forth/forth.fth"); loadForth("test/ans-forth-tests.fth"); var inputHistory = [""]; var historyCount = 0; var historySelection = 0; function useHistory(selection) { var inputNode = document.getElementById("input"); if (inputNode.value !== inputHistory[historySelection]) { historySelection = historyCount - 1; inputHistory[historyCount] = inputNode.value; } else { historySelection = Math.min(Math.max(selection, 0), inputHistory.length - 1); } inputNode.value = inputHistory[historySelection]; inputNode.selectionStart = inputNode.value.length; } function updateHistory(input) { // Remove duplicates for (var i = inputHistory.length - 1; i >= 0; i--) { if (inputHistory[i] === input) { inputHistory.splice(i, 1); historyCount--; } } inputHistory[historyCount] = input; historyCount = inputHistory.length; historySelection = inputHistory.length; inputHistory.push(""); } function createOutputNode(icon, text, className) { var outputNode = document.createElement("div"); var textNode = document.createElement("textarea"); textNode.className = className; textNode.readOnly = true; textNode.cols = 80; text = icon + " " + text; // Roughly guess the number of rows by assuming lines wrap every 80 characters textNode.rows = text.split("\n").map(function(l) { return (l.length / 80) + 1; }).reduce(function(p, c) { return p + c; }, 0); textNode.value = text; outputNode.appendChild(textNode); document.getElementById("output").appendChild(outputNode); } function runforth() { var inputNode = document.getElementById("input"); var input = inputNode.value.trim(); if (input) { updateHistory(input); createOutputNode("\u2192", input, "user-output"); try { var output = forth.run(input); if (output) { createOutputNode("\u2190", output, "forth-output"); } } catch (err) { createOutputNode("X", err, "error"); throw err; } finally { showStack(); inputNode.value = ""; var outputNode = document.getElementById("output"); outputNode.scrollTop = outputNode.scrollHeight; } } } function showStack() { var stack = forth.stack; var stackNode = document.getElementById("stack"); // Clear stack while (stackNode.firstChild) stackNode.removeChild(stackNode.firstChild); for (var i = 1; i <= stack.length(); i++) { var element = document.createElement("span"); element.className = "stack-element"; element.textContent = String(stack.peek(i)); stackNode.appendChild(element); } } return { interpret: function(event) { if (event.keyCode == 13 && !event.shiftKey) runforth(); else if (event.keyCode == 80 && event.ctrlKey) useHistory(historySelection - 1); else if (event.keyCode == 78 && event.ctrlKey) useHistory(historySelection + 1); } }; } global.repl = Repl(); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"../kernel/forth.js":6}],2:[function(require,module,exports){ function ComparisonOperations(f) { f.defjs("true", function _true() { f.stack.push(true); }); f.defjs("false", function _false() { f.stack.push(false); }); f.defjs("and", function and() { var first = f.stack.pop(); f.stack.push(f.stack.pop() & first); }); f.defjs("or", function or() { var first = f.stack.pop(); f.stack.push(f.stack.pop() | first); }); f.defjs("xor", function xor() { var first = f.stack.pop(); f.stack.push(f.stack.pop() ^ first); }); f.defjs("invert", function invert() { f.stack.push(~f.stack.pop()); }); f.defjs("=", function equal() { var first = f.stack.pop(); f.stack.push(f.stack.pop() == first); }); f.defjs("<>", function notEqual() { var first = f.stack.pop(); f.stack.push(f.stack.pop() != first); }); f.defjs("<", function lessThan() { var first = f.stack.pop(); f.stack.push(f.stack.pop() < first); }); f.defjs(">", function greaterThan() { var first = f.stack.pop(); f.stack.push(f.stack.pop() > first); }); f.defjs("<=", function lessThanEqual() { var first = f.stack.pop(); f.stack.push(f.stack.pop() <= first); }); f.defjs(">=", function greaterThanEqual() { var first = f.stack.pop(); f.stack.push(f.stack.pop() >= first); }); return f; } module.exports = ComparisonOperations; },{}],3:[function(require,module,exports){ function ControlStructures(f) { // if, else, then f.defjs("jump", function jump() { f.instructionPointer += f.dataSpace[f.instructionPointer]; }); f.defjs("jumpIfFalse", function jumpIfFalse() { if (!f.stack.pop()) { f.instructionPointer += f.dataSpace[f.instructionPointer]; } else { f.instructionPointer++; // Skip the offset } }); // do, loop, +loop, unloop, leave, i, j function _do() { f.returnStack.push(f.dataSpace[f.instructionPointer++]); var top = f.stack.pop(); f.returnStack.push(f.stack.pop()); f.returnStack.push(top); } f.defjs("do", function compileDo() { f.dataSpace.push(_do); f.dataSpace.push(0); // Dummy endLoop f.stack.push(f.dataSpace.length - 1); }, true); // Immediate function plusLoop() { var step = f.stack.pop(); var index = f.returnStack.pop(); var limit = f.returnStack.pop(); if (index < limit && index + step < limit || index >= limit && index + step >= limit) { f.returnStack.push(limit); f.returnStack.push(index + step); f.instructionPointer += f.dataSpace[f.instructionPointer]; } else { f.returnStack.pop(); f.instructionPointer++; } } var compilePlusLoop = f.defjs("+loop", function compilePlusLoop() { f.dataSpace.push(plusLoop); var doPosition = f.stack.pop(); f.dataSpace.push(doPosition - f.dataSpace.length + 1); f.dataSpace[doPosition] = f.dataSpace.length; }, true); // Immediate f.defjs("loop", function loop() { f.dataSpace.push(f._lit); f.dataSpace.push(1); compilePlusLoop(); }, true); // Immediate f.defjs("unloop", function unloop() { f.returnStack.pop(); f.returnStack.pop(); f.returnStack.pop(); }); f.defjs("leave", function leave() { f.returnStack.pop(); f.returnStack.pop(); f.instructionPointer = f.returnStack.pop(); }); f.defjs("i", function i() { f.stack.push(f.returnStack.peek()); }); f.defjs("j", function j() { f.stack.push(f.returnStack.peek(4)); }); // recurse f.defjs("recurse", function recurse() { f.dataSpace.push(f.dataSpace[f._latest() + 1]); }, true); // Immediate // does function _does() { var wordPosition = f._latest(); var doDoesPosition = f.instructionPointer; f.dataSpace[wordPosition + 1] = function doDoes() { f.stack.push(wordPosition + 2); f.returnStack.push(f.instructionPointer); f.instructionPointer = doDoesPosition; }; f.instructionPointer = f.returnStack.pop(); } f.defjs("does>", function compileDoes() { f.dataSpace.push(_does); }, true); // Immediate return f; } module.exports = ControlStructures; },{}],4:[function(require,module,exports){ var Stack = require("./stack.js"); function Data(f) { f.instructionPointer = 0; f.dataSpace = []; f.returnStack = new Stack("Return Stack"); f.stack = new Stack("Stack"); return f; } module.exports = Data; },{"./stack.js":14}],5:[function(require,module,exports){ 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 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.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("", 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 word = f.stack.pop(); if (typeof word === "number") { var startPosition = word; 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(word); 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 = !wordDefinition.immediate; }, true); // Immediate 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._latest = latest f._lit = _lit; return f; } module.exports = Definitions; },{}],6:[function(require,module,exports){ var Data = require("./data.js"); var Definitions = require("./definitions.js"); var NumericOperations = require("./numeric-operations.js"); var BooleanOperations = require("./boolean-operations.js"); var StackOperations = require("./stack-operations.js"); var MemoryOperations = require("./memory-operations.js"); var ControlStructures = require("./control-structures.js"); var JsInterop = require("./js-interop.js"); var Input = require("./input.js"); var Output = require("./output.js") var Interpreter = require("./interpreter.js") function Forth() { var forth = {}; Data(forth); Definitions(forth); Input(forth); NumericOperations(forth); BooleanOperations(forth); StackOperations(forth); MemoryOperations(forth); ControlStructures(forth); Output(forth); JsInterop(forth); Interpreter(forth); return forth; } module.exports = Forth; },{"./boolean-operations.js":2,"./control-structures.js":3,"./data.js":4,"./definitions.js":5,"./input.js":7,"./interpreter.js":8,"./js-interop.js":9,"./memory-operations.js":10,"./numeric-operations.js":11,"./output.js":12,"./stack-operations.js":13}],7:[function(require,module,exports){ var EndOfInput = (function() {})(); function InputWindow(input, startPosition, endPosition, toIn) { var inputBufferPosition = startPosition; var inputBufferLength = -1; refill(); function refill() { inputBufferPosition += inputBufferLength + 1; inputBufferLength = input.substring(inputBufferPosition).search(/\n/); if (inputBufferLength == -1 || inputBufferPosition + inputBufferLength > endPosition) inputBufferLength = endPosition - inputBufferPosition; toIn(0); return inputBufferPosition < endPosition; } function readKey() { if (toIn() > inputBufferLength) { if (!refill()) throw EndOfInput; } var keyPosition = inputBufferPosition + toIn(); toIn(toIn() + 1); if (keyPosition < endPosition) { return input.charAt(keyPosition); } else { return " "; } } function parse(delimiter) { if (typeof delimiter === "number") delimiter = String.fromCharCode(delimiter); var address = inputBufferPosition + toIn(); var length = 0; var result = ""; if (toIn() <= inputBufferLength) { var key = readKey(); while (key !== delimiter) { length++; result += key; key = readKey(); } } else { refill(); } return [address, length, result]; } function readWord(delimiter) { if (toIn() >= inputBufferLength) { refill(); } delimiter = delimiter || /\s/; var word = ""; var key = readKey(); // Skip leading delimiters while (key.match(delimiter)) key = readKey(); while (!key.match(delimiter) && toIn() <= inputBufferLength) { word += key; key = readKey(); } return word; } function source() { return [inputBufferPosition, inputBufferLength]; } function inputBuffer() { return input.substring(inputBufferPosition, inputBufferPosition + inputBufferLength); } function subInput(position, length) { return InputWindow(input, position, position + length, toIn); } function charCodeAt(index) { return input.charCodeAt(index); } return { readWord: readWord, readKey: readKey, parse: parse, refill: refill, inputBuffer: inputBuffer, source: source, charCodeAt: charCodeAt, subInput: subInput }; } function Input(f) { f._base = f.defvar("base", 10); // Input buffer pointer var toIn = f.defvar(">in", 0); // Address offset to indicate input addresses var INPUT_SOURCE = 1 << 31; f.defjs("source", function source() { var positionLength = f._currentInput.source(); f.stack.push(INPUT_SOURCE + positionLength[0]); f.stack.push(positionLength[1]); }); f.defjs("refill", function refill() { f.stack.push(f._currentInput.refill()); }); f.defjs("key", function key() { f.stack.push(f._currentInput.readKey().charCodeAt(0)); }); f.defjs("parse", function parse() { var addressLength = f._currentInput.parse(f.stack.pop()); f.stack.push(INPUT_SOURCE + addressLength[0]); f.stack.push(addressLength[1]); }); function readWord(delimiter) { return f._currentInput.readWord(delimiter); }; var wordBufferStart = f.dataSpace.length; f.dataSpace.length += 32; f.defjs("word", function word() { var delimiter = f.stack.pop(); if (typeof delimiter === "number") delimiter = String.fromCharCode(delimiter); f.stack.push(wordBufferStart); var word = readWord(delimiter); var length = Math.min(word.length, 31); f.dataSpace[wordBufferStart] = length; for (var i = 0; i < length; i++) { f.dataSpace[wordBufferStart + i + 1] = word.charCodeAt(i); } }); f.defjs("char", function char() { f.stack.push(readWord().charCodeAt(0)); }); f.defjs("accept", function accept() { var savedInput = f._currentInput; var savedToIn = toIn(); var maxLength = f.stack.pop(); var address = f.stack.pop(); f.currentInstruction = function acceptCallback() { var received = f._currentInput.inputBuffer().substring(0, maxLength).split("\n")[0]; f.stack.push(received.length); for (var i = 0; i < received.length; i++) { f._setAddress(address + i, received[i]); } f._currentInput = savedInput; toIn(savedToIn); }; throw Input.EndOfInput; }); function _parseInt(string, base) { var int = 0; if (string[0] !== "-") { // Positive for (var i = 0; i < string.length; i++) { int *= base; int += parseInt(string[i], base); } return int; } else { for (var j = 1; j < string.length; j++) { int *= base; int -= parseInt(string[j], base); } return int; } } // Parse a float in the provide base function _parseFloat(string) { var base = f._base(); //split the string at the decimal point string = string.split(/\./); //if there is nothing before the decimal point, make it 0 if (string[0] === '') { string[0] = "0"; } //if there was a decimal point & something after it if (string.length > 1 && string[1] !== '') { var fractionLength = string[1].length; string[1] = _parseInt(string[1], base); string[1] *= Math.pow(base, -fractionLength); var int = _parseInt(string[0], base); if (int >= 0) return int + string[1]; else return int - string[1]; } //if there wasn't a decimal point or there was but nothing was after it return _parseInt(string[0], base); } var inputString = ""; function newInput(input) { var startPosition = inputString.length; inputString += input; f._currentInput = InputWindow(inputString, startPosition, inputString.length, toIn); } var inputStack = []; function subInput(position, length) { inputStack.push({ input: f._currentInput, toIn: toIn() }); f._currentInput = f._currentInput.subInput(position, length); } function popInput() { var savedInput = inputStack.pop(); f._currentInput = savedInput.input; toIn(savedInput.toIn); } f._readWord = readWord; f._newInput = newInput; f._subInput = subInput; f._popInput = popInput; f._parseFloat = _parseFloat; f._INPUT_SOURCE = INPUT_SOURCE; return f; } module.exports = Input; module.EndOfInput = EndOfInput; },{}],8:[function(require,module,exports){ var Input = require("./input.js"); function Interpreter(f) { function run(input) { f._newInput(input); f._output = ""; try { runInterpreter() } catch (err) { if (err !== Input.EndOfInput) { console.error("Exception " + err + " at:\n" + printStackTrace()); console.error(f._currentInput.inputBuffer()); console.error(f._output); f.currentInstruction = quit; f.stack.clear(); throw err; } } return f._output; } function runInterpreter() { // 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++]; } } 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.defjs("evaluate", function evaluate() { var length = f.stack.pop(); var position = f.stack.pop() - f._INPUT_SOURCE; f._subInput(position, length); var savedInstructionPointer = f.instructionPointer; f.currentInstruction = interpret; try { runInterpreter(); } catch (err) { if (err == Input.EndOfInput) { f._popInput(); f.instructionPointer = savedInstructionPointer; } else { throw err; } } }); function interpretWord() { var 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._parseFloat(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; var interpret = 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('"')[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; },{"./input.js":7}],9:[function(require,module,exports){ (function (global){ function JsInterop(f) { // Interop // - new with params js .new{1} // - global variable access js /document // - array access js .0.2 // - property access/setting js .name js .name! // - function calling js .sin{1} .{2} >> obj = pop, f = obj[name], f.call(obj, pop(), pop()) // - method calling js /document.getElementById{1} // // When compiling it should resolve global names immediately. function jsNewCall(path) { var constructor = f.stack.pop(); var argsCount = parseInt(path.match(/\{(\d*)\}/)[1] || 0); var args = [null]; // new replaces the first argument with this for (var j = 0; j < argsCount; j++) { args.push(f.stack.pop()); } // Use new operator with any number of arguments return new(Function.prototype.bind.apply(constructor, args))(); } function jsFunctionCall(path) { var argsCount = parseInt(path.match(/\{(\d*)\}/)[1] || 0); var obj = f.stack.pop(); path = path.match(/[^\{]*/)[0]; var func = path ? obj[path] : obj; var args = []; for (var j = 0; j < argsCount; j++) { args.push(f.stack.pop()); } return func.apply(obj, args); } var jsAssignmentRegex = /(^[A-Za-z$_][\w$_]*!$)|(^\d+!$)/; // name! var jsNewCallRegex = /new\{\d*\}$/; // new{2} var jsFunctionCallRegex = /((^[A-Za-z$_][\w$_]*)|(^\d+))?\{\d*\}$/; // getElementById{1} var globl = (typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document) ? window : global; function jsInterop(js) { if (js.startsWith("/")) { // Add global to f.stack f.stack.push(globl); } else if (!js.startsWith(".")) { throw "js interop call must start with '/' or '.'"; } var paths = js.length > 1 ? js.substring(1).split(".") : []; for (var i = 0; i < paths.length; i++) { var path = paths[i]; if (path.match(jsAssignmentRegex)) { f.stack.pop()[path.substring(0, path.length - 1)] = f.stack.pop(); } else if (path.match(jsNewCallRegex)) { f.stack.push(jsNewCall(path)); } else if (path.match(jsFunctionCallRegex)) { f.stack.push(jsFunctionCall(path)); } else { // Property access f.stack.push(f.stack.pop()[path]); } } } var JS = f.defjs("js", function js() { jsInterop(f.stack.pop()); }); f.defjs("js", function js() { if (f.compiling()) { f.dataSpace.push(f._lit); f.dataSpace.push(f._readWord()); f.dataSpace.push(JS); } else { jsInterop(f._readWord()); } }, true); return f; } module.exports = JsInterop; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],10:[function(require,module,exports){ function MemoryOperations(f) { function getAddress(address) { if (address < 0) { return f._currentInput.charCodeAt(address - f._INPUT_SOURCE); } else { var value = f.dataSpace[address]; if (typeof value == "string") return value.charCodeAt(0); else return value; } } function setAddress(address, value) { if (address < 0) { throw "Illegal attempt to change input"; } else { f.dataSpace[address] = value; } } f.defjs("!", function store() { var address = f.stack.pop(); var data = f.stack.pop(); setAddress(address, data); }); f.defjs("@", function fetch() { var address = f.stack.pop(); f.stack.push(getAddress(address)); }); f.defjs("+!", function addStore() { var address = f.stack.pop(); var data = f.stack.pop(); f.dataSpace[address] = f.dataSpace[address] + data; }); f.defjs("-!", function subtractStore() { var address = f.stack.pop(); var data = f.stack.pop(); f.dataSpace[address] = f.dataSpace[address] - data; }); f.defjs("here", function here() { f.stack.push(f.dataSpace.length); }); f._getAddress = getAddress; f._setAddress = setAddress; return f; } module.exports = MemoryOperations; },{}],11:[function(require,module,exports){ var Long = require("long") function NumericOperations(f) { f.defjs("+", function plus() { var first = f.stack.pop(); f.stack.push(f.stack.pop() + first | 0); }); f.defjs("-", function minus() { var first = f.stack.pop(); f.stack.push(f.stack.pop() - first | 0); }); f.defjs("*", function multiply() { var first = f.stack.pop(); f.stack.push(f.stack.pop() * first | 0); }); f.defjs("/", function divide() { var first = f.stack.pop(); f.stack.push(f.stack.pop() / first); }); f.defjs("2*", function inc() { f.stack.push(f.stack.pop() << 1); }); f.defjs("2/", function inc() { f.stack.push(f.stack.pop() >> 1); }); f.defjs("mod", function mod() { var first = f.stack.pop(); f.stack.push(f.stack.pop() % first); }); var maxUInt = Math.pow(2, 32); f.defjs("m*", function() { var first = Long.fromInt(f.stack.pop()); var second = Long.fromInt(f.stack.pop()); var result = first.mul(second); f.stack.push(result.low); f.stack.push(result.high); }); f.defjs("um*", function() { var first = Long.fromInt(f.stack.pop(), true); var second = Long.fromInt(f.stack.pop(), true); var result = first.mul(second); f.stack.push(result.low); f.stack.push(result.high); }); f.defjs("um/mod", function unsignedDivideMod() { var divisor = f.stack.pop() >>> 0; var bigPart = f.stack.pop() >>> 0; var smallPart = f.stack.pop(); var quotient = (bigPart % divisor) * Math.floor(maxUInt / divisor) + Math.floor(smallPart / divisor); var mod = Math.floor((bigPart % divisor) + (maxUInt % divisor) + (smallPart % divisor) & divisor); f.stack.push(mod); f.stack.push(quotient); }); f.defjs("fm/mod", function flooredDivideMod() { var divisor = f.stack.pop(); var bigPart = f.stack.pop(); var smallPart = f.stack.pop(); var quotient = (bigPart % divisor) * Math.floor(maxUInt / divisor) + Math.floor(smallPart / divisor); var mod = Math.floor((bigPart % divisor) + (maxUInt % divisor) + (smallPart % divisor) & divisor); f.stack.push(mod); f.stack.push(quotient); }); f.defjs("sm/rem", function symmetricDivideRem() { var divisor = f.stack.pop(); var bigPart = f.stack.pop(); var smallPart = f.stack.pop(); var quotient = (bigPart % divisor) * Math.floor(maxUInt / divisor) + Math.trunc(smallPart / divisor); var rem = Math.floor((bigPart % divisor) + (maxUInt % divisor) + (smallPart % divisor) & divisor); f.stack.push(rem); f.stack.push(quotient); }); f.defjs("abs", function abs() { f.stack.push(Math.abs(f.stack.pop()) | 0); }); f.defjs("lshift", function lshift() { var shift = f.stack.pop(); var num = f.stack.pop(); f.stack.push(num << shift); }); f.defjs("rshift", function rshift() { var shift = f.stack.pop(); var num = f.stack.pop(); f.stack.push(num >>> shift); }); f.defjs("max", function max() { f.stack.push(Math.max(f.stack.pop(), f.stack.pop())); }); f.defjs("min", function min() { f.stack.push(Math.min(f.stack.pop(), f.stack.pop())); }); f.defjs("negate", function negate() { f.stack.push(-f.stack.pop()); }); return f; } module.exports = NumericOperations; },{"long":15}],12:[function(require,module,exports){ function Output(f) { f._output = ""; f.defjs("cr", function cr() { f._output += "\n"; }); f.defjs(".", function dot() { var value; var top = f.stack.pop(); if (typeof top === "undefined") value = "undefined"; else if (top === null) value = "null"; else value = top.toString(f._base()); // Output numbers in current base f._output += value + " "; }); f.defjs("emit", function emit() { var value = f.stack.pop(); if (typeof value === "number") f._output += String.fromCharCode(value); else f._output += value; }); f.defjs("type", function type() { var length = f.stack.pop(); var address = f.stack.pop(); for (var i = 0; i < length; i++) { var value = f._getAddress(address + i); if (typeof value === "number") { f._output += String.fromCharCode(value); } else f._output += value; } }); // Numeric output var maxUInt = Math.pow(2, 32); var numericOutputStart = f.dataSpace.length; var numericOutput = ""; f.dataSpace.length += 128; f.defjs("<#", function initialiseNumericOutput() { numericOutput = ""; }); f.defjs("hold", function hold() { var value = f.stack.pop(); if (typeof value === "number") value = String.fromCharCode(value); numericOutput += value; }); f.defjs("#>", function finishNumericOutput() { f.stack.pop(); f.stack.pop(); for (var i = 0; i < numericOutput.length; i++) { f.dataSpace[numericOutputStart + i] = numericOutput[numericOutput.length - i - 1]; } f.stack.push(numericOutputStart); f.stack.push(numericOutput.length); }); f.defjs("sign", function sign() { if (f.stack.pop() < 0) numericOutput += "-"; }); f.defjs("#", function writeNextNumericOutput() { var bigPart = f.stack.pop() >>> 0; var smallPart = f.stack.pop() >>> 0; var base = f._base(); numericOutput += Math.floor(smallPart % base).toString(base).toUpperCase(); smallPart = (bigPart % base) * Math.floor(maxUInt / base) + Math.floor(smallPart / base); bigPart = Math.floor(bigPart / base); f.stack.push(smallPart); f.stack.push(bigPart); }); f.defjs("#S", function writeAllNumericOutput() { var bigPart = f.stack.pop() >>> 0; var smallPart = f.stack.pop() >>> 0; var base = f._base(); if (smallPart > 0 || bigPart > 0) { while (smallPart > 0 || bigPart > 0) { numericOutput += Math.floor(smallPart % base).toString(base).toUpperCase(); smallPart = (bigPart % base) * Math.floor(maxUInt / base) + Math.floor(smallPart / base); bigPart = Math.floor(bigPart / base); } } else { numericOutput += "0"; } f.stack.push(0); f.stack.push(0); }); f.defjs(">number", function toNumber() { var base = f._base(); var length = f.stack.pop(); var address = f.stack.pop(); var bigPart = f.stack.pop() >>> 0; var smallPart = f.stack.pop() >>> 0; var unconverted = length; for (var i = 0; i < length; i++) { var next = parseInt(String.fromCharCode(f._getAddress(address)), base); if (isNaN(next)) { break; } else { address++; unconverted--; var temp = (smallPart * base) + next; smallPart = temp % maxUInt; bigPart = (bigPart * base) + Math.floor(temp / maxUInt); } } f.stack.push(smallPart); f.stack.push(bigPart); f.stack.push(address); f.stack.push(unconverted); }); return f; } module.exports = Output; },{}],13:[function(require,module,exports){ function StackOperations(f) { f.defjs("drop", function drop() { f.stack.pop(); }); f.defjs("swap", function swap() { var first = f.stack.pop(); var second = f.stack.pop(); f.stack.push(first); f.stack.push(second); }); f.defjs("dup", function dup() { f.stack.push(f.stack.peek()); }); f.defjs("over", function over() { f.stack.push(f.stack.peek(2)); }); f.defjs("rot", function rot() { var first = f.stack.pop(); var second = f.stack.pop(); var third = f.stack.pop(); f.stack.push(second); f.stack.push(first); f.stack.push(third); }); f.defjs("-rot", function backRot() { var first = f.stack.pop(); var second = f.stack.pop(); var third = f.stack.pop(); f.stack.push(first); f.stack.push(third); f.stack.push(second); }); f.defjs("2drop", function twoDrop() { f.stack.pop(); f.stack.pop(); }); f.defjs("2dup", function twoDup() { f.stack.push(f.stack.peek(2)); f.stack.push(f.stack.peek(2)); }); f.defjs("2over", function twoOver() { f.stack.push(f.stack.peek(4)); f.stack.push(f.stack.peek(4)); }); f.defjs("2swap", function twoSwap() { var first = f.stack.pop(); var second = f.stack.pop(); var third = f.stack.pop(); var fourth = f.stack.pop(); f.stack.push(second); f.stack.push(first); f.stack.push(fourth); f.stack.push(third); }); f.defjs("?dup", function nonZeroDup() { var first = f.stack.peek(); if (first !== 0) f.stack.push(first); }); f.defjs("depth", function depth() { f.stack.push(f.stack.length()); }); // Return f.stack f.defjs(">r", function toR() { f.returnStack.push(f.stack.pop()); }); f.defjs("r>", function rFrom() { f.stack.push(f.returnStack.pop()); }); f.defjs("r@", function rFetch() { f.stack.push(f.returnStack.peek()); }); f.defjs("2r>", function twoRFrom() { var top = f.returnStack.pop(); f.stack.push(f.returnStack.pop()); f.stack.push(top); }); f.defjs("2>r", function twoToR() { var top = f.stack.pop(); f.returnStack.push(f.stack.pop()); f.returnStack.push(top); }); f.defjs("2r@", function twoRFetch() { f.stack.push(f.returnStack.peek(2)); f.stack.push(f.returnStack.peek(1)); }); return f; } module.exports = StackOperations; },{}],14:[function(require,module,exports){ function Stack(name) { var data = []; this.pop = function() { if (data.length > 0) return data.pop(); else throw "Stack empty: " + name; }; this.push = function(element) { data.push(element); }; this.peek = function(offset) { var index = data.length - (offset || 1); if (0 <= index && index < data.length) return data[index]; else throw "Attempted to peek at invalid stack index " + index + ": " + name; }; this.length = function() { return data.length; }; this.clear = function() { data.length = 0; }; } module.exports = Stack; },{}],15:[function(require,module,exports){ /* Copyright 2013 Daniel Wirtz <dcode@dcode.io> Copyright 2009 The Closure Library Authors. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** * @license long.js (c) 2013 Daniel Wirtz <dcode@dcode.io> * Released under the Apache License, Version 2.0 * see: https://github.com/dcodeIO/long.js for details */ (function(global, factory) { /* AMD */ if (typeof define === 'function' && define["amd"]) define([], factory); /* CommonJS */ else if (typeof require === 'function' && typeof module === "object" && module && module["exports"]) module["exports"] = factory(); /* Global */ else (global["dcodeIO"] = global["dcodeIO"] || {})["Long"] = factory(); })(this, function() { "use strict"; /** * Constructs a 64 bit two's-complement integer, given its low and high 32 bit values as *signed* integers. * See the from* functions below for more convenient ways of constructing Longs. * @exports Long * @class A Long class for representing a 64 bit two's-complement integer value. * @param {number} low The low (signed) 32 bits of the long * @param {number} high The high (signed) 32 bits of the long * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @constructor */ function Long(low, high, unsigned) { /** * The low 32 bits as a signed value. * @type {number} * @expose */ this.low = low | 0; /** * The high 32 bits as a signed value. * @type {number} * @expose */ this.high = high | 0; /** * Whether unsigned or not. * @type {boolean} * @expose */ this.unsigned = !!unsigned; } // The internal representation of a long is the two given signed, 32-bit values. // We use 32-bit pieces because these are the size of integers on which // Javascript performs bit-operations. For operations like addition and // multiplication, we split each number into 16 bit pieces, which can easily be // multiplied within Javascript's floating-point representation without overflow // or change in sign. // // In the algorithms below, we frequently reduce the negative case to the // positive case by negating the input(s) and then post-processing the result. // Note that we must ALWAYS check specially whether those values are MIN_VALUE // (-2^63) because -MIN_VALUE == MIN_VALUE (since 2^63 cannot be represented as // a positive number, it overflows back into a negative). Not handling this // case would often result in infinite recursion. // // Common constant values ZERO, ONE, NEG_ONE, etc. are defined below the from* // methods on which they depend. /** * An indicator used to reliably determine if an object is a Long or not. * @type {boolean} * @const * @expose * @private */ Long.__isLong__; Object.defineProperty(Long.prototype, "__isLong__", { value: true, enumerable: false, configurable: false }); /** * @function * @param {*} obj Object * @returns {boolean} * @inner */ function isLong(obj) { return (obj && obj["__isLong__"]) === true; } /** * Tests if the specified object is a Long. * @function * @param {*} obj Object * @returns {boolean} * @expose */ Long.isLong = isLong; /** * A cache of the Long representations of small integer values. * @type {!Object} * @inner */ var INT_CACHE = {}; /** * A cache of the Long representations of small unsigned integer values. * @type {!Object} * @inner */ var UINT_CACHE = {}; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromInt(value, unsigned) { var obj, cachedObj, cache; if (unsigned) { value >>>= 0; if (cache = (0 <= value && value < 256)) { cachedObj = UINT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, (value | 0) < 0 ? -1 : 0, true); if (cache) UINT_CACHE[value] = obj; return obj; } else { value |= 0; if (cache = (-128 <= value && value < 128)) { cachedObj = INT_CACHE[value]; if (cachedObj) return cachedObj; } obj = fromBits(value, value < 0 ? -1 : 0, false); if (cache) INT_CACHE[value] = obj; return obj; } } /** * Returns a Long representing the given 32 bit integer value. * @function * @param {number} value The 32 bit integer in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value * @expose */ Long.fromInt = fromInt; /** * @param {number} value * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromNumber(value, unsigned) { if (isNaN(value) || !isFinite(value)) return unsigned ? UZERO : ZERO; if (unsigned) { if (value < 0) return UZERO; if (value >= TWO_PWR_64_DBL) return MAX_UNSIGNED_VALUE; } else { if (value <= -TWO_PWR_63_DBL) return MIN_VALUE; if (value + 1 >= TWO_PWR_63_DBL) return MAX_VALUE; } if (value < 0) return fromNumber(-value, unsigned).neg(); return fromBits((value % TWO_PWR_32_DBL) | 0, (value / TWO_PWR_32_DBL) | 0, unsigned); } /** * Returns a Long representing the given value, provided that it is a finite number. Otherwise, zero is returned. * @function * @param {number} value The number in question * @param {boolean=} unsigned Whether unsigned or not, defaults to `false` for signed * @returns {!Long} The corresponding Long value * @expose */ Long.fromNumber = fromNumber; /** * @param {number} lowBits * @param {number} highBits * @param {boolean=} unsigned * @returns {!Long} * @inner */ function fromBits(lowBits, high