whalesong/js-assembler/runtime-src/runtime.js

2650 lines
76 KiB
JavaScript

// 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']);