////////////////////////////////////////////////////////////////////// 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"], 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"], 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('# #')); 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'], 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'], 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(); });