whalesong/tests/older-tests/unit-tests/tests.js

3463 lines
104 KiB
JavaScript

//////////////////////////////////////////////////////////////////////
var run = function(state) {
while (!state.isStuck()) {
interpret.step(state);
}
return state.v;
}
var step = interpret.step;
//////////////////////////////////////////////////////////////////////
var EXIT_ON_FIRST_ERROR = true;
//////////////////////////////////////////////////////////////////////
var StateModule = state;
var makeStateWithConstant = function(c) {
var s = new StateModule.State();
s.v = c;
return s;
};
var makePrefix = function(n) {
var arr = [];
for (var i = 0; i < n; i++) {
arr.push(false);
}
return new control.Prefix({numLifts: 0,
toplevels: arr });
};
var makeMod = function(prefix, body) {
return new control.ModControl(prefix, [], body);
};
var makeConstant = function(c) {
return new control.ConstantControl(c);
};
var makeBranch = function(x, y, z) {
return new control.BranchControl(x, y, z);
};
var makeSeq = function() {
return new control.SeqControl(arguments);
};
var makeBeg0 = function() {
return new control.Beg0Control(arguments);
};
var makeToplevel = function(depth, pos) {
return new control.ToplevelControl(depth, pos);
};
var makeDefValues = function(ids, body) {
return new control.DefValuesControl(ids, body);
};
var makeLam = function(arity, closureMap, body) {
var aClosureMap = [];
var aClosureTypes = [];
var aParamTypes = [];
for (var i = 0; i < closureMap.length; i++) {
aClosureMap.push(closureMap[i]);
aClosureTypes.push("val/ref");
}
for (var i = 0; i < arity; i++) {
aParamTypes.push("val");
}
return new control.LamControl({'numParams': arity,
'paramTypes': aParamTypes,
'isRest': false,
'closureMap' : aClosureMap,
'closureTypes' : aClosureTypes,
'body': body});
};
var makeLamWithRest = function(arity, closureMap, body) {
var aClosureMap = [];
var aClosureTypes = [];
var aParamTypes = [];
for (var i = 0; i < closureMap.length; i++) {
aClosureMap.push(closureMap[i]);
aClosureTypes.push("val/ref");
}
for (var i = 0; i < arity; i++) {
aParamTypes.push("val");
}
return new control.LamControl({'numParams': arity,
'paramTypes': aParamTypes,
'isRest': true,
'closureMap' : aClosureMap,
'closureTypes' : aClosureTypes,
'body': body});
};
var makePrimval = function(name) {
return new control.PrimvalControl(name);
};
var makeApplication = function(rator, rands) {
assert.ok(typeof(rands) === 'object' && rands.length !== undefined);
return new control.ApplicationControl(rator, rands);
};
var makeLocalRef = function(n) {
return new control.LocalrefControl(n);
};
var makeApplyValues = function(proc, argsExpr) {
return new control.ApplyValuesControl(proc, argsExpr);
};
var makeLet1 = function(rhs, body) {
return new control.LetOneControl(rhs, body);
};
var makeLetVoid = function(count, isBoxes, body) {
return new control.LetVoidControl({count: count,
isBoxes : isBoxes,
body : body});
};
var makeBoxenv = function(pos, body) {
return new control.BoxenvControl(pos, body);
};
var makeInstallValue = function(count, pos, isBoxes, rhs, body) {
return new control.InstallValueControl({count: count,
pos: pos,
isBoxes: isBoxes,
rhs: rhs,
body: body});
};
var makeWithContMark = function(key, val, body) {
return new control.WithContMarkControl(key, val, body);
};
var makeAssign = function(id, rhs, isUndefOk) {
return new control.AssignControl({id: id,
rhs: rhs,
isUndefOk: isUndefOk});
};
var makeVarref = function(aToplevel) {
return new control.VarrefControl(aToplevel);
};
var makeClosure = function(genId) {
return new control.ClosureControl(genId);
};
var makeCaseLam = function(name, clauses) {
assert.ok(typeof(clauses) === 'object' && clauses.length !== undefined);
return new control.CaseLamControl(name, clauses);
};
var makeLetrec = function(procs, body) {
return new control.LetRecControl(procs, body);
};
/////////////////////////////////////////////////////////////////////
var testPrim = function(funName, f, baseArgs, expectedValue) {
var state = new StateModule.State();
var args = [];
for (var i = 0; i < baseArgs.length; i++) {
args.push(makeConstant(f(baseArgs[i])));
}
state.pushControl(makeApplication(makePrimval(funName), args));
assert.ok(types.isEqual(run(state),
expectedValue));
};
var testPrimF = function(funName, f, baseArgs, expectedValue, transform) {
var state = new StateModule.State();
var args = [];
for (var i = 0; i < baseArgs.length; i++) {
args.push(makeConstant(f(baseArgs[i])));
}
state.pushControl(makeApplication(makePrimval(funName), args));
assert.deepEqual(transform(run(state)),
expectedValue);
}
var listToStringArray = function(lst) {
var ret = [];
while ( !lst.isEmpty() ) {
ret.push( lst.first().toString() );
lst = lst.rest();
}
return ret;
}
var id = function(x) {return x;};
//////////////////////////////////////////////////////////////////////
var runTest = function(name, thunk) {
sys.print("running " + name + "... ");
try {
thunk();
} catch(e) {
sys.print(" FAIL\n");
sys.print(e);
if (EXIT_ON_FIRST_ERROR) {
if (typeof(console) !== 'undefined' && console.log && e.stack) {
console.log(e.stack);
}
// if (typeof(console) !== 'undefined' && console.log && e.stack) {
// console.log(e.stack);
// }
// sys.print(sys.inspect(e) + '\n');
throw e;
}
}
sys.print(" ok\n")
};
//////////////////////////////////////////////////////////////////////
sys.print("START TESTS\n\n");
runTest("simple empty state",
// Simple running should just terminate, and always be at the "stuck" state.
function() {
var state = new StateModule.State();
assert.ok(state.isStuck());
run(state);
assert.ok(state.isStuck());
});
// Numeric constants should just evaluate through.
runTest("Numeric constant",
function() {
var state = new StateModule.State();
state.pushControl(makeConstant(42));
var result = run(state);
assert.deepEqual(result,
42);
assert.deepEqual(state, makeStateWithConstant(42));
});
// String constant.
runTest("String constant",
function() {
var state = new StateModule.State();
state.pushControl(makeConstant("hello world"));
var result = run(state);
assert.deepEqual(result,
"hello world");
assert.deepEqual(state, makeStateWithConstant("hello world"));
});
// boolean constant.
runTest("Boolean constant",
function() {
var state = new StateModule.State();
state.pushControl(makeConstant(true));
var result = run(state);
assert.deepEqual(result, true);
assert.deepEqual(state, makeStateWithConstant(true));
});
runTest("external call",
function() {
var state = new StateModule.State();
interpret.call(state,
primitive.getPrimitive("*"),
[2, 3],
function(v) { assert.equal(v, 6) });
});
// Simple branch to true
runTest("Simple boolean branch to true",
function() {
var state = new StateModule.State();
state.pushControl(makeBranch(makeConstant(true),
makeConstant(true),
makeConstant(false)));
var result = run(state);
assert.deepEqual(result, true);
});
// Simple branch to false
runTest("Simple boolean branch to false",
function() {
var state = new StateModule.State();
state.pushControl(makeBranch(makeConstant(false),
makeConstant(false),
makeConstant(true)));
var result = run(state);
assert.deepEqual(result,
true);
assert.deepEqual(state, makeStateWithConstant(true));
});
// (if (if true false true) "apple" "pie") --> "pie"
runTest("nested booleans",
function() {
var state = new StateModule.State();
state.pushControl(makeBranch(makeBranch(makeConstant(true), makeConstant(false), makeConstant(true)),
makeConstant("apple"),
makeConstant("pie")));
var result = run(state);
assert.deepEqual(result, "pie");
assert.deepEqual(state, makeStateWithConstant("pie"));
});
// Sequences
runTest("Sequences",
function() {
var state1 = new StateModule.State();
state1.pushControl(makeSeq(makeConstant(3),
makeConstant(4),
makeConstant(5)));
step(state1);
step(state1);
assert.ok(!state1.isStuck());
assert.deepEqual(state1.v, 3);
step(state1);
assert.deepEqual(state1.v, 4);
var result = run(state1);
assert.deepEqual(result, 5);
assert.deepEqual(state1, makeStateWithConstant(5));
});
// Module prefix
runTest("module prefix",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(3),
[]));
run(state);
assert.equal(1, state.vstack.length);
assert.ok(state.vstack[0] instanceof types.PrefixValue);
assert.equal(state.vstack[0].length(), 3);
});
runTest("toplevel lookup",
// toplevel lookup
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(3),
[]));
run(state);
state.vstack[0].set(0, "zero");
state.vstack[0].set(1, "one");
state.vstack[0].set(2, "two");
state.pushControl(makeToplevel(0, 0));
assert.equal(run(state), "zero");
state.pushControl(makeToplevel(0, 1));
assert.equal(run(state), "one");
state.pushControl(makeToplevel(0, 2));
assert.equal(run(state), "two");
});
runTest("define-values",
// define-values
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(3), []));
run(state);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeConstant("try it")));
run(state);
var expectedState = new StateModule.State();
expectedState.pushControl(makeMod(makePrefix(3),
[]));
run(expectedState);
expectedState.v = "try it";
expectedState.vstack[0].set(0, "try it");
assert.deepEqual(state, expectedState);
});
runTest("lambda",
// lambda
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(3), []));
run(state);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeConstant("Some toplevel value")));
run(state);
state.pushControl(makeLam(3, [0], makeConstant("I'm a body")));
var result = run(state);
// result should be a lambda.
assert.ok(result instanceof types.ClosureValue);
assert.equal(result.closureVals.length, 1);
assert.ok(result.closureVals[0] instanceof types.PrefixValue);
assert.deepEqual(result.body, makeConstant("I'm a body"));
assert.equal(result.numParams, 3);
});
runTest("primval (current-print)",
// primval
function() {
var state = new StateModule.State();
state.pushControl(makePrimval("current-print"));
var result = run(state);
assert.ok(result instanceof types.PrimProc);
});
runTest("primval on bad primitive should throw error",
// primval on unknowns should throw error
function() {
var state = new StateModule.State();
state.pushControl(makePrimval("foobar"));
assert.throws(function() { run(state); });
});
runTest("Primval on *",
// primval on *
// primval
function() {
var state = new StateModule.State();
state.pushControl(makePrimval("*"));
var result = run(state);
assert.ok(result instanceof types.PrimProc);
});
runTest("My own list function",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makeLamWithRest(0, [], makeLocalRef(0)),
[makeConstant("one"),
makeConstant("two"),
makeConstant("three")]))
var result = run(state);
assert.deepEqual(result,
types.list(["one", "two", "three"]));
});
runTest("primitive application",
// primitive application.
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval("*"),
[makeConstant(types.rational(3)),
makeConstant(types.rational(5))]));
var result = run(state);
assert.deepEqual(result, types.rational(15));
assert.equal(state.vstack.length, 0);
});
runTest("primitive application, no arguments",
// primitive application with no arguments.
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval("*"),
[]));
var result = run(state);
assert.deepEqual(result, types.rational(1));
assert.equal(state.vstack.length, 0);
});
runTest("primitive application, nested application",
// primitive application, with nesting
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("*"),
[makeApplication(
makePrimval("*"),
[makeConstant(types.rational(3)),
makeConstant(types.rational(5))]),
makeConstant(types.rational(7))]));
var result = run(state);
assert.deepEqual(result, types.rational(105));
assert.equal(state.vstack.length, 0);
});
runTest("primitive appliation, nesting, testing non-commutativity",
// primitive application, with nesting, testing order
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("string-append"),
[makeApplication(
makePrimval("string-append"),
[makeConstant(types.string("hello")),
makeConstant(types.string("world"))]),
makeConstant(types.string("testing"))]));
var result = run(state);
assert.deepEqual(result, types.string("helloworldtesting"));
assert.equal(state.vstack.length, 0);
});
runTest("primitive application, subtraction",
// subtraction
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("-"),
[makeApplication(
makePrimval("-"),
[makeConstant(types.rational(3)),
makeConstant(types.rational(4))]),
makeConstant(types.rational(15))]));
var result = run(state);
assert.deepEqual(result, types.rational(-16));
assert.equal(state.vstack.length, 0);
});
runTest("primitive application, unary subtraction (negation)",
// Checking negation.
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("-"),
[makeConstant(types.rational(1024))]));
var result = run(state);
assert.deepEqual(result, types.rational(-1024));
assert.equal(state.vstack.length, 0);
});
runTest("closure application",
// Closure application
// lambda will just return a constant value
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeLam(1, [],
makeConstant("I'm a body"))));
run(state);
state.pushControl(makeApplication(makeToplevel(1, 0), [makeConstant("boo")]));
var result = run(state);
assert.equal(result, "I'm a body");
assert.equal(state.vstack.length, 1);
});
runTest("closure application, defining square",
// Closure application
// lambda will square its argument
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeLam(1, [],
makeApplication(makePrimval("*"),
[makeLocalRef(2),
makeLocalRef(2)]))));
run(state);
state.pushControl(makeApplication(makeToplevel(1, 0),
[makeConstant(types.rational(4))]));
var result = run(state);
assert.deepEqual(result, types.rational(16));
assert.equal(state.vstack.length, 1);
});
runTest("closure application, testing tail calls",
// Checking tail calling behavior
// The standard infinite loop should consume bounded control stack.
// (define (f) (f)) (begin (f)) --> infinite loop, but with bounded control stack.
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeLam(0, [0],
makeApplication(makeToplevel(0, 0),
[]))));
run(state);
state.pushControl(makeApplication(makeToplevel(0, 0), []));
var MAXIMUM_BOUND = 5;
var ITERATIONS = 1000000;
for (var i = 0; i < ITERATIONS; i++) {
step(state);
assert.ok(state.cstack.length < MAXIMUM_BOUND);
}
});
runTest("closure application, testing tail calls with even/odd",
// Checking tail calling behavior
// The standard infinite loop should consume bounded control stack.
// (define (even? x) (if (zero? x) true (odd? (sub1 x))))
// (define (odd? x) (if (zero? x) false (even? (sub1 x))))
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(2), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues
([makeToplevel(0, 0)],
makeLam(1, [0],
makeBranch(
makeApplication(makePrimval("zero?"),
[makeLocalRef(2)]),
makeConstant(true),
makeApplication(makeToplevel(1, 1),
[makeApplication(
makePrimval("sub1"),
[makeLocalRef(3)])])))));
state.pushControl(makeDefValues
([makeToplevel(0, 1)],
makeLam(1, [0],
makeBranch(
makeApplication(makePrimval("zero?"),
[makeLocalRef(2)]),
makeConstant(false),
makeApplication(makeToplevel(1, 0),
[makeApplication(
makePrimval("sub1"),
[makeLocalRef(3)])])))));
run(state);
var even = function(n) {
state.pushControl(makeApplication(makeToplevel(1, 0),
[makeConstant(types.rational(n))]));
var MAXIMUM_BOUND = 10;
while (!state.isStuck()) {
step(state);
assert.ok(state.cstack.length < MAXIMUM_BOUND);
//sys.print(state.cstack.length + "\n");
}
return state.v;
}
assert.equal(even(0), true);
assert.equal(even(1), false);
assert.equal(even(50), true);
assert.equal(even(51), false);
assert.equal(even(501), false);
assert.equal(even(1001), false);
assert.equal(even(10000), true);
assert.equal(even(10001), false);
});
runTest("factorial",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues(
[makeToplevel(0, 0)],
makeLam(1, [0],
makeBranch(
makeApplication(makePrimval("zero?"),
[makeLocalRef(2)]),
makeConstant(types.rational(1)),
makeApplication(makePrimval("*"),
[makeLocalRef(3),
makeApplication(
makeToplevel(3, 0),
[makeApplication(makePrimval("sub1"),
[makeLocalRef(5)])])])))));
run(state);
var fact = function(n) {
state.pushControl(makeApplication(makeToplevel(1, 0),
[makeConstant(types.rational(n))]));
return run(state);
}
assert.equal(fact(0), 1);
assert.equal(fact(1), 1);
assert.equal(fact(2), 2);
assert.equal(fact(3), 6);
assert.equal(fact(4), 24);
assert.equal(fact(5), 120);
assert.equal(fact(6), 720);
assert.equal(fact(10), 3628800);
assert.equal(fact(11), 39916800);
assert.equal(fact(12), 479001600);
});
runTest("apply on a primitive *",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("apply"),
[makePrimval("*"),
makeConstant(
types.list([types.rational(3),
types.rational(9)]))]));
assert.deepEqual(run(state),
27);
assert.equal(state.vstack.length, 0);
});
runTest("apply on a primitive -",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("apply"),
[makePrimval("-"),
makeConstant(
types.list([types.rational(3),
types.rational(9)]))]));
assert.deepEqual(run(state),
-6);
assert.equal(state.vstack.length, 0);
});
runTest("apply on a primitive -, three arguments",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("apply"),
[makePrimval("-"),
makeConstant(
types.list([types.rational(3),
types.rational(9),
types.rational(12)]))]));
assert.deepEqual(run(state),
-18);
assert.equal(state.vstack.length, 0);
});
runTest("values",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("values"),
[makePrimval("*"),
makeConstant(
types.list([types.rational(3),
types.rational(9),
types.rational(12)]))]));
var result = run(state);
assert.equal(state.vstack.length, 0);
assert.ok(result instanceof types.ValuesWrapper);
assert.equal(result.elts.length, 2);
});
runTest("values with no arguments",
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(
makePrimval("values"),[]));
var result = run(state);
assert.equal(state.vstack.length, 0);
assert.ok(result instanceof types.ValuesWrapper);
assert.equal(result.elts.length, 0);
});
runTest("current-inexact-milliseconds",
function() {
var state = new StateModule.State();
for (var i = 0; i < 2000; i++) {
state.pushControl(makeApplication(
makePrimval("current-inexact-milliseconds"),[]));
var result1 = run(state);
state.pushControl(makeApplication(
makePrimval("current-inexact-milliseconds"),[]));
var result2 = run(state);
assert.ok(jsnums.lessThanOrEqual(result1, result2));
}
});
runTest("values with def-values",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(2), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues(
[makeToplevel(0, 0),
makeToplevel(0, 1)],
makeApplication(makePrimval("values"),
[makeConstant("hello"),
makeConstant("world")])));
run(state);
assert.equal(state.vstack.length, 1);
assert.ok(state.vstack[0] instanceof types.PrefixValue);
assert.equal(state.vstack[0].ref(0), "hello");
assert.equal(state.vstack[0].ref(1), "world");
});
runTest("apply-values",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(2), []));
run(state);
state.pushControl(makeDefValues(
[makeToplevel(0, 0),
makeToplevel(0, 1)],
makeApplication(makePrimval("values"),
[makeConstant(types.string("hello")),
makeConstant(types.string("world"))])));
run(state);
state.pushControl(makeApplyValues(
makeLam(2, [], makeApplication(makePrimval("string-append"),
[makeLocalRef(2),
makeLocalRef(3)])),
makeApplication(makePrimval("values"),
[makeToplevel(2, 0),
makeToplevel(2, 1)])));
assert.deepEqual(run(state), types.string("helloworld"));
});
runTest("apply-values, testing no stack usage",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(2), []));
run(state);
state.pushControl(makeDefValues(
[makeToplevel(0, 0),
makeToplevel(0, 1)],
makeApplication(makePrimval("values"),
[makePrimval("zero?"),
makeConstant(types.rational(0))])));
run(state);
state.pushControl(makeApplyValues(
makeToplevel(0, 0),
makeToplevel(0, 1)));
assert.equal(run(state), true);
assert.equal(state.vstack.length, 1);
});
runTest("let-one, trivial",
function() {
var state = new StateModule.State();
assert.equal(state.vstack.length, 0);
var body = makeLocalRef(0);
state.pushControl(makeLet1(makeConstant("someValue"),
body));
while (state.cstack[state.cstack.length - 1] !== body) {
step(state);
}
assert.equal(state.vstack.length, 1);
assert.equal(state.vstack[0], "someValue");
var result = run(state);
assert.equal(state.vstack.length, 0);
assert.deepEqual(result, "someValue");
});
runTest("let-one, different body",
function() {
var state = new StateModule.State();
assert.equal(state.vstack.length, 0);
var body = makeConstant("something else");
state.pushControl(makeLet1(makeConstant("someValue"),
body));
while (state.cstack[state.cstack.length - 1] !== body) {
step(state);
}
assert.equal(state.vstack.length, 1);
assert.equal(state.vstack[0], "someValue");
var result = run(state);
assert.equal(state.vstack.length, 0);
assert.deepEqual(result, "something else");
});
runTest("let-void, no boxes",
function() {
var state = new StateModule.State();
var body = makeConstant("blah");
state.pushControl(makeLetVoid(2, false, body));
while (state.cstack[state.cstack.length - 1] !== body) {
step(state);
}
assert.equal(state.vstack.length, 2);
for(var i = 0; i < state.vstack.length; i++) {
assert.ok(state.vstack[i] === types.UNDEFINED);
}
var result = run(state);
assert.equal(result, "blah");
assert.equal(state.vstack.length, 0);
});
runTest("let-void, with boxes",
function() {
var state = new StateModule.State();
var body = makeConstant("blah");
state.pushControl(makeLetVoid(2, true, body));
while (state.cstack[state.cstack.length - 1] !== body) {
step(state);
}
assert.equal(state.vstack.length, 2);
for(var i = 0; i < state.vstack.length; i++) {
assert.ok( types.isBox(state.vstack[i]) );
}
var result = run(state);
assert.equal(result, "blah");
assert.equal(state.vstack.length, 0);
});
runTest("beg0 with just one argument should immediately reduce to its argument",
function() {
var state = new StateModule.State();
state.pushControl(makeBeg0(makeConstant("first post")));
step(state);
assert.equal(state.cstack.length, 1);
assert.deepEqual(state.cstack[0],
makeConstant("first post"));
var result = run(state);
assert.equal(result, "first post");
});
runTest("beg0, more general",
function() {
var state = new StateModule.State();
state.pushControl(makeBeg0(makeConstant("first post"),
makeConstant("second post"),
makeConstant("third post"),
makeConstant("fourth post")));
step(state);
// By this point, there should be two elements
// in the control stack, the evaluation of the first
// argument, and a control to continue the
// rest of the sequence evaluation.
assert.equal(state.cstack.length, 2);
var result = run(state);
assert.equal(result, "first post");
});
runTest("boxenv",
function() {
var state = new StateModule.State();
state.pushControl(makeLet1(makeConstant("foo"),
makeBoxenv(0,
makeLocalRef(0))));
var result = run(state);
assert.ok( types.isBox(result) );
assert.deepEqual(result, types.box("foo"));
});
runTest("install-value, without boxes",
function() {
var state = new StateModule.State();
var aBody = makeConstant("peep");
state.pushControl
(makeLetVoid
(4,
false,
makeInstallValue
(3, 1, false,
makeApplication(makePrimval("values"),
[makeConstant("3"),
makeConstant("1"),
makeConstant("4")]),
aBody)));
while (state.cstack[state.cstack.length - 1] !== aBody) {
step(state);
}
assert.equal(state.vstack.length, 4);
assert.equal(state.vstack[0], "4");
assert.equal(state.vstack[1], "1");
assert.equal(state.vstack[2], "3");
var result = run(state);
assert.equal(result, "peep");
assert.equal(state.vstack.length, 0);
});
runTest("install-value, with boxes",
function() {
var state = new StateModule.State();
var aBody = makeConstant("peep");
state.pushControl
(makeLetVoid
(4,
true,
makeInstallValue
(3, 1, true,
makeApplication(makePrimval("values"),
[makeConstant("3"),
makeConstant("1"),
makeConstant("4")]),
aBody)));
while (state.cstack[state.cstack.length - 1] !== aBody) {
step(state);
}
assert.equal(state.vstack.length, 4);
assert.deepEqual(state.vstack[0], types.box("4"));
assert.deepEqual(state.vstack[1], types.box("1"));
assert.deepEqual(state.vstack[2], types.box("3"));
var result = run(state);
assert.equal(result, "peep");
assert.equal(state.vstack.length, 0);
});
runTest("assign",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1),
[makeAssign(makeToplevel(0, 0),
makeConstant("some value"),
true)]));
run(state);
assert.equal(state.vstack.length, 1);
assert.equal(state.vstack[0].ref(0), "some value");
});
runTest("varref",
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(2),
[makeSeq(makeAssign(makeToplevel(0, 0),
makeConstant("a toplevel value"),
true),
makeAssign(
makeToplevel(0, 1),
makeVarref(makeToplevel(0, 0))))]));
var prefixValue = run(state);
// WARNING: breaking abstractions.
// Let's look directly at the representation structures
// and make sure we are dealing with a variable reference.
var result = prefixValue.slots[1];
assert.ok(result instanceof types.VariableReference);
assert.equal(result.ref(), "a toplevel value");
result.set("something else!");
assert.equal(state.vstack.length, 1);
assert.equal(state.vstack[0].ref(0), "something else!");
});
runTest("closure",
function() {
var state = new StateModule.State();
state.heap['some-closure'] = 42;
state.pushControl(makeClosure('some-closure'));
// The way we process closures in bytecode-compiler
// should make this a direct heap lookup.
assert.equal(run(state), 42);
});
runTest("with-cont-mark",
function() {
var state = new StateModule.State();
var aBody = makeConstant("peep");
state.pushControl
(makeWithContMark(makeConstant
(types.symbol("x")),
makeConstant("42"),
aBody));
while (state.cstack[state.cstack.length -1] !== aBody) {
step(state);
}
assert.equal(state.cstack.length, 2);
assert.ok( types.isContMarkRecordControl(state.cstack[0]) );
assert.equal(state.cstack[0].dict.get(types.symbol('x')),
"42");
var result = run(state);
assert.equal(result, "peep");
});
runTest("closure application, testing tail calls in the presence of continuation marks",
// Checking tail calling behavior
// The standard infinite loop should consume bounded control stack.
// (define (f) (call-with-continuation-marks 'x 1 (f))) (begin (f)) --> infinite loop, but with bounded control stack.
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
assert.equal(state.vstack.length, 1);
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeLam(0, [0],
(makeWithContMark
(makeConstant(types.symbol("x")),
makeConstant(types.rational(1)),
makeApplication(makeToplevel(0, 0),
[]))))));
run(state);
state.pushControl(makeApplication(makeToplevel(0, 0), []));
var MAXIMUM_BOUND = 6;
var ITERATIONS = 1000000;
for (var i = 0; i < ITERATIONS; i++) {
step(state);
assert.ok(state.cstack.length < MAXIMUM_BOUND);
}
});
runTest("case-lambda, with a function that consumes one or two values",
function() {
var state = new StateModule.State();
state.pushControl
(makeMod(makePrefix(1),
[makeDefValues
([makeToplevel(0, 0)],
makeCaseLam(types.symbol("last"),
[makeLam(1, [], makeLocalRef(0)),
makeLam(2, [], makeLocalRef(1))]))]));
run(state);
state.pushControl(makeApplication(makeToplevel(1, 0),
[makeConstant(types.rational(5))]));
var result = run(state);
assert.deepEqual(result, types.rational(5));
state.pushControl(makeApplication(makeToplevel(2, 0),
[makeConstant(types.rational(7)),
makeConstant(types.rational(42))]));
result = run(state);
assert.deepEqual(result, types.rational(42));
});
// runTest("factorial again, testing the accumulation of continuation marks",
// //
// // (define marks #f)
// // (define (f x)
// // (with-continuation-marks 'x x
// // (if (= x 0)
// // (begin (set! marks (current-continuation-marks))
// // 1)
// // (* x (f (sub1 x))))))
// function() {
// });
runTest("let-rec",
function() {
var state = new StateModule.State();
state.pushControl(makeLetVoid(2,
false,
makeLetrec([makeLam(1, [1],
makeBranch
(makeApplication(makePrimval("zero?"),
[makeLocalRef(2)]),
makeConstant(true),
makeApplication(makeLocalRef(1),
[makeApplication
(makePrimval("sub1"),
[makeLocalRef(3)])]))),
makeLam(1, [0],
makeBranch
(makeApplication(makePrimval("zero?"),
[makeLocalRef(2)]),
makeConstant(false),
makeApplication(makeLocalRef(1),
[makeApplication
(makePrimval("sub1"),
[makeLocalRef(3)])])))],
makeLocalRef(0))));
var evenValue = run(state);
var e = function(x) {
state.pushControl(makeApplication(makeConstant(evenValue),
[makeConstant(types.rational(x))]));
return run(state);
}
assert.equal(state.vstack.length, 0);
assert.equal(e(0), true);
assert.equal(e(1), false);
assert.equal(e(2), true);
assert.equal(e(3), false);
assert.equal(e(100), true);
assert.equal(e(101), false);
assert.equal(e(10000), true);
assert.equal(e(10001), false);
});
/***************************************
*** Primitive String Function Tests ***
***************************************/
runTest('symbol?',
function() {
testPrim('symbol?', types.symbol, ['hi'], true);
testPrim('symbol?', types.rational, [1], false);
});
runTest('symbol=?',
function() {
testPrim('symbol=?', types.symbol, ['abc', 'abd'], false);
testPrim('symbol=?', types.symbol, ['cdf', 'cdf'], true);
});
runTest('string->symbol',
function() {
testPrim('string->symbol', id, ['hello!'], types.symbol('hello!'));
testPrim('string->symbol', types.string, [' world'], types.symbol(' world'));
});
runTest('symbol->string',
function() {
testPrim('symbol->string', types.symbol, ['hello!'], types.string('hello!'));
});
runTest('number->string',
function() {
testPrim('number->string', types.rational, [5], types.string('5'));
testPrim('number->string', id, [types.complex(0, 2)], types.string('0+2i'));
testPrim('number->string', id, [types.rational(5, 3)], types.string('5/3'));
});
runTest('stinrg->number',
function() {
testPrim('string->number', types.string, ['abc'], false);
testPrim('string->number', id, ['123'], 123);
testPrim('string->number', types.string, ['0+3i'], types.complex(0, 3));
});
runTest('string?',
function() {
testPrim('string?', id, [types.symbol('hello!')], false);
testPrim('string?', id, ['string'], true);
testPrim('string?', types.string, ['world'], true);
});
runTest('make-string',
function() {
testPrim('make-string', id, [0, types.char('A')], types.string(""));
testPrim('make-string', id, [types.rational(3), types.char('b')], types.string('bbb'));
});
runTest('string',
function() {
testPrim('string', id, [], types.string(''));
testPrim('string', types.char, ['a', 'b'], types.string('ab'));
});
runTest('string-length',
function() {
testPrim('string-length', types.string, [''], 0);
testPrim('string-length', id, ['5'], 1);
testPrim('string-length', types.string, ['antidisestablishmentarianism'], 28);
});
runTest('string-ref',
function() {
testPrim('string-ref', id, ['world', 3], types.char('l'));
testPrim('string-ref', id, [types.string('abcd'), 1], types.char('b'));
testPrim('string-ref', id, [types.string('asdfasdf'), 4], types.char('a'));
});
runTest('string=?',
function() {
testPrim('string=?', id, ['asdf', 'Asdf'], false);
testPrim('string=?', id, ['asdf', types.string('asdf')], true);
testPrim('string=?', types.string, ['asdf', 'asdf', 'Asdf'], false);
testPrim('string=?', types.string, ['far', 'fAr'], false);
testPrim('string=?', id, ['', ''], true);
testPrim('string=?', types.string, ['as', 'as', 'as'], true);
testPrim('string=?', types.string, ['1', '1', '2'], false);
});
runTest('string-ci=?',
function() {
testPrim('string-ci=?', id, ['asdf', 'Asdf'], true);
testPrim('string-ci=?', id, ['asdf', types.string('asdf')], true);
testPrim('string-ci=?', types.string, ['asdf', 'asdf', 'Asdf'], true);
testPrim('string-ci=?', types.string, ['far', 'fAr'], true);
testPrim('string-ci=?', id, ['', ''], true);
testPrim('string-ci=?', types.string, ['as', 'as', 'as'], true);
testPrim('string-ci=?', types.string, ['1', '1', '2'], false);
});
runTest('string<?',
function() {
testPrim('string<?', id, ["", "a"], true);
testPrim('string<?', types.string, ['abc', 'ab'], false);
testPrim('string<?', id, [types.string('abc'), 'abc'], false);
testPrim('string<?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string<?', id, ['A', types.string(']'), 'a'], true);
testPrim('string<?', types.string, ['a', 'b', 'c', 'd', 'dd', 'e'], true);
});
runTest('string>?',
function() {
testPrim('string>?', id, ["", "a"], false);
testPrim('string>?', types.string, ['abc', 'ab'], true);
testPrim('string>?', id, [types.string('abc'), 'abc'], false);
testPrim('string>?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string>?', id, ['a', types.string(']'), 'A'], true);
testPrim('string>?', types.string, ['e', 'd', 'cc', 'c', 'b', 'a'], true);
});
runTest('string<=?',
function() {
testPrim('string<=?', id, ["", "a"], true);
testPrim('string<=?', types.string, ['abc', 'ab'], false);
testPrim('string<=?', id, [types.string('abc'), 'abc'], true);
testPrim('string<=?', types.string, ['abc', 'aBc'], false);
testPrim('string<=?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string<=?', id, ['A', types.string(']'), 'a'], true);
testPrim('string<=?', types.string, ['a', 'b', 'b', 'd', 'dd', 'e'], true);
});
runTest('string>=?',
function() {
testPrim('string>=?', id, ["", "a"], false);
testPrim('string>=?', types.string, ['abc', 'ab'], true);
testPrim('string>=?', id, [types.string('abc'), 'abc'], true);
testPrim('string>=?', types.string, ['aBc', 'abc'], false);
testPrim('string>=?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string>=?', id, ['a', types.string(']'), 'A'], true);
testPrim('string>=?', types.string, ['e', 'e', 'cc', 'c', 'b', 'a'], true);
});
runTest('string-ci<?',
function() {
testPrim('string-ci<?', id, ["", "a"], true);
testPrim('string-ci<?', id, [types.string('Abc'), 'ab'], false);
testPrim('string-ci<?', types.string, ['abc', 'abc'], false);
testPrim('string-ci<?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string-ci<?', types.string, ['a', 'b', 'C', 'd', 'dd', 'e'], true);
});
runTest('string-ci>?',
function() {
testPrim('string-ci>?', id, ["", "a"], false);
testPrim('string-ci>?', id, [types.string('Abc'), 'ab'], true);
testPrim('string-ci>?', types.string, ['abc', 'abc'], false);
testPrim('string-ci>?', types.string, ['def', 'abc', 'cde'], false);
testPrim('string-ci>?', types.string, ['e', 'D', 'cc', 'c', 'b', 'a'], true);
});
runTest('string-ci<=?',
function() {
testPrim('string-ci<=?', id, ["", "a"], true);
testPrim('string-ci<=?', types.string, ['Abc', 'ab'], false);
testPrim('string-ci<=?', id, [types.string('abc'), 'abc'], true);
testPrim('string-ci<=?', types.string, ['abc', 'aBc'], true);
testPrim('string-ci<=?', types.string, ['abc', 'def', 'cde'], false);
testPrim('string-ci<=?', types.string, ['a', 'b', 'b', 'D', 'dd', 'e'], true);
});
runTest('string-ci>=?',
function() {
testPrim('string-ci>=?', id, ["", "a"], false);
testPrim('string-ci>=?', types.string, ['Abc', 'ab'], true);
testPrim('string-ci>=?', id, [types.string('abc'), 'abc'], true);
testPrim('string-ci>=?', types.string, ['aBc', 'abc'], true);
testPrim('string-ci>=?', types.string, ['def', 'abc', 'cde'], false);
testPrim('string-ci>=?', types.string, ['e', 'e', 'cc', 'C', 'b', 'a'], true);
});
runTest('substring',
function() {
testPrim('substring', id, ['abc', 1], types.string('bc'));
testPrim('substring', id, [types.string('abc'), 0], types.string('abc'));
testPrim('substring', id, ['abcdefgh', 2, 4], types.string('cd'));
testPrim('substring', id, [types.string('abc'), 3], types.string(''));
testPrim('substring', id, [types.string('abcd'), 2, 2], types.string(''));
});
runTest('string-append',
function() {
testPrim('string-append', types.string, [], types.string(''));
testPrim('string-append', id, ['a', types.string('b'), 'c'], types.string('abc'));
testPrim('string-append', types.string, ['a', '', 'b', ' world'], types.string('ab world'));
});
runTest('string->list',
function() {
testPrim('string->list', types.string, [''], types.EMPTY);
testPrim('string->list', id, ['one'], types.list([types.char('o'), types.char('n'), types.char('e')]));
testPrim('string->list', types.string, ['two'], types.list([types.char('t'),
types.char('w'),
types.char('o')]));
});
runTest('list->string',
function() {
testPrim('list->string', id, [types.EMPTY], types.string(''));
testPrim('list->string', id,
[types.list([types.char('H'),
types.char('e'),
types.char('l'),
types.char('l'),
types.char('o')])],
types.string('Hello'));
});
runTest('string-copy',
function() {
testPrim('string-copy', types.string, [''], types.string(''));
testPrim('string-copy', id, ['had'], types.string('had'));
testPrim('string-copy', types.string, ['hello'], types.string('hello'));
var state = new StateModule.State();
var str = types.string('hello');
state.pushControl(makeApplication(makePrimval('string-copy'), [makeConstant(str)]));
var result = run(state);
assert.deepEqual(result, str);
assert.ok(result !== str);
});
runTest('format',
function() {
testPrim('format', types.string, ['hello'], types.string('hello'));
testPrim('format', id, ['hello~n'], types.string('hello\n'));
testPrim('format', id, [types.string('Test: ~a~nTest2: ~A~%'),
types.char('A'),
types.list([1, 2, 3])],
types.string('Test: A\nTest2: (1 2 3)\n'));
testPrim('format', id, ['~s ~S ~a',
types.char('b'),
types.complex(0, 2),
types.char('b')],
types.string('#\\b 0+2i b'));
testPrim('format', id, ['~s ~a', primitive.getPrimitive('+'), primitive.getPrimitive('format')],
types.string('#<procedure:+> #<procedure:format>'));
var box1 = types.box('junk');
var box2 = types.box(box1);
box1.set(box2);
testPrim('format', id, ['~s', box1], types.string('#&#&...'));
var box3 = types.box('junk');
box3.set(box3);
testPrim('format', id, ['~a', box3], types.string('#&...'));
});
runTest('explode',
function() {
testPrim('explode', id, [''], types.EMPTY);
testPrim('explode', types.string, ['hello'], types.list([types.string('h'),
types.string('e'),
types.string('l'),
types.string('l'),
types.string('o')]));
});
runTest('implode',
function() {
testPrim('implode', id, [types.EMPTY], types.string(''));
testPrim('implode', types.list, [[types.string('h'),
types.string('e'),
types.string('l'),
types.string('l'),
types.string('o')]],
types.string('hello'));
});
runTest('string->int',
function() {
testPrim('string->int', types.string, ['0'], 48);
testPrim('string->int', types.string, ['\n'], 10);
});
runTest('int->string',
function() {
testPrim('int->string', id, [50], types.string('2'));
testPrim('int->string', id, [10], types.string('\n'));
});
runTest('string-alphabetic?',
function() {
testPrim('string-alphabetic?', id, ['abcd'], true);
testPrim('string-alphabetic?', types.string, ['AbCZ'], true);
testPrim('string-alphabetic?', id, ['a b c'], false);
testPrim('string-alphabetic?', types.string, ['1243!'], false);
});
runTest('string-ith',
function() {
testPrim('string-ith', id, ['abcde', 2], types.string('c'));
testPrim('string-ith', id, [types.string('12345'), 0], types.string('1'));
});
runTest('string-lower-case?',
function() {
testPrim('string-lower-case?', types.string, ['abcd'], true);
testPrim('string-lower-case?', id, ['abc1'], false);
testPrim('string-lower-case?', types.string, ['Abc'], false);
});
runTest('string-numeric?',
function() {
testPrim('string-numeric?', id, ['1234'], true);
testPrim('string-numeric?', types.string, ['5432'], true);
testPrim('string-numeric?', types.string, ['0+2i'], false);
testPrim('string-numeric?', types.string, ['03()'], false);
});
runTest('string-upper-case?',
function() {
testPrim('string-upper-case?', id, ['ABCD'], true);
testPrim('string-upper-case?', types.string, ['ADF'], true);
testPrim('string-upper-case?', types.string, ['AbZ'], false);
testPrim('string-upper-case?', types.string, ['05AB'], false);
});
runTest('string-whitespace?',
function() {
testPrim('string-whitespace?', types.string, ['a b c'], false);
testPrim('string-whitespace?', id, [' \n '], true);
testPrim('string-whitespace?', types.string, ['\t\r\n '], true);
});
runTest('replicate',
function() {
testPrim('replicate', id, [3, types.string('ab')], types.string('ababab'))
testPrim('replicate', id, [0, 'hi'], types.string(''));
testPrim('replicate', id, [50, types.string('')], types.string(''));
});
runTest('string->immutable-string',
function() {
testPrim('string->immutable-string', id, ['hello'], 'hello');
testPrim('string->immutable-string', types.string, ['world'], 'world');
});
runTest('string-set!',
function() {
var str1 = types.string('hello');
testPrim('string-set!', id, [str1, 2, types.char('w')], types.VOID);
assert.deepEqual(str1, types.string('hewlo'));
var str2 = types.string('no');
testPrim('string-set!', id, [str2, 1, types.char('!')], types.VOID);
assert.deepEqual(str2, types.string('n!'));
});
runTest('string-fill!',
function() {
var str1 = types.string('lawl');
testPrim('string-fill!', id, [str1, types.char('q')], types.VOID);
assert.deepEqual(str1, types.string('qqqq'));
var str2 = types.string('');
testPrim('string-fill!', id, [str2, types.char(' ')], types.VOID);
assert.deepEqual(str2, types.string(''));
});
/*************************************
*** Primitive Math Function Tests ***
*************************************/
runTest("zero?",
function() {
testPrim('zero?', types.rational, [0], true);
testPrim('zero?', types.rational, [1], false);
testPrim('zero?', id, [types.complex(0, 1)], false);
});
runTest("sub1",
function() {
testPrim('sub1', types.rational, [25], types.rational(24));
testPrim('sub1', id, [types.complex(3, 5)], types.complex(2, 5));
});
runTest("add1",
function() {
testPrim('add1', types.rational, [25], types.rational(26));
testPrim('add1', id, [types.complex(3, 5)], types.complex(4, 5));
});
runTest("+",
function() {
testPrim('+', types.rational, [], types.rational(0));
testPrim('+', types.rational, [2], types.rational(2));
testPrim('+', types.rational, [1, 2], types.rational(3));
testPrim('+', types.rational, [1, 2, 3, 4], types.rational(10));
});
runTest("-",
function() {
testPrim('-', types.rational, [2], types.rational(-2));
testPrim('-', types.rational, [1, 2], types.rational(-1));
testPrim('-', types.rational, [1, 2, 3, 4], types.rational(-8));
});
runTest("*",
function() {
testPrim('*', types.rational, [], types.rational(1));
testPrim('*', types.rational, [2], types.rational(2));
testPrim('*', types.rational, [1, 2], types.rational(2));
testPrim('*', types.rational, [1, 2, 3, 4], types.rational(24));
});
runTest("/",
function() {
testPrim('/', types.rational, [2], types.rational(1, 2));
testPrim('/', types.rational, [1, 3], types.rational(1, 3));
testPrim('/', types.rational, [18, 2, 3, 4], types.rational(3, 4));
});
runTest('abs',
function() {
testPrim('abs', types.rational, [2], types.rational(2));
testPrim('abs', types.rational, [0], types.rational(0));
testPrim('abs', types.rational, [-2], types.rational(2));
});
runTest('quotient',
function() {
testPrim('quotient', types.rational, [5, 3], types.rational(1));
});
runTest('remainder',
function() {
testPrim('remainder', types.rational, [5, 3], types.rational(2));
});
runTest('modulo',
function() {
testPrim('modulo', types.rational, [-5, 3], types.rational(1));
});
runTest('=',
function() {
testPrim('=', types.rational, [2, 3], false);
testPrim('=', types.rational, [2, 2, 2, 2], true);
testPrim('=', types.rational, [2, 2, 3, 3], false);
});
runTest('<',
function() {
testPrim('<', types.rational, [1, 2], true);
testPrim('<', types.rational, [2, 2], false);
testPrim('<', types.rational, [3, 2], false);
testPrim('<', types.rational, [1, 2, 3, 4], true);
testPrim('<', types.rational, [1, 2, 2, 3], false);
testPrim('<', types.rational, [1, 3, 5, 4], false);
});
runTest('>',
function() {
testPrim('>', types.rational, [1, 2], false);
testPrim('>', types.rational, [2, 2], false);
testPrim('>', types.rational, [3, 2], true);
testPrim('>', types.rational, [4, 3, 2, 1], true);
testPrim('>', types.rational, [4, 3, 3, 2], false);
testPrim('>', types.rational, [4, 3, 5, 2], false);
});
runTest('<=',
function() {
testPrim('<=', types.rational, [1, 2], true);
testPrim('<=', types.rational, [2, 2], true);
testPrim('<=', types.rational, [3, 2], false);
testPrim('<=', types.rational, [1, 2, 3, 4], true);
testPrim('<=', types.rational, [2, 3, 3, 3], true);
testPrim('<=', types.rational, [1, 3, 5, 4], false);
});
runTest('>=',
function() {
testPrim('>=', types.rational, [1, 2], false);
testPrim('>=', types.rational, [2, 2], true);
testPrim('>=', types.rational, [3, 2], true);
testPrim('>=', types.rational, [4, 3, 2, 1], true);
testPrim('>=', types.rational, [4, 3, 3, 2], true);
testPrim('>=', types.rational, [5, 3, 5, 4], false);
});
runTest('positive?',
function() {
testPrim('positive?', types.rational, [-1], false);
testPrim('positive?', types.rational, [0], false);
testPrim('positive?', types.rational, [1], true);
});
runTest('negative?',
function() {
testPrim('negative?', types.rational, [-1], true);
testPrim('negative?', types.rational, [0], false);
testPrim('negative?', types.rational, [1], false);
});
runTest('max',
function() {
testPrim('max', types.rational, [1], types.rational(1));
testPrim('max', types.rational, [1, 2], types.rational(2));
testPrim('max', types.rational, [2, 1, 4, 3, 6, 2], types.rational(6));
});
runTest('min',
function() {
testPrim('min', types.rational, [1], types.rational(1));
testPrim('min', types.rational, [1, 2], types.rational(1));
testPrim('min', types.rational, [2, 1, 4, 3, 6, 2], types.rational(1));
});
runTest('=~',
function() {
testPrim('=~', id, [1, 2, 2], true);
testPrim('=~', id, [1, 2, types.float(0.5)], false);
testPrim('=~', types.rational, [5, 3, 1], false);
testPrim('=~', types.rational, [5, 3, 4], true);
});
runTest('conjugate',
function() {
testPrim('conjugate', id, [1], 1);
testPrim('conjugate', id, [types.complex(3, 3)], types.complex(3, -3));
});
runTest('magnitude',
function() {
testPrim('magnitude', id, [4], 4);
testPrim('magnitude', id, [types.complex(3, 4)], 5);
testPrim('magnitude', id, [types.float(3.5)], types.float(3.5));
testPrim('magnitude', id, [types.rational(3, 5)], types.rational(3, 5));
testPrim('magnitude', id, [types.complex(12, 5)], 13);
});
runTest('number?',
function() {
testPrim('number?', id, [5], true);
testPrim('number?', types.rational, [10], true);
testPrim('number?', id, [types.rational(10, 3)], true);
testPrim('number?', types.float, [10.5], true);
testPrim('number?', id, [types.complex(5, 3)], true);
testPrim('number?', id, ['string'], false);
});
runTest('complex?',
function() {
testPrim('complex?', id, [5], true);
testPrim('complex?', types.rational, [10], true);
testPrim('complex?', id, [types.rational(10, 3)], true);
testPrim('complex?', types.float, [10.5], true);
testPrim('complex?', id, [types.complex(5, 3)], true);
testPrim('complex?', id, ['string'], false);
});
runTest('real?',
function() {
testPrim('real?', id, [5], true);
testPrim('real?', types.rational, [10], true);
testPrim('real?', id, [types.rational(10, 3)], true);
testPrim('real?', types.float, [10.5], true);
testPrim('real?', id, [types.complex(5, 3)], false);
testPrim('real?', id, ['string'], false);
});
runTest('rational?',
function() {
testPrim('rational?', id, [5], true);
testPrim('rational?', types.rational, [10], true);
testPrim('rational?', id, [types.rational(10, 3)], true);
testPrim('rational?', types.float, [10.5], true);
testPrim('rational?', types.float, [Math.sqrt(2)], true);
testPrim('rational?', id, [types.complex(5, 3)], false);
testPrim('rational?', id, ['string'], false);
});
runTest('integer?',
function() {
testPrim('integer?', id, [5], true);
testPrim('integer?', types.rational, [10], true);
testPrim('integer?', id, [types.complex(5, 0)], true);
testPrim('integer?', id, [types.rational(10, 3)], false);
testPrim('integer?', types.float, [10.5], false);
testPrim('integer?', id, [types.complex(5, 3)], false);
testPrim('integer?', id, ['string'], false);
});
runTest('exact?',
function() {
testPrim('exact?', id, [5], true);
testPrim('exact?', id, [types.rational(4, 3)], true);
testPrim('exact?', types.float, [10.0], false);
testPrim('exact?', id, [types.complex(5, 2)], true);
testPrim('exact?', id, [types.complex(types.float(5.2), types.float(0.1))], false);
});
runTest('inexact?',
function() {
testPrim('inexact?', id, [5], false);
testPrim('inexact?', id, [types.rational(4, 3)], false);
testPrim('inexact?', types.float, [10.0], true);
testPrim('inexact?', id, [types.complex(5, 2)], false);
testPrim('inexact?', id, [types.complex(types.float(5.2), types.float(0.1))], true);
});
runTest('odd? and even?',
function() {
testPrim('odd?', id, [5], true);
testPrim('odd?', types.float, [10.0], false);
testPrim('even?', id, [15], false);
testPrim('even?', types.float, [13.0], false);
});
runTest('gcd and lcm',
function() {
testPrim('gcd', id, [1001, 98], 7);
testPrim('gcd', id, [6, 10, 15], 1);
testPrim('lcm', id, [91, 77], 1001);
testPrim('lcm', id, [6, 10, 15], 30);
});
runTest('floor, ceiling, and round',
function() {
testPrim('floor', id, [14], 14);
testPrim('floor', types.float, [12.56], types.float(12));
testPrim('ceiling', id, [13], 13);
testPrim('ceiling', types.float, [12.23], types.float(13));
testPrim('ceiling', types.float, [12.00], types.float(12));
testPrim('round', id, [124], 124);
testPrim('round', types.float, [12.432], types.float(12));
testPrim('round', types.float, [12.543], types.float(13));
});
runTest('numerator and denominator',
function() {
testPrim('numerator', id, [30], 30);
testPrim('numerator', id, [types.rational(10, -2)], -5);
testPrim('numerator', types.float, [10.5], types.float(21));
testPrim('numerator', types.float, [-2.53], types.float(-253));
testPrim('denominator', id, [43], 1);
testPrim('denominator', id, [types.rational(12, 4)], 1);
testPrim('denominator', id, [types.rational(23, -5)], 5);
testPrim('denominator', types.float, [12.125], types.float(8));
testPrim('denominator', types.float, [-2.53], types.float(100));
});
runTest('exp and log',
function() {
testPrim('exp', id, [0], 1);
testPrim('exp', types.float, [0], types.float(1));
testPrim('exp', id, [3], types.float(Math.exp(3)));
testPrim('log', id, [1], 0);
testPrim('log', types.float, [1], types.float(0));
testPrim('log', id, [primitive.getPrimitive('e')], types.float(1));
});
runTest('sin, cos, tan, asin, acos, atan',
function() {
testPrim('sin', id, [20], types.float(Math.sin(20)));
testPrim('sin', id, [0], 0);
testPrim('cos', id, [0], 1);
testPrim('cos', types.float, [43], types.float(Math.cos(43)));
testPrim('tan', types.float, [0], types.float(0));
testPrim('tan', id, [-30], types.float(Math.tan(-30)));
testPrim('asin', types.float, [-0.5], types.float(Math.asin(-0.5)));
testPrim('acos', types.float, [0.53], types.float(Math.acos(0.53)));
testPrim('atan', types.float, [-543], types.float(Math.atan(-543)));
});
runTest('sqrt, integer-sqrt, and expt',
function() {
testPrim('sqrt', id, [25], 5);
testPrim('sqrt', types.float, [1.44], types.float(1.2));
testPrim('sqrt', id, [-1], types.complex(0, 1));
testPrim('sqrt', id, [types.complex(0, 2)], types.complex(1, 1));
testPrim('sqrt', id, [types.complex(types.float(0), types.float(-2))],
types.complex(types.float(1), types.float(-1)));
testPrim('integer-sqrt', id, [15], 3);
testPrim('integer-sqrt', id, [88], 9);
testPrim('expt', id, [2, 20], 1048576);
testPrim('expt', id, [3, 3], 27);
testPrim('expt', types.float, [12.4, 5.43], types.float(Math.pow(12.4, 5.43)));
});
runTest('make-rectangular, make-polar, real-part, imag-part, angle',
function() {
testPrim('make-rectangular', id, [5, 3], types.complex(5, 3));
testPrim('make-rectangular', id, [5, types.float(4)],
types.complex(types.float(5), types.float(4)));
testPrim('make-polar', id, [1, 0], types.complex(1, 0));
testPrimF('make-polar', types.float, [5, Math.PI/2], true,
function(res) {
return (jsnums.isInexact(res) &&
Math.abs(jsnums.toFixnum(jsnums.realPart(res))) < 0.000001 &&
Math.abs(jsnums.toFixnum(jsnums.imaginaryPart(res)) - 5) < 0.0000001);
});
testPrim('real-part', id, [14], 14);
testPrim('real-part', types.float, [4], types.float(4));
testPrim('real-part', id, [types.complex(0, 1)], 0);
testPrim('real-part', id, [types.complex(types.float(1.44), types.float(5))], types.float(1.44));
testPrim('imag-part', id, [14], 0);
testPrim('imag-part', types.float, [4], 0);
testPrim('imag-part', id, [types.complex(0, 1)], 1);
testPrim('imag-part', id, [types.complex(types.float(1.44), types.float(5))], types.float(5));
testPrim('angle', id, [types.complex(3, 0)], 0);
testPrim('angle', types.float, [4.46], 0);
testPrim('angle', id, [-54], types.float(Math.PI));
testPrimF('angle', id, [types.complex(1, 1)], true,
function(res) {
return (jsnums.isInexact(res) &&
Math.abs(jsnums.toFixnum(res) - Math.PI/4) < 0.0000001);
});
});
runTest('exact->inexact and inexact->exact',
function() {
testPrim('exact->inexact', id, [5], types.float(5));
testPrim('exact->inexact', types.float, [5.2], types.float(5.2));
testPrim('exact->inexact', id, [types.rational(2, 3)], types.float(2/3));
testPrim('exact->inexact', id, [types.complex(3, 5)], types.complex(types.float(3), types.float(5)));
testPrim('inexact->exact', types.float, [0], 0);
testPrim('inexact->exact', types.float, [1.25], types.rational(5, 4));
testPrim('inexact->exact', id, [5], 5);
testPrim('inexact->exact', id, [types.complex(5, 3)], types.complex(5, 3));
testPrim('inexact->exact', id, [types.complex(types.float(5.2), types.float(4))],
types.complex(types.rational(26, 5), 4));
});
runTest('first, second, third, fourth, fifth, sixth, seventh, eighth',
function() {
var testList1 = types.list([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
var testList2 = types.list([types.list([1, 2]),
types.list([3, 4]),
types.list([5, 6]),
types.list([7, 8]),
types.list([9, 10]),
types.list([11, 12]),
types.list([13, 14]),
types.list([15, 16]),
types.list([17, 18]),
types.list([19, 20])]);
testPrim('first', id, [testList1], 1);
testPrim('first', id, [testList2], types.list([1, 2]));
testPrim('second', id, [testList1], 2);
testPrim('second', id, [testList2], types.list([3, 4]));
testPrim('third', id, [testList1], 3);
testPrim('third', id, [testList2], types.list([5, 6]));
testPrim('fourth', id, [testList1], 4);
testPrim('fourth', id, [testList2], types.list([7, 8]));
testPrim('fifth', id, [testList1], 5);
testPrim('fifth', id, [testList2], types.list([9, 10]));
testPrim('sixth', id, [testList1], 6);
testPrim('sixth', id, [testList2], types.list([11, 12]));
testPrim('seventh', id, [testList1], 7);
testPrim('seventh', id, [testList2], types.list([13, 14]));
testPrim('eighth', id, [testList1], 8);
testPrim('eighth', id, [testList2], types.list([15, 16]));
});
/*************************************
*** Primitive List Function Tests ***
*************************************/
runTest('cons, car, and cdr',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('car'),
[makeApplication(makePrimval('cons'),
[makeConstant(types.rational(1)),
makeConstant(types.EMPTY)])]));
assert.deepEqual(run(state), types.rational(1));
state.pushControl(makeApplication(makePrimval('cdr'),
[makeApplication(makePrimval('cons'),
[makeConstant(types.rational(1)),
makeConstant(types.EMPTY)])]));
assert.deepEqual(run(state), types.EMPTY);
state.pushControl(makeApplication(makePrimval('cdr'),
[makeApplication(makePrimval('cons'),
[makeConstant(types.rational(1)),
makeApplication(makePrimval('cons'),
[makeConstant(types.rational(2)),
makeConstant(types.EMPTY)])])]));
assert.deepEqual(run(state), types.pair(2, types.EMPTY));
});
runTest('list?',
function() {
testPrim('list?', id, [types.EMPTY], true);
testPrim('list?', id, [types.pair(1, types.EMPTY)], true);
testPrim('list?', id, [types.list([1, 2, 0, 3, 2])], true);
testPrim('list?', id, [types.pair(1, 4)], false);
testPrim('list?', id, [types.complex(0, 2)], false);
});
runTest('list',
function() {
testPrim('list', types.rational, [], types.EMPTY);
testPrim('list', types.rational, [1], types.pair(types.rational(1), types.EMPTY));
testPrim('list', types.rational, [1, 5, 3], types.list([types.rational(1),
types.rational(5),
types.rational(3)]));
});
runTest('list*',
function() {
testPrim('list*', id, [types.EMPTY], types.EMPTY);
testPrim('list*', id, [types.rational(1), types.pair(types.rational(2), types.EMPTY)],
types.list([types.rational(1), types.rational(2)]));
testPrim('list*', id, [1, 2, 3, types.list([4, 5])], types.list([1, 2, 3, 4, 5]));
});
runTest('length',
function() {
testPrim('length', id, [types.EMPTY], 0);
testPrim('length', types.list, [[1]], 1);
testPrim('length', types.list, [[1, 2, 3, 4]], 4);
});
runTest('append',
function() {
testPrim('append', types.list, [], types.EMPTY);
testPrim('append', types.list, [[1]], types.list([1]));
testPrim('append', types.list, [[], [1, 2, 3], [1, 2]],
types.list([1, 2, 3, 1, 2]));
testPrim('append', id, [types.list([1, 2]), types.list([3]), 4],
types.pair(1, types.pair(2, types.pair(3, 4))));
testPrim('append', id, [5], 5);
testPrim('append', id, [types.EMPTY, 3], 3);
});
runTest('reverse',
function() {
testPrim('reverse', id, [types.EMPTY], types.EMPTY);
testPrim('reverse', id, [types.list([1])], types.list([1]));
testPrim('reverse', id, [types.list([1, 2, 3, 4, 5])], types.list([5, 4, 3, 2, 1]));
});
runTest('list-ref',
function() {
var testList = types.list([types.rational(1),
types.rational(1),
types.rational(2),
types.rational(3),
types.rational(5),
types.rational(8),
types.rational(11)]);
testPrim('list-ref', id, [testList, types.rational(0)], types.rational(1));
testPrim('list-ref', id, [testList, types.rational(5)], types.rational(8));
});
runTest('memq',
function() {
testPrim('memq', id, [0, types.list([1, 2, 3])], false);
testPrim('memq', id, [2, types.list([1, 2, 3])], types.list([2, 3]));
testPrim('memq', id, [types.complex(2, 2),
types.list([types.complex(1, 1),
types.complex(2, 2),
types.complex(3, 3)])],
false);
testPrim('memq', id, [types.char('a'),
types.list([types.char('c'),
types.char('b'),
types.char('a')])],
types.list([types.char('a')]));
testPrim('memq', id, [types.string('a'),
types.list([types.string('c'),
types.string('b'),
types.string('a')])],
false);
var str = types.string('hi');
testPrim('memq', id, [str, types.list([types.string('Yo'),
types.string(', '),
str])],
types.list([str]));
});
runTest('memv',
function() {
testPrim('memv', id, [0, types.list([1, 2, 3])], false);
testPrim('memv', id, [2, types.list([1, 2, 3])], types.list([2, 3]));
testPrim('memv', id, [types.complex(2, 2),
types.list([types.complex(1, 1),
types.complex(2, 2),
types.complex(3, 3)])],
types.list([types.complex(2, 2), types.complex(3, 3)]));
testPrim('memv', id, [types.char('a'),
types.list([types.char('c'),
types.char('b'),
types.char('a')])],
types.list([types.char('a')]));
testPrim('memv', id, [types.string('a'),
types.list([types.string('c'),
types.string('b'),
types.string('a')])],
false);
var str = types.string('hi');
testPrim('memv', id, [str, types.list([types.string('Yo'),
types.string(', '),
str])],
types.list([str]));
});
runTest('member',
function() {
testPrim('member', id, [0, types.list([1, 2, 3])], false);
testPrim('member', id, [2, types.list([1, 2, 3])], types.list([2, 3]));
testPrim('member', id, [types.complex(2, 2),
types.list([types.complex(1, 1),
types.complex(2, 2),
types.complex(3, 3)])],
types.list([types.complex(2, 2), types.complex(3, 3)]));
testPrimF('member', id, [types.char('b'),
types.list([types.char('c'),
types.char('b'),
types.char('a')])],
['#\\b', '#\\a'], listToStringArray);
testPrimF('member', id, [types.string('a'),
types.list([types.string('c'),
types.string('b'),
types.string('a')])],
['a'], listToStringArray);
var str = types.string('hi');
testPrim('member', id, [str, types.list([types.string('Yo'),
types.string(', '),
str])],
types.list([str]));
});
runTest('remove',
function() {
testPrim('remove', id, [3, types.list([1, 2, 3, 4, 5])], types.list([1, 2, 4, 5]));
testPrim('remove', id, [1, types.list([1, 2, 1, 2])], types.list([2, 1, 2]));
testPrim('remove', id, [10, types.list([1, 2, 3, 4])], types.list([1,2,3,4]));
testPrimF('remove', id, [types.string('a'), types.list([types.string('b'),
types.string('a'),
types.string('c'),
types.string('a')])],
['b', 'c', 'a'], listToStringArray);
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('remove'),
[makeConstant(types.string('a')),
makeConstant(types.list([types.string('b'),
types.string('a'),
types.string('c'),
types.string('a')]))]));
var res = run(state);
assert.deepEqual(res.first().toString(), 'b');
assert.deepEqual(res.rest().first().toString(), 'c');
assert.deepEqual(res.rest().rest().first().toString(), 'a');
assert.deepEqual(res.rest().rest().rest(), types.EMPTY);
});
runTest('map',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('map'),
[makePrimval('add1'),
makeConstant(types.list([1, 2, 3]))]));
assert.deepEqual(run(state), types.list([2, 3, 4]));
});
runTest('filter',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('filter'),
[makePrimval('even?'),
makeConstant(types.list([1, 2, 3, 4, 5, 6]))]));
assert.deepEqual(run(state), types.list([2, 4, 6]));
state.pushControl(makeApplication(makePrimval('filter'),
[makeLam(1, [], makeConstant(false)),
makeConstant(types.list([1, 2, 3, 4]))]));
assert.deepEqual(run(state), types.EMPTY);
state.pushControl(makeApplication(makePrimval('filter'),
[makeLam(1, [], makeConstant(true)),
makeConstant(types.list([1, 2, 3, 4]))]));
assert.deepEqual(run(state), types.list([1, 2, 3, 4]));
});
runTest('foldl',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('foldl'),
[makePrimval('-'),
makeConstant(2),
makeConstant(types.list([1, 2, 3, 4]))]));
assert.deepEqual(run(state), 4);
state.pushControl(makeApplication(makePrimval('foldl'),
[makePrimval('cons'),
makeConstant(types.list([1, 2])),
makeConstant(types.list([3, 4, 5, 6]))]));
assert.deepEqual(run(state), types.list([6, 5, 4, 3, 1, 2]));
});
runTest('foldr',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('foldr'),
[makePrimval('-'),
makeConstant(2),
makeConstant(types.list([1, 2, 3, 4]))]));
assert.deepEqual(run(state), 0);
state.pushControl(makeApplication(makePrimval('foldr'),
[makePrimval('cons'),
makeConstant(types.list([1, 2])),
makeConstant(types.list([3, 4, 5, 6]))]));
assert.deepEqual(run(state), types.list([3, 4, 5, 6, 1, 2]));
});
runTest('build-list',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('build-list'),
[makeConstant(5), makePrimval('add1')]));
assert.deepEqual(run(state), types.list([1, 2, 3, 4, 5]));
state.pushControl(makeApplication(makePrimval('build-list'),
[makeConstant(5), makePrimval('number->string')]));
assert.deepEqual(run(state), types.list([types.string('0'),
types.string('1'),
types.string('2'),
types.string('3'),
types.string('4')]));
});
runTest('argmax',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('argmax'),
[makePrimval('car'),
makeConstant(types.list([types.pair(1, 2),
types.list([1, 2, 3]),
types.pair(3, 5),
types.pair(2, 13)]))]));
assert.deepEqual(run(state), types.pair(3, 5));
state.pushControl(makeApplication(makePrimval('argmax'),
[makePrimval('-'),
makeConstant(types.list([1, 3, 5, 2, 4]))]));
assert.deepEqual(run(state), 1);
});
runTest('argmin',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('argmin'),
[makePrimval('car'),
makeConstant(types.list([types.pair(1, 2),
types.list([1, 2, 3]),
types.pair(3, 5),
types.pair(2, 13)]))]));
assert.deepEqual(run(state), types.pair(1, 2));
state.pushControl(makeApplication(makePrimval('argmin'),
[makePrimval('-'),
makeConstant(types.list([1, 3, 5, 2, 4]))]));
assert.deepEqual(run(state), 5);
});
runTest('quicksort',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('quicksort'),
[makeConstant(types.list([4, 3, 6, 8, 2, 9])),
makePrimval('<')]));
var result = run(state);
assert.ok(types.isEqual(result, types.list([2, 3, 4, 6, 8, 9])));
state.pushControl(makeApplication(makePrimval('quicksort'),
[makeConstant(types.list([types.char('k'),
types.char('o'),
types.char('c'),
types.char('g')])),
makePrimval('char>?')]));
assert.ok(types.isEqual(run(state), types.list([types.char('o'),
types.char('k'),
types.char('g'),
types.char('c')])));
});
runTest('compose',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makeApplication(makePrimval('compose'),
[makePrimval('magnitude'),
makePrimval('+'),
makePrimval('values')]),
[makeConstant(2),
makeConstant(3),
makeConstant(2),
makeConstant(types.complex(-4, 4))]));
assert.deepEqual(run(state), types.rational(5));
var composed = makeApplication(makePrimval('compose'),
[makePrimval('even?'),
makePrimval('*'),
makePrimval('values')]);
state.pushControl(makeApplication(composed, [makeConstant(3), makeConstant(5)]));
assert.deepEqual(run(state), false);
state.pushControl(makeApplication(composed, [makeConstant(2), makeConstant(4), makeConstant(15)]));
assert.deepEqual(run(state), true);
});
runTest('caar, cadr, cdar, cddr, etc.',
function() {
var deepArrayToList = function(a) {
if ( !(a instanceof Array) ) {
return a;
}
return types.list( helpers.map(deepArrayToList, a) );
}
testPrim('car', types.list, [[1, 2, 3]], 1);
testPrim('caar', deepArrayToList, [[[1, 2], [3, 4], []]], 1);
testPrim('caar', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], types.list([1, 2]));
testPrim('caar', types.list, [[types.pair(1, types.pair(2, 3))]], 1);
testPrim('cadr', types.list, [[1, 2, 3]], 2);
testPrim('cadr', deepArrayToList, [[[1, 2], [3, 4]]], types.list([3, 4]));
testPrim('cdar', deepArrayToList, [[[1, 2], [3, 4], []]], types.list([2]));
testPrim('cdar', types.list, [[types.pair(1, 2)]], 2);
testPrim('cddr', types.list, [[1, 2, 3, 4]], types.list([3, 4]));
testPrim('cddr', deepArrayToList, [[[], [1], [1, 2], [1, 2, 3]]], deepArrayToList([[1, 2], [1, 2, 3]]));
testPrim('cddr', id, [types.pair(1, types.pair(2, 3))], 3);
testPrim('caaar', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], 1);
testPrim('caaar', deepArrayToList, [[[types.pair(0, 1)]]], 0);
testPrim('caadr', deepArrayToList, [[[1, 2], [3, 4], []]], 3);
testPrim('caadr', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], types.list([5, 6]));
testPrim('cadar', deepArrayToList, [[[1, 2], [3, 4], []]], 2);
testPrim('cadar', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], types.list([3, 4]));
testPrim('cdaar', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], types.list([2]));
testPrim('cdaar', deepArrayToList, [[[types.pair(0, 1)]]], 1);
testPrim('cdadr', deepArrayToList, [[[1, 2], [3, 4], []]], types.list([4]));
testPrim('cdadr', deepArrayToList, [[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]], deepArrayToList([[7, 8]]));
testPrim('cdadr', deepArrayToList, [[types.pair(1, 2), types.pair(3, 4)]], 4);
testPrim('cddar', deepArrayToList, [[[1, 2], [3, 4], []]], types.EMPTY);
testPrim('cddar', deepArrayToList, [[types.pair(1, types.pair(2, 3))]], 3);
testPrim('caddr', types.list, [[1, 2, 3, 4]], 3);
testPrim('caddr', deepArrayToList, [[[1, 2], [3, 4], []]], types.EMPTY);
testPrim('cdddr', types.list, [[1, 2, 3, 4]], types.list([4]));
testPrim('cdddr', id, [types.pair(1, types.pair(2, types.pair(3, 4)))], 4);
testPrim('cadddr', types.list, [[1, 2, 3, 4]], 4);
testPrim('cadddr', deepArrayToList, [[[1, 2], [3, 4], [5, 6], [7, 8]]], types.list([7, 8]));
});
/***************************
*** Box Primitive Tests ***
***************************/
runTest('box',
function() {
testPrim('box', id, [1], types.box(1));
testPrim('box', types.string, ['abc'], types.box(types.string('abc')));
});
runTest('box?',
function() {
testPrim('box?', types.box, [1], true);
testPrim('box?', types.char, ['a'], false);
testPrim('box?', id, [15], false);
});
runTest('unbox',
function() {
testPrim('unbox', types.box, [2], 2);
testPrim('unbox', types.box, [types.char('a')], types.char('a'));
});
runTest('set-box!',
function() {
var testBox1 = types.box(1);
var testBox2 = types.box(types.string('hello'));
testPrim('set-box!', id, [testBox1, 15], types.VOID);
testPrim('set-box!', id, [testBox2, types.string('world')], types.VOID);
assert.deepEqual(testBox1, types.box(15));
assert.deepEqual(testBox2, types.box(types.string('world')));
});
/****************************
*** Hash Primitive Tests ***
****************************/
runTest('hash?',
function() {
testPrim('hash?', id, [1], false);
testPrim('hash?', types.vector, [[1, 2, 3]], false);
testPrim('hash?', types.hash, [types.EMPTY], true);
testPrim('hash?', types.hashEq, [types.EMPTY], true);
testPrim('hash?', types.hash, [types.list([types.pair(1, 2)])], true);
testPrim('hash?', types.hashEq, [types.list([types.pair(1, 2)])], true);
});
runTest('str',
function() {
assert.equal(typeof(types.string('a')), 'object');
});
runTest('make-hash',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('make-hash'), []));
var res = run(state);
assert.ok(types.isHash(res));
assert.ok(res.hash.isEmpty());
state.pushControl(makeApplication(makePrimval('make-hash'),
[makeConstant(types.list([types.pair(1, 2),
types.pair(3, 4),
types.pair(5, 6)]))]));
var res2 = run(state);
assert.ok(types.isHash(res2));
assert.ok( !res2.hash.isEmpty() );
assert.ok(res2.hash.containsKey(1));
assert.ok(res2.hash.containsKey(3));
assert.ok(res2.hash.containsKey(5));
assert.deepEqual(res2.hash.get(1), 2);
assert.deepEqual(res2.hash.get(3), 4);
assert.deepEqual(res2.hash.get(5), 6);
state.pushControl(makeApplication(makePrimval('make-hash'),
[makeConstant(types.list(
[types.pair(types.string('a'),
2)]))]));
var res3 = run(state);
assert.deepEqual(res3.hash.get(types.string('a')), 2);
});
runTest('make-hasheq',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('make-hasheq'), []));
var res = run(state);
assert.ok(types.isHash(res));
assert.ok(res.hash.isEmpty());
state.pushControl(makeApplication(makePrimval('make-hasheq'),
[makeConstant(types.list([types.pair(1, 2),
types.pair(3, 4),
types.pair(5, 6)]))]));
var res2 = run(state);
assert.ok(types.isHash(res2));
assert.ok( !res2.hash.isEmpty() );
assert.ok(res2.hash.containsKey(1));
assert.ok(res2.hash.containsKey(3));
assert.ok(res2.hash.containsKey(5));
assert.deepEqual(res2.hash.get(1), 2);
assert.deepEqual(res2.hash.get(3), 4);
assert.deepEqual(res2.hash.get(5), 6);
var str1 = types.string('a');
var str2 = types.string('a');
state.pushControl(makeApplication(makePrimval('make-hasheq'),
[makeConstant(types.list(
[types.pair(str1, 1),
types.pair(str2, 2)]))]));
var res3 = run(state);
assert.ok( !res3.hash.containsKey(types.string('a')) );
assert.deepEqual(res3.hash.get(str1), 1);
assert.deepEqual(res3.hash.get(str2), 2);
});
runTest('hash-set!',
function() {
var testHash = types.hash(types.list([types.pair(1, 1), types.pair(2, 3)]));
// sys.print('\ntestHash = ' + sys.inspect(testHash) + "\n");
// sys.print('testHash.hash = ' + sys.inspect(testHash.hash) + '\n');
assert.deepEqual(testHash.hash.get(1), 1);
assert.deepEqual(testHash.hash.containsKey(5), false);
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('hash-set!'),
[makeConstant(testHash), makeConstant(5), makeConstant(8)]));
var result = run(state);
assert.deepEqual(result, types.VOID);
assert.deepEqual(testHash.hash.get(5), 8);
state.pushControl(makeApplication(makePrimval('hash-set!'),
[makeConstant(testHash), makeConstant(1), makeConstant(0)]));
assert.deepEqual(run(state), types.VOID);
assert.deepEqual(testHash.hash.get(1), 0);
});
runTest('hash-ref',
function() {
var hash1 = types.hash(types.list([types.pair(1, 2),
types.pair(types.string('hello'),
types.string('world')),
types.pair(types.string('hello'),
types.string('world2'))]));
testPrim('hash-ref', id, [hash1, types.string('hello')], types.string('world2'));
testPrim('hash-ref', id, [hash1, 1, false], 2);
testPrim('hash-ref', id, [hash1, 2, false], false);
var str1 = types.string('hello');
var str2 = str1.copy();
var hash2 = types.hashEq(types.list([types.pair(str1, types.string('world')),
types.pair(str2, types.string('world2')),
types.pair(1, 2),
types.pair(3, 4)]));
testPrim('hash-ref', id, [hash2, types.string('hello'), false], false);
testPrim('hash-ref', id, [hash2, str1], types.string('world'));
testPrim('hash-ref', id, [hash2, types.string('a'), 2], 2);
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('hash-ref'),
[makeConstant(hash1),
makeConstant(2),
makeLam(0, [], makeConstant(15))]));
assert.deepEqual(run(state), 15);
state.pushControl(makeApplication(makePrimval('hash-ref'),
[makeConstant(hash2),
makeConstant(types.string('hello')),
makeLam(0, [], makeConstant(true))]));
assert.deepEqual(run(state), true);
});
runTest('hash-remove!',
function() {
var hash1 = types.hash(types.list([types.pair(1, 2),
types.pair(2, 3),
types.pair(3, 4),
types.pair(4, 5)]));
assert.ok(hash1.hash.containsKey(1));
testPrim('hash-remove!', id, [hash1, 1], types.VOID);
assert.ok( !hash1.hash.containsKey(1) );
var str1 = types.string('a');
var str2 = types.string('b');
var hash2 = types.hashEq(types.list([types.pair(str1, 5),
types.pair(str2, 3)]));
testPrim('hash-remove!', id, [hash2, types.string('a')], types.VOID);
assert.ok(hash2.hash.containsKey(str1));
testPrim('hash-remove!', id, [hash2, str2], types.VOID);
assert.ok( !hash2.hash.containsKey(str2) );
});
runTest('hash-map',
function() {
var str1 = types.string('hello');
var str2 = str1.copy();
var str3 = str1.copy();
var hash1 = types.hash(types.list([types.pair(str1, types.string('a')),
types.pair(str2, types.string('b')),
types.pair(str3, types.string('c'))]));
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('hash-map'),
[makeConstant(hash1), makePrimval('string-append')]));
assert.ok( hash1.hash.containsKey(types.string('hello')) );
assert.deepEqual(run(state), types.list([types.string('helloc')]));
var hash2 = types.hashEq(types.list([types.pair(str1, types.string('a')),
types.pair(str2, types.string('b')),
types.pair(str3, types.string('c'))]));
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('hash-map'),
[makeConstant(hash2), makePrimval('string-append')]));
assert.deepEqual(run(state), types.list([types.string('helloc'),
types.string('hellob'),
types.string('helloa')]));
});
runTest('hash-for-each',
function() {
var hash1 = types.hash(types.list([types.pair(1, 2),
types.pair(2, 3),
types.pair(3, 4),
types.pair(4, 5)]));
var state = new StateModule.State();
var ret = [];
state.pushControl(makeApplication(makePrimval('hash-for-each'),
[makeConstant(hash1),
makeConstant(new types.PrimProc('', 2, false, false,
function(key, val) {
ret.push( helpers.format('~s - ~s!~n', [key, val]) );
}))]));
assert.deepEqual(run(state), types.VOID);
assert.deepEqual(ret, ['1 - 2!\n', '2 - 3!\n', '3 - 4!\n', '4 - 5!\n']);
});
/******************************
*** Vector Primitive Tests ***
******************************/
runTest('vector?',
function() {
testPrim('vector?', id, [1], false);
testPrim('vector?', types.list, [[1, 2, 3]], false);
testPrim('vector?', types.vector, [[1, 2, 3]], true);
});
runTest('make-vector',
function() {
testPrim('make-vector', id, [0, types.char('a')], types.vector([]));
testPrim('make-vector', id, [3, 5], types.vector([5, 5, 5]));
});
runTest('vector',
function() {
testPrim('vector', id, [1, 2, 3, 4], types.vector([1, 2, 3, 4]));
testPrim('vector', id, [], types.vector([]));
});
runTest('vector-length',
function() {
testPrim('vector-length', types.vector, [[]], 0);
testPrim('vector-length', types.vector, [[1, 2, 3]], 3);
});
runTest('vector-ref',
function() {
testPrim('vector-ref', id, [types.vector([1, 2]), 1], 2);
testPrim('vector-ref', id, [types.vector([3, 2, 1]), 0], 3);
});
runTest('vector-set!',
function() {
testPrim('vector-set!', id, [types.vector([1, 2, 3]), 0, types.char('a')], types.VOID);
var testVec = types.vector([1, 2, 3, 4]);
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('vector-set!'),
[makeConstant(testVec),
makeConstant(2),
makeConstant(5)]));
var result = run(state);
assert.deepEqual(result, types.VOID);
assert.deepEqual(testVec, types.vector([1, 2, 5, 4]));
var testVec2 = types.vector([types.char('a'),
types.char('b'),
types.char('c')]);
state.pushControl(makeApplication(makePrimval('vector-set!'),
[makeConstant(testVec2),
makeConstant(1),
makeConstant(types.char('B'))]));
run(state);
assert.deepEqual(testVec2, types.vector([types.char('a'),
types.char('B'),
types.char('c')]));
});
runTest('vector->list',
function() {
testPrim('vector->list', types.vector, [[]], types.EMPTY);
testPrim('vector->list', types.vector, [[1, 2, 3]], types.list([1, 2, 3]));
});
/****************************
*** Char Primitive Tests ***
****************************/
runTest('char?',
function() {
testPrim('char?', id, [types.symbol('hello!')], false);
testPrim('char?', types.string, ['string'], false);
testPrim('char?', types.char, ['w'], true);
});
runTest('char=?',
function() {
testPrim('char=?', types.char, ['a', 's', 'D'], false);
testPrim('char=?', types.char, ['f', 'F'], false);
testPrim('char=?', types.char, ['a', 'a', 'a'], true);
testPrim('char=?', types.char, ['1', '1', '2'], false);
});
runTest('char-ci=?',
function() {
testPrim('char-ci=?', types.char, ['a', 's', 'D'], false);
testPrim('char-ci=?', types.char, ['f', 'F'], true);
testPrim('char-ci=?', types.char, ['a', 'a', 'a'], true);
testPrim('char-ci=?', types.char, ['1', '1', '2'], false);
});
runTest('char<?',
function() {
testPrim('char<?', types.char, ['A', 'a'], true);
testPrim('char<?', types.char, ['a', 'b'], true);
testPrim('char<?', types.char, ['b', 'a'], false);
testPrim('char<?', types.char, ['a', 'd', 'c'], false);
testPrim('char<?', types.char, ['a', 'b', 'b', 'd'], false);
testPrim('char<?', types.char, ['a', 'b', 'c', 'd', 'e'], true);
});
runTest('char>?',
function() {
testPrim('char>?', types.char, ['A', 'a'], false);
testPrim('char>?', types.char, ['a', 'b'], false);
testPrim('char>?', types.char, ['b', 'a'], true);
testPrim('char>?', types.char, ['f', 'd', 'e'], false);
testPrim('char>?', types.char, ['e', 'd', 'c', 'c', 'a'], false);
testPrim('char>?', types.char, ['e', 'd', 'c', 'b', 'a'], true);
});
runTest('char<=?',
function() {
testPrim('char<=?', types.char, ['A', 'a'], true);
testPrim('char<=?', types.char, ['a', 'b'], true);
testPrim('char<=?', types.char, ['b', 'a'], false);
testPrim('char<=?', types.char, ['a', 'd', 'c'], false);
testPrim('char<=?', types.char, ['a', 'b', 'b', 'd'], true);
testPrim('char<=?', types.char, ['a', 'b', 'c', 'd', 'e'], true);
});
runTest('char>=?',
function() {
testPrim('char>=?', types.char, ['A', 'a'], false);
testPrim('char>=?', types.char, ['a', 'b'], false);
testPrim('char>=?', types.char, ['b', 'a'], true);
testPrim('char>=?', types.char, ['f', 'd', 'e'], false);
testPrim('char>=?', types.char, ['e', 'd', 'c', 'c', 'a'], true);
testPrim('char>=?', types.char, ['e', 'd', 'c', 'b', 'a'], true);
});
runTest('char-ci<?',
function() {
testPrim('char-ci<?', types.char, ['A', 'a'], false);
testPrim('char-ci<?', types.char, ['a', 'b'], true);
testPrim('char-ci<?', types.char, ['b', 'A'], false);
testPrim('char-ci<?', types.char, ['a', 'd', 'c'], false);
testPrim('char-ci<?', types.char, ['a', 'b', 'b', 'd'], false);
testPrim('char-ci<?', types.char, ['a', 'B', 'c', 'd', 'e'], true);
});
runTest('char-ci>?',
function() {
testPrim('char-ci>?', types.char, ['a', 'A'], false);
testPrim('char-ci>?', types.char, ['a', 'b'], false);
testPrim('char-ci>?', types.char, ['b', 'A'], true);
testPrim('char-ci>?', types.char, ['f', 'd', 'e'], false);
testPrim('char-ci>?', types.char, ['e', 'd', 'c', 'c', 'a'], false);
testPrim('char-ci>?', types.char, ['e', 'd', 'C', 'b', 'a'], true);
});
runTest('char-ci<=?',
function() {
testPrim('char-ci<=?', types.char, ['a', 'A'], true);
testPrim('char-ci<=?', types.char, ['a', 'B'], true);
testPrim('char-ci<=?', types.char, ['b', 'a'], false);
testPrim('char-ci<=?', types.char, ['a', 'd', 'c'], false);
testPrim('char-ci<=?', types.char, ['a', 'b', 'B', 'd'], true);
testPrim('char-ci<=?', types.char, ['a', 'b', 'C', 'd', 'e'], true);
});
runTest('char-ci>=?',
function() {
testPrim('char-ci>=?', types.char, ['A', 'a'], true);
testPrim('char-ci>=?', types.char, ['a', 'b'], false);
testPrim('char-ci>=?', types.char, ['B', 'a'], true);
testPrim('char-ci>=?', types.char, ['f', 'd', 'e'], false);
testPrim('char-ci>=?', types.char, ['e', 'd', 'C', 'c', 'a'], true);
testPrim('char-ci>=?', types.char, ['e', 'd', 'c', 'B', 'a'], true);
});
runTest('char-alphabetic?',
function() {
testPrim('char-alphabetic?', types.char, ['a'], true);
testPrim('char-alphabetic?', types.char, ['Z'], true);
testPrim('char-alphabetic?', types.char, ['3'], false);
testPrim('char-alphabetic?', types.char, [' '], false);
testPrim('char-alphabetic?', types.char, ['!'], false);
testPrim('char-alphabetic?', types.char, ['\n'], false);
});
runTest('char-numeric?',
function() {
testPrim('char-numeric?', types.char, ['a'], false);
testPrim('char-numeric?', types.char, ['Z'], false);
testPrim('char-numeric?', types.char, ['3'], true);
testPrim('char-numeric?', types.char, [' '], false);
testPrim('char-numeric?', types.char, ['!'], false);
testPrim('char-numeric?', types.char, ['\n'], false);
});
runTest('char-whitespace?',
function() {
testPrim('char-whitespace?', types.char, ['a'], false);
testPrim('char-whitespace?', types.char, ['Z'], false);
testPrim('char-whitespace?', types.char, ['3'], false);
testPrim('char-whitespace?', types.char, [' '], true);
testPrim('char-whitespace?', types.char, ['!'], false);
testPrim('char-whitespace?', types.char, ['\n'], true);
testPrim('char-whitespace?', types.char, ['\t'], true);
});
runTest('char-upper-case?',
function() {
testPrim('char-upper-case?', types.char, ['a'], false);
testPrim('char-upper-case?', types.char, ['Z'], true);
testPrim('char-upper-case?', types.char, ['3'], false);
testPrim('char-upper-case?', types.char, [' '], false);
testPrim('char-upper-case?', types.char, ['!'], false);
testPrim('char-upper-case?', types.char, ['\n'], false);
});
runTest('char-lower-case?',
function() {
testPrim('char-lower-case?', types.char, ['a'], true);
testPrim('char-lower-case?', types.char, ['Z'], false);
testPrim('char-lower-case?', types.char, ['3'], false);
testPrim('char-lower-case?', types.char, [' '], false);
testPrim('char-lower-case?', types.char, ['!'], false);
testPrim('char-lower-case?', types.char, ['\n'], false);
});
runTest('char->integer',
function() {
testPrim('char->integer', types.char, ['0'], 48);
testPrim('char->integer', types.char, ['\n'], 10);
});
runTest('integer->char',
function() {
testPrim('integer->char', id, [48], types.char('0'));
testPrim('integer->char', id, [65], types.char('A'));
});
runTest('char-upcase',
function() {
testPrim('char-upcase', types.char, ['a'], types.char('A'));
testPrim('char-upcase', types.char, ['B'], types.char('B'));
testPrim('char-upcase', types.char, ['2'], types.char('2'));
testPrim('char-upcase', types.char, ['~'], types.char('~'));
});
runTest('char-downcase',
function() {
testPrim('char-downcase', types.char, ['a'], types.char('a'));
testPrim('char-downcase', types.char, ['B'], types.char('b'));
testPrim('char-downcase', types.char, ['2'], types.char('2'));
testPrim('char-downcase', types.char, ['~'], types.char('~'));
});
runTest('char print formatting',
function() {
testPrim('format', id, ['~s', types.char('\n')], types.string('#\\newline'));
testPrim('format', id, ['~s', types.char('\0')], types.string('#\\nul'));
testPrim('format', id, ['~a', types.char('b')], types.string('b'));
testPrim('format', id, ['~s', types.char('b')], types.string('#\\b'));
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('format'),
[makeConstant('~s'),
makeApplication(makePrimval('integer->char'),
[makeConstant(24)])]));
assert.deepEqual(run(state), types.string('#\\u0018'));
state.pushControl(makeApplication(makePrimval('format'),
[makeConstant('~s'),
makeApplication(makePrimval('integer->char'),
[makeConstant(127)])]));
assert.deepEqual(run(state), types.string('#\\rubout'));
state.pushControl(makeApplication(makePrimval('format'),
[makeConstant('~s'),
makeApplication(makePrimval('integer->char'),
[makeConstant(955)])]));
assert.deepEqual(run(state), types.string('#\\u03BB'));
});
///////////////////////////////////////////////////////////////////////
runTest('values',
function() {
testPrim('values', id, [], new types.ValuesWrapper([]));
testPrim('values', id, [1, 2, 3, 4], new types.ValuesWrapper([1, 2, 3, 4]));
testPrim('values', id, [1], 1);
});
runTest('call-with-values',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('call-with-values'),
[makePrimval('values'),
makePrimval('+')]));
assert.deepEqual(run(state), 0);
state.pushControl(makeApplication(makePrimval('call-with-values'),
[makeLam(0, [], makeConstant(1)),
makePrimval('+')]));
assert.deepEqual(run(state), 1);
state.pushControl(makeApplication(makePrimval('call-with-values'),
[makeLam(0, [], makeApplication(makePrimval('values'),
[makeConstant(1),
makeConstant(2),
makeConstant(3)])),
makePrimval('+')]));
assert.deepEqual(run(state), 6);
});
runTest('not',
function() {
testPrim('not', id, [false], true);
testPrim('not', id, [0], false);
testPrim('not', id, [1], false);
testPrim('not', types.char, ['0'], false);
});
runTest('boolean?',
function() {
testPrim('boolean?', id, [false], true);
testPrim('boolean?', id, [true], true);
testPrim('boolean?', types.string, ['false'], false);
testPrim('boolean?', id, [0], false);
testPrim('boolean?', id, [1], false);
});
runTest('eq?',
function() {
var testStr = types.string('hello');
var testChar = types.char('H');
testPrim('eq?', id, [1, 1], true);
testPrim('eq?', id, [1, 2], false);
testPrim('eq?', id, [types.rational(1, 3), types.rational(1, 3)], false);
testPrim('eq?', types.symbol, ['a', 'a'], true);
testPrim('eq?', types.string, ['a', 'a'], false);
testPrim('eq?', id, [testStr, testStr], true);
testPrim('eq?', id, [testChar, testChar], true);
testPrim('eq?', id, [testChar, types.char('H')], true);
});
runTest('eqv?',
function() {
var testStr = types.string('hello');
var testChar = types.char('H');
testPrim('eqv?', id, [1, 1], true);
testPrim('eqv?', id, [1, 2], false);
testPrim('eqv?', id, [types.rational(1, 3), types.rational(1, 3)], true);
testPrim('eqv?', types.symbol, ['a', 'a'], true);
testPrim('eqv?', types.string, ['a', 'a'], false);
testPrim('eqv?', id, [testStr, testStr], true);
testPrim('eqv?', id, [testChar, testChar], true);
testPrim('eqv?', id, [testChar, types.char('H')], true);
});
runTest('equal?',
function() {
var testStr = types.string('hello');
var testChar = types.char('H');
testPrim('equal?', id, [1, 1], true);
testPrim('equal?', id, [1, 2], false);
testPrim('equal?', id, [types.rational(1, 3), types.rational(1, 3)], true);
testPrim('equal?', types.symbol, ['a', 'a'], true);
testPrim('equal?', types.string, ['a', 'a'], true);
testPrim('equal?', id, [testStr, testStr], true);
testPrim('equal?', id, [testChar, testChar], true);
testPrim('equal?', id, [testChar, types.char('H')], true);
});
runTest('equal~?',
function() {
testPrim('equal~?', id, [types.string('h'), types.string('h'), 5], true);
testPrim('equal~?', id, [5, 4, 0], false);
testPrim('equal~?', id, [types.char('a'), types.char('b'), 3], false);
testPrim('equal~?', id, [5, 3, 3], true);
testPrim('equal~?', types.float, [5.4, 4.9, 0.5], true);
});
runTest('struct?',
function() {
testPrim('struct?', types.string, ['a'], false);
testPrim('struct?', id, [1], false);
testPrim('struct?', id, [types.EMPTY], false);
testPrim('struct?', types.box, [2], false);
var PosnType = types.makeStructureType(
'posn', false, 2, 0, false, false);
testPrim('struct?', id, [PosnType.constructor(2, 4)], true);
});
runTest('procedure-arity',
function() {
var state = new StateModule.State();
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makePrimval('+')]));
assert.deepEqual(run(state), types.arityAtLeast(0));
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makePrimval('-')]));
assert.deepEqual(run(state), types.arityAtLeast(1));
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makePrimval('equal?')]));
assert.deepEqual(run(state), 2);
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makePrimval('random')]));
assert.deepEqual(run(state), types.list([0, 1]));
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makePrimval('hash-ref')]));
assert.deepEqual(run(state), types.list([2, 3]));
var testProc = new types.CaseLambdaValue('',
[new types.PrimProc('', 1, false, false, function() {}),
new types.PrimProc('', 2, true, false, function() {})]);
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makeConstant(testProc)]));
assert.deepEqual(run(state), types.list([1, types.arityAtLeast(2)]));
var testProc2 = new types.CaseLambdaValue('',
[new types.PrimProc('', 1, false, false, function() {}),
new types.PrimProc('', 0, true, false, function() {})]);
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makeConstant(testProc2)]));
assert.deepEqual(run(state), types.arityAtLeast(0));
var testProc3 = new types.CaseLambdaValue('',
[new types.PrimProc('', 1, false, false, function() {}),
new types.PrimProc('', 4, true, false, function() {}),
new types.PrimProc('', 0, false, false, function() {}),
new types.PrimProc('', 3, true, false, function() {}),
new types.PrimProc('', 3, false, false, function() {})]);
state.pushControl(makeApplication(makePrimval('procedure-arity'), [makeConstant(testProc3)]));
assert.deepEqual(run(state), types.list([0, 1, types.arityAtLeast(3)]));
});
runTest('identity',
function() {
testPrim('identity', id, [5], 5);
testPrim('identity', types.string, ['hello'], types.string('hello'));
});
// runTest('make-posn',
// function() {
// testPrim('make-posn', id, [4, 5], types.posn(4, 5));
// testPrim('make-posn', types.char, ['a', 'B'], types.posn(types.char('a'), types.char('B')));
// });
// runTest('posn?',
// function() {
// testPrim('posn?', id, [4], false);
// testPrim('posn?', types.box, [4], false);
// testPrim('posn?', id, [types.posn(5, 4)], true);
// });
// runTest('posn-x',
// function() {
// testPrim('posn-x', id, [types.posn(5, 4)], 5);
// testPrim('posn-x', id, [types.posn(types.char('a'), types.char('b'))], types.char('a'));
// });
// runTest('posn-y',
// function() {
// testPrim('posn-y', id, [types.posn(5, 4)], 4);
// testPrim('posn-y', id, [types.posn(types.char('a'), types.char('b'))], types.char('b'));
// });
runTest('structure equality',
function() {
var ParentType = types.makeStructureType('parent', false, 2, 0, false, false);
var makeParent = ParentType.constructor;
var ChildType = types.makeStructureType('child', ParentType, 0, 0, false, false);
var makeChild = ChildType.constructor;
testPrim('equal?', id, [makeParent('a', 5), makeParent('a', 5)], true);
testPrim('equal?', id, [makeParent('a', 5), makeParent('b', 5)], false);
testPrim('equal?', id, [makeParent('a', 5), makeChild('a', 5)], false);
testPrim('equal?', id, [makeChild('a', 5), makeParent('a', 5)], false);
testPrim('equal?', id, [makeParent('a', 5), types.color(4, 3, 6)], false);
});
/***************************
*** FFI Primitive Tests ***
***************************/
/*
runTest('get-js-object',
function() {
testPrim('get-js-object', id, ['setInterval'], types.jsObject('setInterval', setInterval));
testPrim('get-js-object', id, [types.jsObject('types', types), 'box'],
types.jsObject('types.box', types.box));
testPrim('get-js-object', types.string, ['types', 'cons'], types.jsObject('types.cons', types.cons));
testPrim('get-js-object', id, ['world', types.string('Kernel'), 'ellipseImage'],
types.jsObject('world.Kernel.ellipseImage', world.Kernel.ellipseImage));
testPrim('get-js-object', id, [types.jsObject('world', world), 'Kernel', 'isColor'],
types.jsObject('world.Kernel.isColor', world.Kernel.isColor));
testPrim('get-js-object', id, [types.jsObject('world.config', world.config), 'Kernel', 'getNoneEffect'],
types.jsObject('world.config.Kernel.getNoneEffect', world.config.Kernel.getNoneEffect));
testPrim('get-js-object', id, ['junk'], types.jsObject('junk', undefined));
try {
testPrim('get-js-object', id, ['world', 'junk', 'something'], false);
} catch(e) {
assert.deepEqual(e, types.schemeError(
types.exnFailContract('get-js-object: tried to access field something of world.junk, '
+ 'but world.junk was undefined'),
false));
}
});
runTest('js-call',
function() {
testPrim('js-call', id, [types.jsObject('jsnums.greaterThan', jsnums.greaterThan), 4, types.rational(3, 2)], true);
testPrim('js-call', id, [types.jsObject('types.hash', types.hash), types.EMPTY], types.hash(types.EMPTY));
var state = new StateModule.State();
var results = [];
state.pushControl(makeApplication(makePrimval('js-call'),
[makeConstant(types.jsObject('setInterval', setInterval)),
makeConstant(function() { results.push('tick'); }),
makeConstant(500)]));
var watchId = run(state);
setTimeout(function() {
clearInterval(watchId);
assert.deepEqual(results, ['tick', 'tick', 'tick', 'tick', 'tick']);
}, 2600);
});
*/
runTest("topsyntax",
function() {
sys.print("!Not implemented yet! ");
});
runTest("Error structure hierarchy",
function() {
assert.ok(types.isExnFail(types.exnFail("hello", types.continuationMarkSet())));
assert.ok(types.isExnFail(types.exnFailContract("hello", types.continuationMarkSet())));
assert.ok(types.isExnFail(types.exnFailContractDivisionByZero("hello", types.continuationMarkSet())));
});
/**
This next test is special and should be last. It'll run an infinite loop, and
schedule a break.
Only after the interpreter breaks do we print "END TESTS".
*/
runTest("closure application, testing break",
// (define (f) (f)) (begin (f)) --> infinite loop, but with bounded control stack.
function() {
var state = new StateModule.State();
state.pushControl(makeMod(makePrefix(1), []));
run(state);
state.pushControl(makeApplication(makeToplevel(0, 0), []));
state.pushControl(makeDefValues([makeToplevel(0, 0)],
makeLam(0, [0],
makeApplication(makeToplevel(0, 0),
[]))));
var isTerminated = false;
state.onFail = function(e) {
assert.ok(types.isSchemeError(e));
assert.ok(types.isExnBreak(e.val));
isTerminated = true;
};
interpret.run(state);
var waitTillBreak = function() {
if (isTerminated) {
sys.print("\nEND TESTS\n")
return;
} else {
state.breakRequested = true;
setTimeout(waitTillBreak, 10);
}
};
waitTillBreak();
});