// runtime.js: the main runtime library for whalesong. // if(this['plt'] === undefined) { this['plt'] = {}; } // All of the values here are namespaced under "plt.runtime". (function(scope) { var runtime = {}; scope['runtime'] = runtime; var helpers = plt.helpers; var types = plt.types; // Consumes a class and creates a predicate that recognizes subclasses. var makeClassPredicate = function(aClass) { return function(x) { return x instanceof aClass; }; }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // We try to isolate the effect of external modules: all the identifiers we // pull from external modules should be listed here, and should otherwise not // show up outside this section! var isNumber = jsnums.isSchemeNumber; var isNatural = types.isNatural; var isPair = types.isPair; var isList = types.isList; var isVector = types.isVector; var isString = types.isString; var equals = types.equals; var NULL = types.EMPTY; var VOID = types.VOID; var makeVector = types.vector; var makeList = types.list; var makePair = types.pair; var heir = helpers.heir; var toDomNode = helpers.toDomNode; var toWrittenString = helpers.toWrittenString; var toDisplayedString = helpers.toDisplayedString; var makeBox = types.box; var isBox = types.isBox; //////////////////////////////////////////////////////////////////////] var isNonNegativeReal = function(x) { return jsnums.isReal(x) && jsnums.greaterThanOrEqual(x, 0); }; // This value will be dynamically determined. // See findStackLimit later in this file. var STACK_LIMIT_ESTIMATE = 100; var Machine = function() { this.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE; this.val = undefined; this.proc = undefined; this.argcount = undefined; this.env = []; this.control = []; // Arrayof (U Frame CallFrame PromptFrame) this.running = false; this.modules = {}; // String -> ModuleRecord this.mainModules = []; // Arrayof String this.params = { // currentDisplayer: DomNode -> Void // currentDisplayer is responsible for displaying to the browser. 'currentDisplayer': function(MACHINE, domNode) { $(domNode).appendTo(document.body); }, // currentErrorDisplayer: DomNode -> Void // currentErrorDisplayer is responsible for displaying errors to the browser. 'currentErrorDisplayer': function(MACHINE, domNode) { $(domNode).appendTo(document.body); }, 'currentInspector': plt.baselib.inspectors.DEFAULT_INSPECTOR, 'currentOutputPort': new StandardOutputPort(), 'currentErrorPort': new StandardErrorPort(), 'currentSuccessHandler': function(MACHINE) {}, 'currentErrorHandler': function(MACHINE, exn) { MACHINE.params.currentErrorDisplayer( MACHINE, toDomNode(exn)); }, 'currentNamespace': {}, // These parameters control how often // control yields back to the browser // for response. The implementation is a // simple PID controller. // // To tune this, adjust desiredYieldsPerSecond. // Do no touch numBouncesBeforeYield or // maxNumBouncesBeforeYield, because those // are adjusted automatically by the // recomputeMaxNumBouncesBeforeYield // procedure. 'desiredYieldsPerSecond': 5, 'numBouncesBeforeYield': 2000, // self-adjusting 'maxNumBouncesBeforeYield': 2000, // self-adjusting 'currentPrint': new Closure( function(MACHINE) { if(--MACHINE.callsBeforeTrampoline<0) { throw arguments.callee; } var elt = MACHINE.env[MACHINE.env.length - 1]; var outputPort = MACHINE.params.currentOutputPort; if (elt !== VOID) { outputPort.writeDomNode(MACHINE, toDomNode(elt, 'print')); outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); } return finalizeClosureCall(MACHINE, VOID); }, 1, [], "printer") }; this.primitives = Primitives; }; // Finalize the return from a closure. This is a helper function // for those who implement Closures by hand. // // If used in the body of a Closure, it must be in tail // position. This finishes the closure call, and does the following: // // * Clears out the existing arguments off the stack frame // * Sets up the return value // * Jumps either to the single-value return point, or the multiple-value // return point. // // I'd personally love for this to be a macro and avoid the // extra function call here. var finalizeClosureCall = function(MACHINE) { MACHINE.callsBeforeTrampoline--; var frame, i, returnArgs = [].slice.call(arguments, 1); // clear out stack space // TODO: replace with a splice. for(i = 0; i < MACHINE.argcount; i++) { MACHINE.env.pop(); } if (returnArgs.length === 1) { MACHINE.val = returnArgs[0]; frame = MACHINE.control.pop(); return frame.label(MACHINE); } else if (returnArgs.length === 0) { MACHINE.argcount = 0; frame = MACHINE.control.pop(); return frame.label.multipleValueReturn(MACHINE); } else { MACHINE.argcount = returnArgs.length; MACHINE.val = returnArgs.shift(); // TODO: replace with a splice. for(i = 0; i < MACHINE.argcount - 1; i++) { MACHINE.env.push(returnArgs.pop()); } frame = MACHINE.control.pop(); return frame.label.multipleValueReturn(MACHINE); } }; var ModuleRecord = function(name, label) { this.name = name; this.label = label; this.isInvoked = false; this.prefix = false; this.namespace = {}; }; // Returns access to the names defined in the module. ModuleRecord.prototype.getNamespace = function() { return this.namespace; }; ModuleRecord.prototype.finalizeModuleInvokation = function() { var i, len = this.prefix.names.length; for (i=0; i < len; i++) { this.namespace[this.prefix.names[i]] = this.prefix[i]; } }; // External invokation of a module. ModuleRecord.prototype.invoke = function(MACHINE, succ, fail) { MACHINE = MACHINE || plt.runtime.currentMachine; succ = succ || function(){}; fail = fail || function(){}; var oldErrorHandler = MACHINE.params['currentErrorHandler']; var afterGoodInvoke = function(MACHINE) { MACHINE.params['currentErrorHandler'] = oldErrorHandler; setTimeout(succ, 0); }; if (this.isInvoked) { setTimeout(succ, 0); } else { MACHINE.params['currentErrorHandler'] = function(MACHINE, anError) { MACHINE.params['currentErrorHandler'] = oldErrorHandler; setTimeout( function() { fail(MACHINE, anError) }, 0); }; MACHINE.control.push(new CallFrame(afterGoodInvoke, null)); trampoline(MACHINE, this.label); } }; // A generic frame just holds marks. var Frame = function() { // The set of continuation marks. this.marks = []; // When we're in the middle of computing with-cont-mark, we // stash the key in here temporarily. this.pendingContinuationMarkKey = undefined; this.pendingApplyValuesProc = undefined; this.pendingBegin0Count = undefined; this.pendingBegin0Values = undefined; }; // Frames must support marks and the temporary variables necessary to // support with-continuation-mark and with-values. // Specialized frames support more features: // A CallFrame represents a call stack frame, and includes the return address // as well as the function being called. var CallFrame = function(label, proc) { this.label = label; this.proc = proc; // When we're in the middle of computing with-cont-mark, we // stash the key in here temporarily. this.pendingContinuationMarkKey = undefined; // The set of continuation marks. this.marks = []; }; CallFrame.prototype = heir(Frame.prototype); // A prompt frame includes a return address, as well as a prompt tag // for supporting delimited continuations. var PromptFrame = function(label, tag) { this.label = label; this.tag = tag; // ContinuationPromptTag // The set of continuation marks. this.marks = []; // When we're in the middle of computing with-cont-mark, we // stash the key in here temporarily. this.pendingContinuationMarkKey = undefined; }; PromptFrame.prototype = heir(Frame.prototype); // Output Ports var OutputPort = function() {}; var isOutputPort = makeClassPredicate(OutputPort); var StandardOutputPort = function() { OutputPort.call(this); }; StandardOutputPort.prototype = heir(OutputPort.prototype); StandardOutputPort.prototype.writeDomNode = function(MACHINE, domNode) { MACHINE.params['currentDisplayer'](MACHINE, domNode); }; var StandardErrorPort = function() { OutputPort.call(this); }; StandardErrorPort.prototype = heir(OutputPort.prototype); StandardErrorPort.prototype.writeDomNode = function(MACHINE, domNode) { MACHINE.params['currentErrorDisplayer'](MACHINE, domNode); }; var OutputStringPort = function() { this.buf = []; }; OutputStringPort.prototype = heir(OutputPort.prototype); OutputStringPort.prototype.writeDomNode = function(MACHINE, v) { this.buf.push($(v).text()); }; OutputStringPort.prototype.getOutputString = function() { return this.buf.join(''); }; var isOutputStringPort = makeClassPredicate(OutputStringPort); // Function types: a function is either a Primitive or a Closure. // A Primitive is a function that's expected to return. It is not // allowed to call into Closures. Its caller is expected to pop off // its argument stack space. // // // A Closure is a function that takes on more responsibilities: it is // responsible for popping off stack space before it finishes, and it // is also explicitly responsible for continuing the computation by // popping off the control stack and doing the jump. Because of this, // closures can do pretty much anything to the machine. // A closure consists of its free variables as well as a label // into its text segment. var Closure = function(label, arity, closedVals, displayName) { this.label = label; // (MACHINE -> void) this.arity = arity; // number this.closedVals = closedVals; // arrayof number this.displayName = displayName; // string }; var VariableReference = function(prefix, pos) { this.prefix = prefix; this.pos = pos; }; // A continuation prompt tag labels a prompt frame. var ContinuationPromptTag = function(name) { this.name = name; }; // There is a single, distinguished default continuation prompt tag // that's used to wrap around toplevel prompts. var DEFAULT_CONTINUATION_PROMPT_TAG = new ContinuationPromptTag("default-continuation-prompt-tag"); var raise = function(MACHINE, e) { if (typeof(window['console']) !== 'undefined' && typeof(console['log']) === 'function') { console.log(MACHINE); if (e['stack']) { console.log(e['stack']); } else { console.log(e); } } throw e; }; // testArgument: (X -> boolean) X number string string -> boolean // Produces true if val is true, and otherwise raises an error. var testArgument = function(MACHINE, expectedTypeName, predicate, val, index, callerName) { if (predicate(val)) { return true; } else { raiseArgumentTypeError(MACHINE, callerName, expectedTypeName, index, val); } }; var testArity = function(callerName, observed, minimum, maximum) { if (observed < minimum || observed > maximum) { raise(MACHINE, new Error(callerName + ": expected at least " + minimum + " arguments " + " but received " + observed)); } }; var raiseUnboundToplevelError = function(MACHINE, name) { raise(MACHINE, new Error("Not bound: " + name)); }; var raiseArgumentTypeError = function(MACHINE, callerName, expectedTypeName, argumentOffset, actualValue) { raise(MACHINE, new Error(callerName + ": expected " + expectedTypeName + " as argument " + (argumentOffset + 1) + " but received " + helpers.toWrittenString(actualValue))); }; var raiseContextExpectedValuesError = function(MACHINE, expected) { raise(MACHINE, new Error("expected " + expected + " values, received " + MACHINE.argcount + " values")); }; var raiseArityMismatchError = function(MACHINE, proc, expected, received) { raise(MACHINE, new Error(proc.displayName + ": " + "expected " + expected + " value(s), received " + received + " value(s)")); }; var raiseOperatorApplicationError = function(MACHINE, operator) { raise(MACHINE, new Error("not a procedure: " + helpers.toWrittenString(operator))); }; var raiseOperatorIsNotClosure = function(MACHINE, operator) { raise(MACHINE, new Error("not a closure: " + helpers.toWrittenString(operator))); }; var raiseOperatorIsNotPrimitiveProcedure = function(MACHINE, operator) { raise(MACHINE, new Error("not a primitive procedure: " + helpers.toWrittenString(operator))); }; var raiseUnimplementedPrimitiveError = function(MACHINE, name) { raise(MACHINE, new Error("unimplemented kernel procedure: " + name)) }; // captureControl implements the continuation-capturing part of // call/cc. It grabs the control frames up to (but not including) the // prompt tagged by the given tag. var captureControl = function(MACHINE, skip, tag) { var i; for (i = MACHINE.control.length - 1 - skip; i >= 0; i--) { if (MACHINE.control[i].tag === tag) { return MACHINE.control.slice(i + 1, MACHINE.control.length - skip); } } raise(MACHINE, new Error("captureControl: unable to find tag " + tag)); }; // restoreControl clears the control stack (up to, but not including the // prompt tagged by tag), and then appends the rest of the control frames. // At the moment, the rest of the control frames is assumed to be in the // top of the environment. var restoreControl = function(MACHINE, tag) { var i; for (i = MACHINE.control.length - 1; i >= 0; i--) { if (MACHINE.control[i].tag === tag) { MACHINE.control = MACHINE.control.slice(0, i+1).concat( MACHINE.env[MACHINE.env.length - 1]); return; } } raise(MACHINE, new Error("restoreControl: unable to find tag " + tag)); }; // Splices the list argument in the environment. Adjusts MACHINE.argcount // appropriately. var spliceListIntoStack = function(MACHINE, depth) { var lst = MACHINE.env[MACHINE.env.length - 1 - depth]; var vals = []; while(lst !== NULL) { vals.push(lst.first); lst = lst.rest; } vals.reverse(); MACHINE.env.splice.apply(MACHINE.env, [MACHINE.env.length - 1 - depth, 1].concat(vals)); MACHINE.argcount = MACHINE.argcount + vals.length - 1; }; var unspliceRestFromStack = function(MACHINE, depth, length) { var lst = NULL; var i; for (i = 0; i < length; i++) { lst = makePair(MACHINE.env[MACHINE.env.length - depth - length + i], lst); } MACHINE.env.splice(MACHINE.env.length - depth - length, length, lst); MACHINE.argcount = MACHINE.argcount - length + 1; }; // Helper to deal with the argument-passing of primitives. Call f // with arguments bound from MACHINE.env, assuming // MACHINE.argcount has been initialized with the number of // arguments on the stack. vs provides optional values for the // arguments that go beyond those of the mandatoryArgCount. var withArguments = function(MACHINE, mandatoryArgCount, vs, f) { var args = []; for (var i = 0; i < MACHINE.argcount; i++) { if (i < mandatoryArgCount) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } else { if (i < MACHINE.argcount) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } else { args.push(vs[mandatoryArgCount - i]); } } } return f.apply(null, args); }; // Primitives are the set of primitive values. Not all primitives // are coded here; several of them (including call/cc) are injected by // the bootstrapping code in compiler/boostrapped-primitives.rkt var Primitives = {}; var installPrimitiveProcedure = function(name, arity, f) { Primitives[name] = f; Primitives[name].arity = arity; Primitives[name].displayName = name; }; var installPrimitiveClosure = function(name, arity, f) { Primitives[name] = new Closure(f, arity, [], name); }; var makePrimitiveProcedure = function(name, arity, f) { f.arity = arity; f.displayName = name; return f; }; var installPrimitiveConstant = function(name, v) { Primitives[name] = v; }; installPrimitiveConstant('pi', jsnums.pi); installPrimitiveConstant('e', jsnums.e); installPrimitiveConstant('null', NULL); installPrimitiveProcedure( 'display', makeList(1, 2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 2) { testArgument(MACHINE, 'output-port', isOutputPort, MACHINE.env.length-2, 1, 'display'); outputPort = MACHINE.env[MACHINE.env.length-2]; } outputPort.writeDomNode(MACHINE, toDomNode(firstArg, 'display')); return VOID; }); installPrimitiveProcedure( 'newline', makeList(0, 1), function(MACHINE) { var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 1) { testArgument(MACHINE, 'output-port', isOutputPort, MACHINE.env.length-1, 1, 'newline'); outputPort = MACHINE.env[MACHINE.env.length-1]; } outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); return VOID; }); installPrimitiveProcedure( 'displayln', makeList(1, 2), function(MACHINE){ var firstArg = MACHINE.env[MACHINE.env.length-1]; var outputPort = MACHINE.params.currentOutputPort; if (MACHINE.argcount === 2) { testArgument(MACHINE, 'output-port', isOutputPort, MACHINE.env.length-2, 1, 'displayln'); outputPort = MACHINE.env[MACHINE.env.length-2]; } outputPort.writeDomNode(MACHINE, toDomNode(firstArg, 'display')); outputPort.writeDomNode(MACHINE, toDomNode("\n", 'display')); return VOID; }); installPrimitiveProcedure( 'format', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { var args = [], i, formatString; testArgument(MACHINE, 'string', isString, MACHINE.env[MACHINE.env.length-1], 0, 'format'); for(i = 0; i < MACHINE.argcount; i++) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } formatString = args.shift(); return helpers.format(formatString, args, 'format'); }); installPrimitiveProcedure( 'printf', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { var args = [], i, formatString; testArgument(MACHINE, 'string', isString, MACHINE.env[MACHINE.env.length-1], 0, 'printf'); for(i = 0; i < MACHINE.argcount; i++) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } formatString = args.shift(); var result = helpers.format(formatString, args, 'format'); var outputPort = MACHINE.params.currentOutputPort; outputPort.writeDomNode(MACHINE, toDomNode(result, 'display')); return VOID; }); installPrimitiveProcedure( 'fprintf', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var args = [], i, formatString; testArgument(MACHINE, 'output-port', isOutputPort, MACHINE.env[MACHINE.env.length-1], 0, 'fprintf'); testArgument(MACHINE, 'string', isString, MACHINE.env[MACHINE.env.length-2], 1, 'fprintf'); for(i = 1; i < MACHINE.argcount; i++) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } formatString = args.shift(); var result = helpers.format(formatString, args, 'format'); var outputPort = MACHINE.env[MACHINE.env.length-1]; outputPort.writeDomNode(MACHINE, toDomNode(result, 'display')); return VOID; }); installPrimitiveProcedure( 'current-print', makeList(0, 1), function(MACHINE) { if (MACHINE.argcount === 1) { MACHINE.params['currentPrint'] = MACHINE.env[MACHINE.env.length - 1]; return VOID; } else { return MACHINE.params['currentPrint']; } }); installPrimitiveProcedure( 'current-output-port', makeList(0, 1), function(MACHINE) { if (MACHINE.argcount === 1) { MACHINE.params['currentOutputPort'] = MACHINE.env[MACHINE.env.length - 1]; return VOID; } else { return MACHINE.params['currentOutputPort']; } }); installPrimitiveProcedure( '=', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; testArgument(MACHINE, 'number', isNumber, firstArg, 0, '='); for (var i = 0; i < MACHINE.argcount - 1; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '='); if (! (jsnums.equals(MACHINE.env[MACHINE.env.length - 1 - i], MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { return false; } } return true; }); installPrimitiveProcedure( '=~', 3, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, '=~'); testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 2], 1, '=~'); testArgument(MACHINE, 'nonnegative real', isNonNegativeReal, MACHINE.env[MACHINE.env.length - 3], 2, '=~'); var x = MACHINE.env[MACHINE.env.length-1]; var y = MACHINE.env[MACHINE.env.length-2]; var range = MACHINE.env[MACHINE.env.length-3]; return jsnums.lessThanOrEqual(jsnums.abs(jsnums.subtract(x, y)), range); }); installPrimitiveProcedure( '<', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; testArgument(MACHINE, 'number', isNumber, firstArg, 0, '<'); for (var i = 0; i < MACHINE.argcount - 1; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '<'); if (! (jsnums.lessThan(MACHINE.env[MACHINE.env.length - 1 - i], MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { return false; } } return true; }); installPrimitiveProcedure( '>', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; testArgument(MACHINE, 'number', isNumber, firstArg, 0, '>'); for (var i = 0; i < MACHINE.argcount - 1; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '>'); if (! (jsnums.greaterThan(MACHINE.env[MACHINE.env.length - 1 - i], MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { return false; } } return true; }); installPrimitiveProcedure( '<=', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; testArgument(MACHINE, 'number', isNumber, firstArg, 0, '<='); for (var i = 0; i < MACHINE.argcount - 1; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '<='); if (! (jsnums.lessThanOrEqual(MACHINE.env[MACHINE.env.length - 1 - i], MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { return false; } } return true; }); installPrimitiveProcedure( '>=', plt.baselib.arity.arityAtLeast(2), function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; testArgument(MACHINE, 'number', isNumber, firstArg, 0, '>='); for (var i = 0; i < MACHINE.argcount - 1; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '>='); if (! (jsnums.greaterThanOrEqual(MACHINE.env[MACHINE.env.length - 1 - i], MACHINE.env[MACHINE.env.length - 1 - i - 1]))) { return false; } } return true; }); installPrimitiveProcedure( '+', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { var result = 0; var i = 0; for (i=0; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '+'); result = jsnums.add(result, MACHINE.env[MACHINE.env.length - 1 - i]); }; return result; }); installPrimitiveProcedure( '*', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { var result = 1; var i = 0; for (i=0; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1 - i], i, '*'); result = jsnums.multiply(result, MACHINE.env[MACHINE.env.length - 1 - i]); } return result; }); installPrimitiveProcedure( '-', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { if (MACHINE.argcount === 1) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, '-'); return jsnums.subtract(0, MACHINE.env[MACHINE.env.length-1]); } var result = MACHINE.env[MACHINE.env.length - 1]; for (var i = 1; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1-i], i, '-'); result = jsnums.subtract(result, MACHINE.env[MACHINE.env.length - 1 - i]); } return result; }); installPrimitiveProcedure( '/', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, '/'); var result = MACHINE.env[MACHINE.env.length - 1]; for (var i = 1; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1-i], i, '/'); result = jsnums.divide(result, MACHINE.env[MACHINE.env.length - 1 - i]); } return result; }); installPrimitiveProcedure( 'add1', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'add1'); var firstArg = MACHINE.env[MACHINE.env.length-1]; return jsnums.add(firstArg, 1); }); installPrimitiveProcedure( 'sub1', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'sub1'); var firstArg = MACHINE.env[MACHINE.env.length-1]; return jsnums.subtract(firstArg, 1); }); installPrimitiveProcedure( 'zero?', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return jsnums.equals(firstArg, 0); }); installPrimitiveProcedure( 'cons', 2, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; return makePair(firstArg, secondArg); }); installPrimitiveProcedure( 'list', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { var result = NULL; for (var i = 0; i < MACHINE.argcount; i++) { result = makePair(MACHINE.env[MACHINE.env.length - (MACHINE.argcount - i)], result); } return result; }); installPrimitiveProcedure( 'car', 1, function(MACHINE) { testArgument(MACHINE, 'pair', isPair, MACHINE.env[MACHINE.env.length - 1], 0, 'car'); var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg.first; }); installPrimitiveProcedure( 'cdr', 1, function(MACHINE) { testArgument(MACHINE, 'pair', isPair, MACHINE.env[MACHINE.env.length - 1], 0, 'cdr'); var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg.rest; }); installPrimitiveProcedure( 'pair?', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return isPair(firstArg); }); installPrimitiveProcedure( 'set-car!', 2, function(MACHINE) { testArgument(MACHINE, 'pair', isPair, MACHINE.env[MACHINE.env.length - 1], 0, 'set-car!'); var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; firstArg.first = secondArg; return VOID; }); installPrimitiveProcedure( 'set-cdr!', 2, function(MACHINE) { testArgument(MACHINE, 'pair', isPair, MACHINE.env[MACHINE.env.length - 1], 0, 'set-cdr!'); var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; firstArg.rest = secondArg; return VOID; }); installPrimitiveProcedure( 'not', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return (firstArg === false); }); installPrimitiveProcedure( 'null?', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg === NULL; }); installPrimitiveProcedure( 'vector', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { var i; var result = []; for (i = 0; i < MACHINE.argcount; i++) { result.push(MACHINE.env[MACHINE.env.length-1-i]); } var newVector = makeVector.apply(null, result); return newVector; }); installPrimitiveProcedure( 'vector->list', 1, function(MACHINE) { testArgument(MACHINE, 'vector', isVector, MACHINE.env[MACHINE.env.length - 1], 0, 'vector->list'); var elts = MACHINE.env[MACHINE.env.length-1].elts; var i; var result = NULL; for (i = 0; i < elts.length; i++) { result = makePair(elts[elts.length - 1 - i], result); } return result; }); installPrimitiveProcedure( 'list->vector', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var result = []; while (firstArg !== NULL) { result.push(firstArg.first); firstArg = firstArg.rest; } return makeVector.apply(null, result); }); installPrimitiveProcedure( 'vector-ref', 2, function(MACHINE) { testArgument(MACHINE, 'vector', isVector, MACHINE.env[MACHINE.env.length - 1], 0, 'vector-ref'); var elts = MACHINE.env[MACHINE.env.length-1].elts; var index = MACHINE.env[MACHINE.env.length-2]; return elts[index]; }); installPrimitiveProcedure( 'vector-set!', 3, function(MACHINE) { testArgument(MACHINE, 'vector', isVector, MACHINE.env[MACHINE.env.length - 1], 0, 'vector-set!'); testArgument(MACHINE, 'natural', isNatural, MACHINE.env[MACHINE.env.length - 2], 1, 'vector-set!'); var elts = MACHINE.env[MACHINE.env.length-1].elts; var index = jsnums.toFixnum(MACHINE.env[MACHINE.env.length-2]); var val = MACHINE.env[MACHINE.env.length-3]; elts[index] = val; return VOID; }); installPrimitiveProcedure( 'vector-length', 1, function(MACHINE) { testArgument(MACHINE, 'vector', isVector, MACHINE.env[MACHINE.env.length - 1], 0, 'vector-length'); var firstArg = MACHINE.env[MACHINE.env.length-1].elts; return firstArg.length; }); installPrimitiveProcedure( 'make-vector', makeList(1, 2), function(MACHINE) { var value = 0; testArgument(MACHINE, 'natural', isNatural, MACHINE.env[MACHINE.env.length - 1], 0, 'make-vector'); if (MACHINE.argcount == 2) { value = MACHINE.env[MACHINE.env.length - 2]; } var length = jsnums.toFixnum(MACHINE.env[MACHINE.env.length-1]); var arr = []; for(var i = 0; i < length; i++) { arr[i] = value; } return makeVector.apply(null, arr); }); installPrimitiveProcedure( 'symbol?', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return typeof(firstArg) === 'string'; }); installPrimitiveProcedure( 'symbol->string', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg; }); installPrimitiveProcedure( 'string-append', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { var buffer = []; var i; for (i = 0; i < MACHINE.argcount; i++) { buffer.push(MACHINE.env[MACHINE.env.length - 1 - i]); } return buffer.join(''); }); installPrimitiveProcedure( 'string-length', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg.length; }); installPrimitiveProcedure( 'box', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var result = [firstArg]; return result; }); installPrimitiveProcedure( 'unbox', 1, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; return firstArg[0]; }); installPrimitiveProcedure( 'set-box!', 2, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; firstArg[0] = secondArg; return VOID; }); installPrimitiveProcedure( 'void', plt.baselib.arity.arityAtLeast(0), function(MACHINE) { return VOID; }); installPrimitiveProcedure( 'eq?', 2, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; return firstArg === secondArg; }); installPrimitiveProcedure( 'equal?', 2, function(MACHINE) { var firstArg = MACHINE.env[MACHINE.env.length-1]; var secondArg = MACHINE.env[MACHINE.env.length-2]; return equals(firstArg, secondArg); }); installPrimitiveProcedure( 'member', 2, function(MACHINE) { var x = MACHINE.env[MACHINE.env.length-1]; var lst = MACHINE.env[MACHINE.env.length-2]; var originalLst = lst; while (true) { if (! isList(lst)) { raise(MACHINE, new Error("member: expected list" + " as argument #2" + " but received " + originalLst + " instead")); } if (lst === NULL) { return false; } if (equals(x, (lst.first))) { return lst; } lst = lst.rest; } }); installPrimitiveProcedure( 'reverse', 1, function(MACHINE) { var rev = NULL; var lst = MACHINE.env[MACHINE.env.length-1]; while(lst !== NULL) { testArgument(MACHINE, 'pair', isPair, lst, 0, 'reverse'); rev = makePair(lst.first, rev); lst = lst.rest; } return rev; }); installPrimitiveProcedure( 'abs', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'abs'); return jsnums.abs(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'acos', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'acos'); return jsnums.acos(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'asin', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'asin'); return jsnums.asin(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'sin', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'sin'); return jsnums.sin(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'sinh', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'sinh'); return jsnums.sinh(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'tan', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'tan'); return jsnums.tan(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'atan', makeList(1, 2), function(MACHINE) { if (MACHINE.argcount === 1) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'atan'); return jsnums.atan(MACHINE.env[MACHINE.env.length - 1]); } else { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'atan'); testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 2], 1, 'atan'); return jsnums.makeFloat( Math.atan2(jsnums.toFixnum(MACHINE.env[MACHINE.env.length - 1]), jsnums.toFixnum(MACHINE.env[MACHINE.env.length - 2]))); } }); installPrimitiveProcedure( 'angle', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'angle'); return jsnums.angle(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'magnitude', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'magnitude'); return jsnums.magnitude(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'conjugate', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'conjugate'); return jsnums.conjugate(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'cos', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'cos'); return jsnums.cos(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'cosh', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'cosh'); return jsnums.cosh(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'gcd', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { var args = [], i, x; for (i = 0; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 1 - i], i, 'gcd'); args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } x = args.shift(); return jsnums.gcd(x, args); }); installPrimitiveProcedure( 'lcm', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { var args = [], i, x; for (i = 0; i < MACHINE.argcount; i++) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 1 - i], i, 'lcm'); args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } x = args.shift(); return jsnums.lcm(x, args); }); installPrimitiveProcedure( 'exp', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'exp'); return jsnums.exp(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'expt', 2, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'expt'); testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 2], 1, 'expt'); return jsnums.expt(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'exact?', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'exact?'); return jsnums.isExact(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'imag-part', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'imag-part'); return jsnums.imaginaryPart(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'real-part', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length - 1], 0, 'real-part'); return jsnums.realPart(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'make-polar', 2, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'make-polar'); testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 2], 1, 'make-polar'); return jsnums.makeComplexPolar(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'make-rectangular', 2, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'make-rectangular'); testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 2], 1, 'make-rectangular'); return jsnums.makeComplex(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'modulo', 2, function(MACHINE) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 1], 0, 'modulo'); testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 2], 1, 'modulo'); return jsnums.modulo(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'remainder', 2, function(MACHINE) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 1], 0, 'remainder'); testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 2], 1, 'remainder'); return jsnums.remainder(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'quotient', 2, function(MACHINE) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 1], 0, 'quotient'); testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length - 2], 1, 'quotient'); return jsnums.quotient(MACHINE.env[MACHINE.env.length - 1], MACHINE.env[MACHINE.env.length - 2]); }); installPrimitiveProcedure( 'floor', 1, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'floor'); return jsnums.floor(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'ceiling', 1, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'ceiling'); return jsnums.ceiling(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'round', 1, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'round'); return jsnums.round(MACHINE.env[MACHINE.env.length - 1]); }); installPrimitiveProcedure( 'truncate', 1, function(MACHINE) { testArgument(MACHINE, 'real', jsnums.isReal, MACHINE.env[MACHINE.env.length - 1], 0, 'truncate'); if (jsnums.lessThan(MACHINE.env[MACHINE.env.length - 1], 0)) { return jsnums.ceiling(MACHINE.env[MACHINE.env.length - 1]); } else { return jsnums.floor(MACHINE.env[MACHINE.env.length - 1]); } }); installPrimitiveProcedure( 'numerator', 1, function(MACHINE) { testArgument(MACHINE, 'rational', jsnums.isRational, MACHINE.env[MACHINE.env.length-1], 0, 'numerator'); return jsnums.numerator(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'denominator', 1, function(MACHINE) { testArgument(MACHINE, 'rational', jsnums.isRational, MACHINE.env[MACHINE.env.length-1], 0, 'denominator'); return jsnums.denominator(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'log', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'log'); return jsnums.log(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'sqr', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'sqr'); return jsnums.sqr(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'sqrt', 1, function(MACHINE) { testArgument(MACHINE, 'number', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'sqrt'); return jsnums.sqrt(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'integer-sqrt', 1, function(MACHINE) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length-1], 0, 'integer-sqrt'); return jsnums.integerSqrt(MACHINE.env[MACHINE.env.length-1]); }); // rawSgn: number -> number var rawSgn = function(x) { if (jsnums.isInexact(x)) { if ( jsnums.greaterThan(x, 0) ) { return jsnums.makeFloat(1); } else if ( jsnums.lessThan(x, 0) ) { return jsnums.makeFloat(-1); } else { return jsnums.makeFloat(0); } } else { if ( jsnums.greaterThan(x, 0) ) { return 1; } else if ( jsnums.lessThan(x, 0) ) { return -1; } else { return 0; } } }; installPrimitiveProcedure( 'sgn', 1, function(MACHINE) { testArgument(MACHINE, 'integer', jsnums.isInteger, MACHINE.env[MACHINE.env.length-1], 0, 'sgn'); return rawSgn(MACHINE.env[MACHINE.env.length-1]); }); installPrimitiveProcedure( 'number->string', 1, function(MACHINE) { testArgument(MACHINE, 'integer', isNumber, MACHINE.env[MACHINE.env.length-1], 0, 'number->string'); return MACHINE.env[MACHINE.env.length-1].toString(); }); installPrimitiveProcedure( 'string->number', 1, function(MACHINE) { testArgument(MACHINE, 'string', isString, MACHINE.env[MACHINE.env.length-1], 0, 'string->number'); return jsnums.fromString(MACHINE.env[MACHINE.env.length-1].toString()); }); installPrimitiveProcedure( 'format', plt.baselib.arity.arityAtLeast(1), function(MACHINE) { var args = [], i, formatString; testArgument(MACHINE, 'string', isString, MACHINE.env[MACHINE.env.length-1], 0, 'format'); for(i = 0; i < MACHINE.argcount; i++) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } formatString = args.shift(); return helpers.format(formatString, args, 'format'); }); installPrimitiveClosure( 'make-struct-type', makeList(4, 5, 6, 7, 8, 9, 10, 11), function(MACHINE) { withArguments( MACHINE, 4, [false, NULL, false, false, NULL, false, false], function(name, superType, initFieldCount, autoFieldCount, autoV, props, // FIXME: currently ignored inspector, // FIXME: currently ignored procSpec, // FIXME: currently ignored immutables, // FIXME: currently ignored guard, // FIXME: currently ignored constructorName ) { // FIXME: typechecks. var structType = plt.baselib.structs.makeStructureType( name, superType, initFieldCount, autoFieldCount, autoV, //props, //inspector, //procSpec, //immutables, guard); var constructorValue = makePrimitiveProcedure( constructorName, jsnums.toFixnum(initFieldCount), function(MACHINE) { var args = []; for(var i = 0; i < initFieldCount; i++) { args.push(MACHINE.env[MACHINE.env.length - 1 - i]); } return structType.constructor.apply(null, args); }); var predicateValue = makePrimitiveProcedure( String(name) + "?", 1, function(MACHINE) { return structType.predicate(MACHINE.env[MACHINE.env.length - 1]); }); var accessorValue = makePrimitiveProcedure( String(name) + "-accessor", 2, function(MACHINE) { // FIXME: typechecks return structType.accessor( MACHINE.env[MACHINE.env.length - 1], jsnums.toFixnum(MACHINE.env[MACHINE.env.length - 2])); }); accessorValue.structType = structType; var mutatorValue = makePrimitiveProcedure( String(name) + "-mutator", 3, function(MACHINE) { // FIXME: typechecks return structType.mutator( MACHINE.env[MACHINE.env.length - 1], jsnums.toFixnum(MACHINE.env[MACHINE.env.length - 2]), MACHINE.env[MACHINE.env.length - 3]); }); mutatorValue.structType = structType; finalizeClosureCall(MACHINE, structType, constructorValue, predicateValue, accessorValue, mutatorValue); }); }); installPrimitiveProcedure( 'current-inspector', makeList(0, 1), function(MACHINE) { if (MACHINE.argcount === 1) { MACHINE.params['currentInspector'] = MACHINE.env[MACHINE.env.length - 1]; return VOID; } else { return MACHINE.params['currentInspector']; } } ); installPrimitiveProcedure( 'make-struct-field-accessor', makeList(2, 3), function(MACHINE){ // FIXME: typechecks // We must guarantee that the ref argument is good. var structType = MACHINE.env[MACHINE.env.length - 1].structType; var index = MACHINE.env[MACHINE.env.length - 2]; var name; if (MACHINE.argcount === 3) { name = String(MACHINE.env[MACHINE.env.length - 3]); } else { name = 'field' + index; } return makePrimitiveProcedure( name, 1, function(MACHINE) { return structType.accessor( MACHINE.env[MACHINE.env.length - 1], jsnums.toFixnum(index)); }); }); installPrimitiveProcedure( 'make-struct-field-mutator', makeList(2, 3), function(MACHINE){ // FIXME: typechecks // We must guarantee that the set! argument is good. var structType = MACHINE.env[MACHINE.env.length - 1].structType; var index = MACHINE.env[MACHINE.env.length - 2]; var name; if (MACHINE.argcount === 3) { name = String(MACHINE.env[MACHINE.env.length - 3]); } else { name = 'field' + index; } return makePrimitiveProcedure( name, 2, function(MACHINE) { return structType.mutator( MACHINE.env[MACHINE.env.length - 1], jsnums.toFixnum(index), MACHINE.env[MACHINE.env.length - 2]); }); }); // installPrimitiveProcedure( // 'make-struct-field-accessor', // makeList(2, 3), // function(MACHINE) { // var accessor, fieldPos, fieldName; // accessor = MACHINE.env[MACHINE.env.length-1]; // fieldPos = MACHINE.env[MACHINE.env.length-2]; // if (MACHINE.argcount === 2) { // fieldName = 'field' + fieldPos; // } else { // fieldName = MACHINE.env[MACHINE.env.length-3]; // } // testArgument(MACHINE, // 'accessor procedure that requires a field index', // function(x) { // return (x instanceof types.StructAccessorProc && // x.numParams > 1); // }, // accessor, // 0, // 'make-struct-field-accessor'); // testArgument(MACHINE, // 'exact non-negative integer', // isNatural, // fieldPos, // 'make-struct-field-accessor', // 1) // testArgument(MACHINE, // 'symbol or #f', // function(x) { // return x === false || isSymbol(x); // }, // 'make-struct-field-accessor', // fieldName, // 2); // var procName = accessor.type.name + '-' fieldName; // return new types.StructAccessorProc( // accessor.type, // procName, // 1, // false, // false, // function(MACHINE) { // testArgument(MACHINE, // 'struct:' + accessor.type.name, // accessor.type.predicate, // MACHINE.env[MACHINE.env.length - 1], // procName, // 0); // return accessor.impl(x, fieldPos); // }); // }); // Javascript-specific extensions. A small experiment. installPrimitiveProcedure( 'viewport-width', 0, function(MACHINE) { return $(window).width(); }); installPrimitiveProcedure( 'viewport-height', 0, function(MACHINE) { return $(window).height(); }); installPrimitiveProcedure( 'in-javascript-context?', 0, function(MACHINE) { return true; }); // recomputeGas: state number -> number var recomputeMaxNumBouncesBeforeYield = function(MACHINE, observedDelay) { // We'd like to see a delay of DESIRED_DELAY_BETWEEN_BOUNCES so // that we get MACHINE.params.desiredYieldsPerSecond bounces per // second. var DESIRED_DELAY_BETWEEN_BOUNCES = (1000 / MACHINE.params.desiredYieldsPerSecond); var ALPHA = 256; var delta = (ALPHA * ((DESIRED_DELAY_BETWEEN_BOUNCES - observedDelay) / DESIRED_DELAY_BETWEEN_BOUNCES)); MACHINE.params.maxNumBouncesBeforeYield = Math.max(MACHINE.params.maxNumBouncesBeforeYield + delta, 1); }; var HaltError = function(onHalt) { // onHalt: MACHINE -> void this.onHalt = onHalt || function(MACHINE) {}; }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // The toplevel trampoline. // // // trampoline: MACHINE (MACHINE -> void) -> void // // All evaluation in Racketland happens in the context of this // trampoline. // var trampoline = function(MACHINE, initialJump) { var thunk = initialJump; var startTime = (new Date()).valueOf(); MACHINE.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE; MACHINE.params.numBouncesBeforeYield = MACHINE.params.maxNumBouncesBeforeYield; MACHINE.running = true; while(true) { try { thunk(MACHINE); break; } catch (e) { // There are a few kinds of things that can get thrown // during racket evaluation: // // functions: this gets thrown if the Racket code realizes // that the number of bounces has grown too large. The thrown // function represents a restarter function. // // HaltError: causes evaluation to immediately halt. We schedule // the onHalt function of the HaltError to call afterwards. // // everything else: otherwise, we send the exception value // to the current error handler and exit. if (typeof(e) === 'function') { thunk = e; MACHINE.callsBeforeTrampoline = STACK_LIMIT_ESTIMATE; if (MACHINE.params.numBouncesBeforeYield-- < 0) { recomputeMaxNumBouncesBeforeYield( MACHINE, (new Date()).valueOf() - startTime); setTimeout( function() { trampoline(MACHINE, thunk); }, 0); return; } else { continue; } } else if (e instanceof HaltError) { MACHINE.running = false; setTimeout( function() { e.onHalt(MACHINE); }, 0); return; } else { MACHINE.running = false; setTimeout( function() { MACHINE.params.currentErrorHandler(MACHINE, e); }, 0); return; } } } MACHINE.running = false; setTimeout( function() { MACHINE.params.currentSuccessHandler(MACHINE); }, 0); return; }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Approximately find the stack limit. // This function assumes, on average, five variables or // temporaries per stack frame. // This will never report a number greater than MAXIMUM_CAP. var findStackLimit = function(after) { var MAXIMUM_CAP = 100000; var n = 1; var limitDiscovered = false; setTimeout( function() { if(! limitDiscovered) { limitDiscovered = true; after(n); } }, 0); var loop1 = function(x, y, z, w, k) { // Ensure termination, just in case JavaScript ever // does eliminate stack limits. if (n >= MAXIMUM_CAP) { return; } n++; return 1 + loop2(y, z, w, k, x); }; var loop2 = function(x, y, z, w, k) { n++; return 1 + loop1(y, z, w, k, x); }; try { var dontCare = 1 + loop1(2, "seven", [1], {number: 8}, 2); } catch (e) { // ignore exceptions. } if (! limitDiscovered) { limitDiscovered = true; after(n); } }; // Schedule a stack limit estimation. If it fails, no harm, no // foul (hopefully!) setTimeout(function() { findStackLimit(function(v) { // Trying to be a little conservative. STACK_LIMIT_ESTIMATE = Math.floor(v / 10); }); }, 0); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// (function(scope) { scope.ready = function(f) { if (runtimeIsReady) { notifyWaiter(f); } else { readyWaiters.push(f); } }; scope.setReadyTrue = function() { var i; runtimeIsReady = true; for (i = 0; i < readyWaiters.length; i++) { notifyWaiter(readyWaiters[i]); } readyWaiters = []; }; var runtimeIsReady = false; var readyWaiters = []; var notifyWaiter = function(w) { setTimeout(w, 0); }; })(this); ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Executes all programs that have been labeled as a main module var invokeMains = function(machine, succ, fail) { runtime.ready(function() { machine = machine || runtime.currentMachine; succ = succ || function() {}; fail = fail || function() {}; var mainModules = machine.mainModules.slice(); var loop = function() { if (mainModules.length > 0) { var nextModule = mainModules.shift(); nextModule.invoke(machine, loop, fail); } else { succ(); } }; setTimeout(loop, 0); }); }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // Exports var exports = runtime; exports['currentMachine'] = new Machine(); exports['invokeMains'] = invokeMains; // installing new primitives exports['installPrimitiveProcedure'] = installPrimitiveProcedure; exports['installPrimitiveClosure'] = installPrimitiveClosure; exports['makePrimitiveProcedure'] = makePrimitiveProcedure; exports['Primitives'] = Primitives; exports['ready'] = ready; // Private: the runtime library will set this flag to true when // the library has finished loading. exports['setReadyTrue'] = setReadyTrue; exports['Machine'] = Machine; exports['Frame'] = Frame; exports['CallFrame'] = CallFrame; exports['PromptFrame'] = PromptFrame; exports['Closure'] = Closure; exports['ModuleRecord'] = ModuleRecord; exports['VariableReference'] = VariableReference; exports['ContinuationPromptTag'] = ContinuationPromptTag; exports['DEFAULT_CONTINUATION_PROMPT_TAG'] = DEFAULT_CONTINUATION_PROMPT_TAG; exports['NULL'] = NULL; exports['VOID'] = VOID; exports['testArgument'] = testArgument; exports['testArity'] = testArity; exports['raise'] = raise; exports['raiseUnboundToplevelError'] = raiseUnboundToplevelError; exports['raiseArgumentTypeError'] = raiseArgumentTypeError; exports['raiseContextExpectedValuesError'] = raiseContextExpectedValuesError; exports['raiseArityMismatchError'] = raiseArityMismatchError; exports['raiseOperatorApplicationError'] = raiseOperatorApplicationError; exports['raiseOperatorIsNotPrimitiveProcedure'] = raiseOperatorIsNotPrimitiveProcedure; exports['raiseOperatorIsNotClosure'] = raiseOperatorIsNotClosure; exports['raiseUnimplementedPrimitiveError'] = raiseUnimplementedPrimitiveError; exports['captureControl'] = captureControl; exports['restoreControl'] = restoreControl; exports['trampoline'] = trampoline; exports['spliceListIntoStack'] = spliceListIntoStack; exports['unspliceRestFromStack'] = unspliceRestFromStack; exports['finalizeClosureCall'] = finalizeClosureCall; ////////////////////////////////////////////////////////////////////// // Type constructors // numbers exports['makeList'] = makeList; exports['makePair'] = makePair; exports['makeVector'] = makeVector; exports['makeBox'] = makeBox; // Type predicates exports['isPair'] = isPair; exports['isList'] = isList; exports['isVector'] = isVector; exports['isOutputPort'] = isOutputPort; exports['isOutputStringPort'] = isOutputStringPort; exports['isBox'] = isBox; exports['isString'] = isString; exports['equals'] = equals; exports['toDomNode'] = toDomNode; exports['toWrittenString'] = toWrittenString; exports['toDisplayedString'] = toDisplayedString; exports['ArityAtLeast'] = plt.baselib.arity.ArityAtLeast; exports['arityAtLeast'] = plt.baselib.arity.arityAtLeast; exports['isArityMatching'] = plt.baselib.arity.isArityMatching; exports['heir'] = heir; exports['makeClassPredicate'] = makeClassPredicate; exports['HaltError'] = HaltError; exports['makeStructureType'] = plt.baselib.structs.makeStructureType; exports['Struct'] = plt.baselib.structs.Struct; exports['StructType'] = plt.baselib.structs.StructType; scope.link.announceReady('runtime'); })(this['plt']);