From 6c50b88703cd04dae5528af5430752ecc9af62a1 Mon Sep 17 00:00:00 2001 From: Danny Yoo Date: Sat, 14 May 2011 01:58:56 -0400 Subject: [PATCH] absorbing libraries from js-vm project --- NOTES | 9 +- README | 14 +- js-assembler/get-runtime.rkt | 2 +- js-assembler/{runtime.js => mini-runtime.js} | 0 js-assembler/runtime-src/helpers.js | 587 +++ js-assembler/runtime-src/js-numbers.js | 4089 ++++++++++++++ .../runtime-src/jshashtable-2.1_src.js | 370 ++ js-assembler/runtime-src/json2.js | 482 ++ js-assembler/runtime-src/primitives.js | 4678 +++++++++++++++++ js-assembler/runtime-src/types.js | 2532 +++++++++ runtime.compressed.js | 16 - 11 files changed, 12759 insertions(+), 20 deletions(-) rename js-assembler/{runtime.js => mini-runtime.js} (100%) create mode 100644 js-assembler/runtime-src/helpers.js create mode 100644 js-assembler/runtime-src/js-numbers.js create mode 100644 js-assembler/runtime-src/jshashtable-2.1_src.js create mode 100644 js-assembler/runtime-src/json2.js create mode 100644 js-assembler/runtime-src/primitives.js create mode 100644 js-assembler/runtime-src/types.js delete mode 100644 runtime.compressed.js diff --git a/NOTES b/NOTES index bdabd86..100bedc 100644 --- a/NOTES +++ b/NOTES @@ -505,4 +505,11 @@ js-sicp-5-5 is an uninspired name for the project. I'm renaming it to and song because, well, some songs can be called a "Racket". :) ----------------------------------------------------------------------- \ No newline at end of file + +I want to use Google Closure's compiler to minify. Closure doesn't +like some of the code, so I'll need to rename some of the identifiers +in types.js to avoid colliding with Java keywords (char, float). + + +---------------------------------------------------------------------- + diff --git a/README b/README index 93f8da4..c0cca3f 100644 --- a/README +++ b/README @@ -114,7 +114,7 @@ tricks to make things like tail calls work: Otherwise, the assembler is fairly straightforward. It depends on -library functions defined in runtime.js. As soon as the compiler +library functions defined in mini-runtime.js. As soon as the compiler stabilizes, we will be pulling in the runtime library in Moby Scheme into this project. @@ -138,4 +138,14 @@ run this on a system with a web browser, as the suite will evaluate JavaScript and make sure it is producing values. A bridge module browser-evaluate.rkt brings up a temporary web server that allows us to pass values between Racket and the JavaScript evaluator on the -browser. \ No newline at end of file +browser. + + + +====================================================================== + +This uses the following libraries: + + jshashtable (http://www.timdown.co.uk/jshashtable/) + js-numbers (http://github.com/dyoo/js-numbers/) + JSON (http://www.json.org/js.html) \ No newline at end of file diff --git a/js-assembler/get-runtime.rkt b/js-assembler/get-runtime.rkt index e27a544..25180ee 100644 --- a/js-assembler/get-runtime.rkt +++ b/js-assembler/get-runtime.rkt @@ -9,7 +9,7 @@ (provide/contract [get-runtime (-> string?)]) -(define-runtime-path runtime.js "runtime.js") +(define-runtime-path runtime.js "mini-runtime.js") (define text (call-with-input-file runtime.js (lambda (ip) diff --git a/js-assembler/runtime.js b/js-assembler/mini-runtime.js similarity index 100% rename from js-assembler/runtime.js rename to js-assembler/mini-runtime.js diff --git a/js-assembler/runtime-src/helpers.js b/js-assembler/runtime-src/helpers.js new file mode 100644 index 0000000..c793347 --- /dev/null +++ b/js-assembler/runtime-src/helpers.js @@ -0,0 +1,587 @@ + +////////////////////////////////////////////////////////////// + +// File of helper functions for primitives and world. + +var helpers = {}; + +(function() { + + var format = function(formatStr, args, functionName) { + var throwFormatError = function() { + functionName = functionName || '#'; + var matches = formatStr.match(new RegExp('~[sSaA]', 'g')); + var expectedNumberOfArgs = matches == null ? 0 : matches.length; + var errorStrBuffer = [functionName + ': format string requires ' + expectedNumberOfArgs + + ' arguments, given ' + args.length + '; arguments were:', + types.toWrittenString(formatStr)]; + for (var i = 0; i < args.length; i++) { + errorStrBuffer.push( types.toWrittenString(args[i]) ); + } + + raise( types.incompleteExn(types.exnFailContract, errorStrBuffer.join(' '), []) ); + } + + var pattern = new RegExp("~[sSaAneE%~]", "g"); + var buffer = args.slice(0);; + function f(s) { + if (s == "~~") { + return "~"; + } else if (s == '~n' || s == '~%') { + return "\n"; + } else if (s == '~s' || s == "~S") { + if (buffer.length == 0) { + throwFormatError(); + } + return types.toWrittenString(buffer.shift()); + } else if (s == '~e' || s == "~E") { + // FIXME: we don't yet have support for the error-print + // handler, and currently treat ~e just like ~s. + if (buffer.length == 0) { + throwFormatError(); + } + return types.toWrittenString(buffer.shift()); + } else if (s == '~a' || s == "~A") { + if (buffer.length == 0) { + throwFormatError(); + } + return types.toDisplayedString(buffer.shift()); + } else { + throw types.internalError('format: string.replace matched invalid regexp', false); + } + } + var result = formatStr.replace(pattern, f); + if (buffer.length > 0) { + throwFormatError(); + } + return result; + }; + + + // forEachK: CPS( array CPS(array -> void) (error -> void) -> void ) + // Iterates through an array and applies f to each element using CPS + // If an error is thrown, it catches the error and calls f_error on it + var forEachK = function(a, f, f_error, k) { + var forEachHelp = function(i) { + if( i >= a.length ) { + if (k) { + return k(); + } else { + return; + } + } + + try { + return f(a[i], function() { return forEachHelp(i+1); }); + } catch (e) { + f_error(e); + } + }; + return forEachHelp(0); + }; + + + // reportError: (or exception string) -> void + // Reports an error to the user, either at the console + // if the console exists, or as alerts otherwise. + var reportError = function(e) { + var reporter; + if (typeof(console) != 'undefined' && + typeof(console.log) != 'undefined') { + reporter = (function(x) { console.log(x); }); + } else { + reporter = (function(x) { alert(x); }); + } + if (typeof e == 'string') { + reporter(e); + } else if ( types.isSchemeError(e) ) { + if ( types.isExn(e.val) ) { + reporter( types.exnMessage(e.val) ); + } + else { + reporter(e.val); + } + } else if ( types.isInternalError(e) ) { + reporter(e.val); + } else if (e.message) { + reporter(e.message); + } else { + reporter(e.toString()); + } +// if (plt.Kernel.lastLoc) { +// var loc = plt.Kernel.lastLoc; +// if (typeof(loc) === 'string') { +// reporter("Error was raised around " + loc); +// } else if (typeof(loc) !== 'undefined' && +// typeof(loc.line) !== 'undefined') { +// reporter("Error was raised around: " +// + plt.Kernel.locToString(loc)); +// } +// } + }; + + + var raise = function(v) { + throw types.schemeError(v); + }; + + + var procArityContains = function(n) { + return function(proc) { + var singleCase = function(aCase) { + if ( aCase instanceof types.ContinuationClosureValue ) { + return true; + } + return (aCase.numParams == n || + (aCase.isRest && aCase.numParams <= n)); + }; + + var cases = []; + if ( proc instanceof types.ContinuationClosureValue || + proc instanceof types.ClosureValue || + proc instanceof types.PrimProc ) { + return singleCase(proc); + } + else if (proc instanceof types.CasePrimitive) { + cases = proc.cases; + } + else if (proc instanceof types.CaseLambdaValue) { + cases = proc.closures; + } + + for (var i = 0; i < cases.length; i++) { + if ( singleCase(cases[i]) ) + return true; + } + return false; + } + }; + + var throwCheckError = function(details, pos, args) { + var errorFormatStr; + if (args && args.length > 1) { + var errorFormatStrBuffer = ['~a: expects type <~a> as ~a arguments, given: ~s; other arguments were:']; + for (var i = 0; i < args.length; i++) { + if ( i != pos-1 ) { + errorFormatStrBuffer.push( types.toWrittenString(args[i]) ); + } + } + errorFormatStr = errorFormatStrBuffer.join(' '); + } + else { + errorFormatStr = "~a: expects argument of type <~a>, given: ~s"; + details.splice(2, 1); + } + + raise( types.incompleteExn(types.exnFailContract, + helpers.format(errorFormatStr, details), + []) ); + }; + + var check = function(x, f, functionName, typeName, position, args) { + if ( !f(x) ) { + throwCheckError([functionName, + typeName, + helpers.ordinalize(position), + x], + position, + args); + } + }; + + var isList = function(x) { + var seenPairs = makeLowLevelEqHash(); + while (true) { + if (seenPairs.containsKey(x)) { + return true; + } else if (x === types.EMPTY) { + return true; + } else if (types.isPair(x)) { + seenPairs.put(x, true); + x = x.rest(); + } else { + return false; + } + } + }; + + var isListOf = function(x, f) { + var seenPairs = makeLowLevelEqHash(); + while (true) { + if (seenPairs.containsKey(x)) { + return true; + } else if (x === types.EMPTY) { + return true; + } else if (types.isPair(x)) { + seenPairs.put(x, true); + if (f(x.first())) { + x = x.rest(); + } else { + return false; + } + } else { + return false; + } + } + }; + + var checkListOf = function(lst, f, functionName, typeName, position, args) { + if ( !isListOf(lst, f) ) { + helpers.throwCheckError([functionName, + 'list of ' + typeName, + helpers.ordinalize(position), + lst], + position, + args); + } + }; + + +// // remove: array any -> array +// // removes the first instance of v in a +// // or returns a copy of a if v does not exist +// var remove = function(a, v) { +// for (var i = 0; i < a.length; i++) { +// if (a[i] === v) { +// return a.slice(0, i).concat( a.slice(i+1, a.length) ); +// } +// } +// return a.slice(0); +// }; + + // map: array (any -> any) -> array + // applies f to each element of a and returns the result + // as a new array + var map = function(f, a) { + var b = new Array(a.length); + for (var i = 0; i < a.length; i++) { + b[i] = f(a[i]); + } + return b; + }; + + + var concatMap = function(f, a) { + var b = []; + for (var i = 0; i < a.length; i++) { + b = b.concat( f(a[i]) ); + } + return b; + }; + + + var schemeListToArray = function(lst) { + var result = []; + while ( !lst.isEmpty() ) { + result.push(lst.first()); + lst = lst.rest(); + } + return result; + } + + // deepListToArray: any -> any + // Converts list structure to array structure. + var deepListToArray = function(x) { + var thing = x; + if (thing === types.EMPTY) { + return []; + } else if (types.isPair(thing)) { + var result = []; + while (!thing.isEmpty()) { + result.push(deepListToArray(thing.first())); + thing = thing.rest(); + } + return result; + } else { + return x; + } + } + + + var flattenSchemeListToArray = function(x) { + if ( !isList(x) ) { + return [x]; + } + + var ret = []; + while ( !x.isEmpty() ) { + ret = ret.concat( flattenSchemeListToArray(x.first()) ); + x = x.rest(); + } + return ret; + }; + + + // assocListToHash: (listof (list X Y)) -> (hashof X Y) + var assocListToHash = function(lst) { + var result = {}; + while ( !lst.isEmpty() ) { + var key = lst.first().first(); + var val = lst.first().rest().first(); + result[key] = val; + lst = lst.rest(); + } + return result; + }; + + + var ordinalize = function(n) { + // special case for 11th: + if ( n % 100 == 11 ) { + return n + 'th'; + } + var res = n; + switch( n % 10 ) { + case 1: res += 'st'; break; + case 2: res += 'nd'; break; + case 3: res += 'rd'; break; + default: res += 'th'; break; + } + return res; + } + + + var wrapJsValue = function(x) { + if (x === undefined) { + return types.jsValue('undefined', x); + } + else if (x === null) { + return types.jsValue('null', x); + } + else if (typeof(x) == 'function') { + return types.jsValue('function', x); + } + else if ( x instanceof Array ) { + return types.jsValue('array', x); + } + else if ( typeof(x) == 'string' ) { + return types.jsValue("'" + x.toString() + "'", x); + } + else { + return types.jsValue(x.toString(), x); + } + }; + + + var getKeyCodeName = function(e) { + var code = e.charCode || e.keyCode; + var keyname; + switch(code) { + case 16: keyname = "shift"; break; + case 17: keyname = "control"; break; + case 19: keyname = "pause"; break; + case 27: keyname = "escape"; break; + case 33: keyname = "prior"; break; + case 34: keyname = "next"; break; + case 35: keyname = "end"; break; + case 36: keyname = "home"; break; + case 37: keyname = "left"; break; + case 38: keyname = "up"; break; + case 39: keyname = "right"; break; + case 40: keyname = "down"; break; + case 42: keyname = "print"; break; + case 45: keyname = "insert"; break; + case 46: keyname = String.fromCharCode(127); break; + case 106: keyname = "*"; break; + case 107: keyname = "+"; break; + case 109: keyname = "-"; break; + case 110: keyname = "."; break; + case 111: keyname = "/"; break; + case 144: keyname = "numlock"; break; + case 145: keyname = "scroll"; break; + case 186: keyname = ";"; break; + case 187: keyname = "="; break; + case 188: keyname = ","; break; + case 189: keyname = "-"; break; + case 190: keyname = "."; break; + case 191: keyname = "/"; break; + case 192: keyname = "`"; break; + case 219: keyname = "["; break; + case 220: keyname = "\\"; break; + case 221: keyname = "]"; break; + case 222: keyname = "'"; break; + default: if (code >= 96 && code <= 105) { + keyname = (code - 96).toString(); + } + else if (code >= 112 && code <= 123) { + keyname = "f" + (code - 111); + } + else { + keyname = String.fromCharCode(code).toLowerCase(); + } + break; + } + return keyname; + }; + + + + + + // maybeCallAfterAttach: dom-node -> void + // walk the tree rooted at aNode, and call afterAttach if the element has + // such a method. + var maybeCallAfterAttach = function(aNode) { + var stack = [aNode]; + while (stack.length !== 0) { + var nextNode = stack.pop(); + if (nextNode.afterAttach) { + nextNode.afterAttach(nextNode); + } + if (nextNode.hasChildNodes && nextNode.hasChildNodes()) { + var children = nextNode.childNodes; + for (var i = 0; i < children.length; i++) { + stack.push(children[i]); + } + } + } + }; + + + + + + + + + // makeLocationDom: location -> dom + // Dom type that has special support in the editor through the print hook. + // The print hook is expected to look at the printing of dom values with + // this particular structure. In the context of WeScheme, the environment + // will rewrite these to be clickable links. + var makeLocationDom = function(aLocation) { + var locationSpan = document.createElement("span"); + var idSpan = document.createElement("span"); + var offsetSpan = document.createElement("span"); + var lineSpan = document.createElement("span"); + var columnSpan = document.createElement("span"); + var spanSpan = document.createElement("span"); + + locationSpan['className'] = 'location-reference'; + idSpan['className'] = 'location-id'; + offsetSpan['className'] = 'location-offset'; + lineSpan['className'] = 'location-line'; + columnSpan['className'] = 'location-column'; + spanSpan['className'] = 'location-span'; + + idSpan.appendChild(document.createTextNode(String(aLocation.id))); + offsetSpan.appendChild(document.createTextNode(String(aLocation.offset))); + lineSpan.appendChild(document.createTextNode(String(aLocation.line))); + columnSpan.appendChild(document.createTextNode(String(aLocation.column))); + spanSpan.appendChild(document.createTextNode(String(aLocation.span))); + + locationSpan.appendChild(idSpan); + locationSpan.appendChild(offsetSpan); + locationSpan.appendChild(lineSpan); + locationSpan.appendChild(columnSpan); + locationSpan.appendChild(spanSpan); + + return locationSpan; + }; + + + var isLocationDom = function(thing) { + return (thing + && + (thing.nodeType === Node.TEXT_NODE || + thing.nodeType === Node.ELEMENT_NODE) + && + thing['className'] === 'location-reference'); + }; + + + + + + + + + + + + var _eqHashCodeCounter = 0; + makeEqHashCode = function() { + _eqHashCodeCounter++; + return _eqHashCodeCounter; + }; + + + // getHashCode: any -> (or fixnum string) + // Produces a hashcode appropriate for eq. + getEqHashCode = function(x) { + if (typeof(x) === 'string') { + return x; + } + if (typeof(x) === 'number') { + return String(x); + } + if (x && !x._eqHashCode) { + x._eqHashCode = makeEqHashCode(); + } + if (x && x._eqHashCode) { + return x._eqHashCode; + } + return 0; + }; + + + + + var makeLowLevelEqHash = function() { + return new _Hashtable(function(x) { return getEqHashCode(x); }, + function(x, y) { return x === y; }); + }; + + + + // Inheritance. + var heir = function(parentPrototype) { + var f = function() {} + f.prototype = parentPrototype; + return new f(); + }; + + + + //////////////////////////////////////////////// + + helpers.format = format; + helpers.forEachK = forEachK; + helpers.reportError = reportError; + helpers.raise = raise; + + helpers.procArityContains = procArityContains; + helpers.throwCheckError = throwCheckError; + helpers.isList = isList; + helpers.isListOf = isListOf; + helpers.check = check; + helpers.checkListOf = checkListOf; + +// helpers.remove = remove; + helpers.map = map; + helpers.concatMap = concatMap; + helpers.schemeListToArray = schemeListToArray; + helpers.deepListToArray = deepListToArray; + helpers.flattenSchemeListToArray = flattenSchemeListToArray; + helpers.assocListToHash = assocListToHash; + + helpers.ordinalize = ordinalize; + helpers.wrapJsValue = wrapJsValue; + + helpers.getKeyCodeName = getKeyCodeName; + + helpers.maybeCallAfterAttach = maybeCallAfterAttach; + + helpers.makeLocationDom = makeLocationDom; + helpers.isLocationDom = isLocationDom; + + + helpers.getEqHashCode = getEqHashCode; + helpers.makeLowLevelEqHash = makeLowLevelEqHash; + + helpers.heir = heir; + +})(); + +///////////////////////////////////////////////////////////////// + diff --git a/js-assembler/runtime-src/js-numbers.js b/js-assembler/runtime-src/js-numbers.js new file mode 100644 index 0000000..a0ceac9 --- /dev/null +++ b/js-assembler/runtime-src/js-numbers.js @@ -0,0 +1,4089 @@ +// Scheme numbers. + + +var __PLTNUMBERS_TOP__; +if (typeof(exports) !== 'undefined') { + __PLTNUMBERS_TOP__ = exports; +} else { + if (! this['jsnums']) { + this['jsnums'] = {}; + } + __PLTNUMBERS_TOP__ = this['jsnums']; +} + +//var jsnums = {}; + + +// The numeric tower has the following levels: +// integers +// rationals +// floats +// complex numbers +// +// with the representations: +// integers: fixnum or BigInteger [level=0] +// rationals: Rational [level=1] +// floats: FloatPoint [level=2] +// complex numbers: Complex [level=3] + +// We try to stick with the unboxed fixnum representation for +// integers, since that's what scheme programs commonly deal with, and +// we want that common type to be lightweight. + + +// A boxed-scheme-number is either BigInteger, Rational, FloatPoint, or Complex. +// An integer-scheme-number is either fixnum or BigInteger. + + +(function() { + // Abbreviation + var Numbers = __PLTNUMBERS_TOP__; + //var Numbers = jsnums; + + + // makeNumericBinop: (fixnum fixnum -> any) (scheme-number scheme-number -> any) -> (scheme-number scheme-number) X + // Creates a binary function that works either on fixnums or boxnums. + // Applies the appropriate binary function, ensuring that both scheme numbers are + // lifted to the same level. + var makeNumericBinop = function(onFixnums, onBoxednums, options) { + options = options || {}; + return function(x, y) { + if (options.isXSpecialCase && options.isXSpecialCase(x)) + return options.onXSpecialCase(x, y); + if (options.isYSpecialCase && options.isYSpecialCase(y)) + return options.onYSpecialCase(x, y); + + if (typeof(x) === 'number' && + typeof(y) === 'number') { + return onFixnums(x, y); + } + if (typeof(x) === 'number') { + x = liftFixnumInteger(x, y); + } + if (typeof(y) === 'number') { + y = liftFixnumInteger(y, x); + } + + if (x.level < y.level) x = x.liftTo(y); + if (y.level < x.level) y = y.liftTo(x); + return onBoxednums(x, y); + }; + } + + + // fromFixnum: fixnum -> scheme-number + var fromFixnum = function(x) { + if (isNaN(x) || (! isFinite(x))) { + return FloatPoint.makeInstance(x); + } + var nf = Math.floor(x); + if (nf === x) { + if (isOverflow(nf)) { + return makeBignum(expandExponent(x+'')); + } else { + return nf; + } + } else { + return FloatPoint.makeInstance(x); + } + }; + + var expandExponent = function(s) { + var match = s.match(scientificPattern), mantissaChunks, exponent; + if (match) { + mantissaChunks = match[1].match(/^([^.]*)(.*)$/); + exponent = Number(match[2]); + + if (mantissaChunks[2].length === 0) { + return mantissaChunks[1] + zfill(exponent); + } + + if (exponent >= mantissaChunks[2].length - 1) { + return (mantissaChunks[1] + + mantissaChunks[2].substring(1) + + zfill(exponent - (mantissaChunks[2].length - 1))); + } else { + return (mantissaChunks[1] + + mantissaChunks[2].substring(1, 1+exponent)); + } + } else { + return s; + } + }; + + // zfill: integer -> string + // builds a string of "0"'s of length n. + var zfill = function(n) { + var buffer = []; + buffer.length = n; + for (var i = 0; i < n; i++) { + buffer[i] = '0'; + } + return buffer.join(''); + }; + + + + // liftFixnumInteger: fixnum-integer boxed-scheme-number -> boxed-scheme-number + // Lifts up fixnum integers to a boxed type. + var liftFixnumInteger = function(x, other) { + switch(other.level) { + case 0: // BigInteger + return makeBignum(x); + case 1: // Rational + return new Rational(x, 1); + case 2: // FloatPoint + return new FloatPoint(x); + case 3: // Complex + return new Complex(x, 0); + default: + throwRuntimeError("IMPOSSIBLE: cannot lift fixnum integer to " + other.toString(), x, other); + } + }; + + + // throwRuntimeError: string (scheme-number | undefined) (scheme-number | undefined) -> void + // Throws a runtime error with the given message string. + var throwRuntimeError = function(msg, x, y) { + Numbers['onThrowRuntimeError'](msg, x, y); + }; + + + + // onThrowRuntimeError: string (scheme-number | undefined) (scheme-number | undefined) -> void + // By default, will throw a new Error with the given message. + // Override Numbers['onThrowRuntimeError'] if you need to do something special. + var onThrowRuntimeError = function(msg, x, y) { + throw new Error(msg); + }; + + + // isSchemeNumber: any -> boolean + // Returns true if the thing is a scheme number. + var isSchemeNumber = function(thing) { + return (typeof(thing) === 'number' + || (thing instanceof Rational || + thing instanceof FloatPoint || + thing instanceof Complex || + thing instanceof BigInteger)); + }; + + + // isRational: scheme-number -> boolean + var isRational = function(n) { + return (typeof(n) === 'number' || + (isSchemeNumber(n) && n.isRational())); + }; + + // isReal: scheme-number -> boolean + var isReal = function(n) { + return (typeof(n) === 'number' || + (isSchemeNumber(n) && n.isReal())); + }; + + // isExact: scheme-number -> boolean + var isExact = function(n) { + return (typeof(n) === 'number' || + (isSchemeNumber(n) && n.isExact())); + }; + + // isExact: scheme-number -> boolean + var isInexact = function(n) { + if (typeof(n) === 'number') { + return false; + } else { + return (isSchemeNumber(n) && n.isInexact()); + } + }; + + // isInteger: scheme-number -> boolean + var isInteger = function(n) { + return (typeof(n) === 'number' || + (isSchemeNumber(n) && n.isInteger())); + }; + + // isExactInteger: scheme-number -> boolean + var isExactInteger = function(n) { + return (typeof(n) === 'number' || + (isSchemeNumber(n) && + n.isInteger() && + n.isExact())); + } + + + + // toFixnum: scheme-number -> javascript-number + var toFixnum = function(n) { + if (typeof(n) === 'number') + return n; + return n.toFixnum(); + }; + + // toExact: scheme-number -> scheme-number + var toExact = function(n) { + if (typeof(n) === 'number') + return n; + return n.toExact(); + }; + + + // toExact: scheme-number -> scheme-number + var toInexact = function(n) { + if (typeof(n) === 'number') + return FloatPoint.makeInstance(n); + return n.toInexact(); + }; + + + + ////////////////////////////////////////////////////////////////////// + + + // add: scheme-number scheme-number -> scheme-number + var add = makeNumericBinop( + function(x, y) { + var sum = x + y; + if (isOverflow(sum)) { + return (makeBignum(x)).add(makeBignum(y)); + } else { + return sum; + } + }, + function(x, y) { + return x.add(y); + }, + {isXSpecialCase: function(x) { + return isExactInteger(x) && _integerIsZero(x) }, + onXSpecialCase: function(x, y) { return y; }, + isYSpecialCase: function(y) { + return isExactInteger(y) && _integerIsZero(y) }, + onYSpecialCase: function(x, y) { return x; } + }); + + + // subtract: scheme-number scheme-number -> scheme-number + var subtract = makeNumericBinop( + function(x, y) { + var diff = x - y; + if (isOverflow(diff)) { + return (makeBignum(x)).subtract(makeBignum(y)); + } else { + return diff; + } + }, + function(x, y) { + return x.subtract(y); + }, + {isXSpecialCase: function(x) { + return isExactInteger(x) && _integerIsZero(x) }, + onXSpecialCase: function(x, y) { return negate(y); }, + isYSpecialCase: function(y) { + return isExactInteger(y) && _integerIsZero(y) }, + onYSpecialCase: function(x, y) { return x; } + }); + + + // mulitply: scheme-number scheme-number -> scheme-number + var multiply = makeNumericBinop( + function(x, y) { + var prod = x * y; + if (isOverflow(prod)) { + return (makeBignum(x)).multiply(makeBignum(y)); + } else { + return prod; + } + }, + function(x, y) { + return x.multiply(y); + }, + {isXSpecialCase: function(x) { + return (isExactInteger(x) && + (_integerIsZero(x) || _integerIsOne(x) || _integerIsNegativeOne(x))) }, + onXSpecialCase: function(x, y) { + if (_integerIsZero(x)) + return 0; + if (_integerIsOne(x)) + return y; + if (_integerIsNegativeOne(x)) + return negate(y); + }, + isYSpecialCase: function(y) { + return (isExactInteger(y) && + (_integerIsZero(y) || _integerIsOne(y) || _integerIsNegativeOne(y)))}, + onYSpecialCase: function(x, y) { + if (_integerIsZero(y)) + return 0; + if (_integerIsOne(y)) + return x; + if (_integerIsNegativeOne(y)) + return negate(x); + } + }); + + + // divide: scheme-number scheme-number -> scheme-number + var divide = makeNumericBinop( + function(x, y) { + if (_integerIsZero(y)) + throwRuntimeError("/: division by zero", x, y); + var div = x / y; + if (isOverflow(div)) { + return (makeBignum(x)).divide(makeBignum(y)); + } else if (Math.floor(div) !== div) { + return Rational.makeInstance(x, y); + } else { + return div; + } + }, + function(x, y) { + return x.divide(y); + }, + { isXSpecialCase: function(x) { + return (eqv(x, 0)); + }, + onXSpecialCase: function(x, y) { + if (eqv(y, 0)) { + throwRuntimeError("/: division by zero", x, y); + } + return 0; + }, + isYSpecialCase: function(y) { + return (eqv(y, 0)); }, + onYSpecialCase: function(x, y) { + throwRuntimeError("/: division by zero", x, y); + } + }); + + + // equals: scheme-number scheme-number -> boolean + var equals = makeNumericBinop( + function(x, y) { + return x === y; + }, + function(x, y) { + return x.equals(y); + }); + + + // eqv: scheme-number scheme-number -> boolean + var eqv = function(x, y) { + if (x === y) + return true; + if (typeof(x) === 'number' && typeof(y) === 'number') + return x === y; + if (x === NEGATIVE_ZERO || y === NEGATIVE_ZERO) + return x === y; + if (x instanceof Complex || y instanceof Complex) { + return (eqv(realPart(x), realPart(y)) && + eqv(imaginaryPart(x), imaginaryPart(y))); + } + var ex = isExact(x), ey = isExact(y); + return (((ex && ey) || (!ex && !ey)) && equals(x, y)); + }; + + // approxEqual: scheme-number scheme-number scheme-number -> boolean + var approxEquals = function(x, y, delta) { + return lessThan(abs(subtract(x, y)), + delta); + }; + + // greaterThanOrEqual: scheme-number scheme-number -> boolean + var greaterThanOrEqual = makeNumericBinop( + function(x, y) { + return x >= y; + }, + function(x, y) { + if (!(isReal(x) && isReal(y))) + throwRuntimeError( + ">=: couldn't be applied to complex number", x, y); + return x.greaterThanOrEqual(y); + }); + + + // lessThanOrEqual: scheme-number scheme-number -> boolean + var lessThanOrEqual = makeNumericBinop( + function(x, y){ + + return x <= y; + }, + function(x, y) { + if (!(isReal(x) && isReal(y))) + throwRuntimeError("<=: couldn't be applied to complex number", x, y); + return x.lessThanOrEqual(y); + }); + + + // greaterThan: scheme-number scheme-number -> boolean + var greaterThan = makeNumericBinop( + function(x, y){ + return x > y; + }, + function(x, y) { + if (!(isReal(x) && isReal(y))) + throwRuntimeError(">: couldn't be applied to complex number", x, y); + return x.greaterThan(y); + }); + + + // lessThan: scheme-number scheme-number -> boolean + var lessThan = makeNumericBinop( + function(x, y){ + + return x < y; + }, + function(x, y) { + if (!(isReal(x) && isReal(y))) + throwRuntimeError("<: couldn't be applied to complex number", x, y); + return x.lessThan(y); + }); + + + + // expt: scheme-number scheme-number -> scheme-number + var expt = (function() { + var _expt = makeNumericBinop( + function(x, y){ + var pow = Math.pow(x, y); + if (isOverflow(pow)) { + return (makeBignum(x)).expt(makeBignum(y)); + } else { + return pow; + } + }, + function(x, y) { + if (equals(y, 0)) { + return add(y, 1); + } else { + return x.expt(y); + } + }); + return function(x, y) { + if (equals(y, 0)) + return add(y, 1); + if (isReal(y) && lessThan(y, 0)) { + return _expt(divide(1, x), negate(y)); + } + return _expt(x, y); + }; + })(); + + + // exp: scheme-number -> scheme-number + var exp = function(n) { + if ( eqv(n, 0) ) { + return 1; + } + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.exp(n)); + } + return n.exp(); + }; + + + // modulo: scheme-number scheme-number -> scheme-number + var modulo = function(m, n) { + if (! isInteger(m)) { + throwRuntimeError('modulo: the first argument ' + + m + " is not an integer.", m, n); + } + if (! isInteger(n)) { + throwRuntimeError('modulo: the second argument ' + + n + " is not an integer.", m, n); + } + var result; + if (typeof(m) === 'number') { + result = m % n; + if (n < 0) { + if (result <= 0) + return result; + else + return result + n; + } else { + if (result < 0) + return result + n; + else + return result; + } + } + result = _integerModulo(floor(m), floor(n)); + // The sign of the result should match the sign of n. + if (lessThan(n, 0)) { + if (lessThanOrEqual(result, 0)) { + return result; + } + return add(result, n); + + } else { + if (lessThan(result, 0)) { + return add(result, n); + } + return result; + } + }; + + + + // numerator: scheme-number -> scheme-number + var numerator = function(n) { + if (typeof(n) === 'number') + return n; + return n.numerator(); + }; + + + // denominator: scheme-number -> scheme-number + var denominator = function(n) { + if (typeof(n) === 'number') + return 1; + return n.denominator(); + }; + + // sqrt: scheme-number -> scheme-number + var sqrt = function(n) { + if (typeof(n) === 'number') { + if (n >= 0) { + var result = Math.sqrt(n); + if (Math.floor(result) === result) { + return result; + } else { + return FloatPoint.makeInstance(result); + } + } else { + return (Complex.makeInstance(0, sqrt(-n))); + } + } + return n.sqrt(); + }; + + // abs: scheme-number -> scheme-number + var abs = function(n) { + if (typeof(n) === 'number') { + return Math.abs(n); + } + return n.abs(); + }; + + // floor: scheme-number -> scheme-number + var floor = function(n) { + if (typeof(n) === 'number') + return n; + return n.floor(); + }; + + // ceiling: scheme-number -> scheme-number + var ceiling = function(n) { + if (typeof(n) === 'number') + return n; + return n.ceiling(); + }; + + // conjugate: scheme-number -> scheme-number + var conjugate = function(n) { + if (typeof(n) === 'number') + return n; + return n.conjugate(); + }; + + // magnitude: scheme-number -> scheme-number + var magnitude = function(n) { + if (typeof(n) === 'number') + return Math.abs(n); + return n.magnitude(); + }; + + + // log: scheme-number -> scheme-number + var log = function(n) { + if ( eqv(n, 1) ) { + return 0; + } + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.log(n)); + } + return n.log(); + }; + + // angle: scheme-number -> scheme-number + var angle = function(n) { + if (typeof(n) === 'number') { + if (n > 0) + return 0; + else + return FloatPoint.pi; + } + return n.angle(); + }; + + // tan: scheme-number -> scheme-number + var tan = function(n) { + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.tan(n)); + } + return n.tan(); + }; + + // atan: scheme-number -> scheme-number + var atan = function(n) { + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.atan(n)); + } + return n.atan(); + }; + + // cos: scheme-number -> scheme-number + var cos = function(n) { + if (eqv(n, 0)) { return 1; } + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.cos(n)); + } + return n.cos(); + }; + + // sin: scheme-number -> scheme-number + var sin = function(n) { + if (eqv(n, 0)) { return 0; } + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.sin(n)); + } + return n.sin(); + }; + + // acos: scheme-number -> scheme-number + var acos = function(n) { + if (eqv(n, 1)) { return 0; } + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.acos(n)); + } + return n.acos(); + }; + + // asin: scheme-number -> scheme-number + var asin = function(n) { + if (typeof(n) === 'number') { + return FloatPoint.makeInstance(Math.asin(n)); + } + return n.asin(); + }; + + // imaginaryPart: scheme-number -> scheme-number + var imaginaryPart = function(n) { + if (typeof(n) === 'number') { + return 0; + } + return n.imaginaryPart(); + }; + + // realPart: scheme-number -> scheme-number + var realPart = function(n) { + if (typeof(n) === 'number') { + return n; + } + return n.realPart(); + }; + + // round: scheme-number -> scheme-number + var round = function(n) { + if (typeof(n) === 'number') { + return n; + } + return n.round(); + }; + + + + // sqr: scheme-number -> scheme-number + var sqr = function(x) { + return multiply(x, x); + }; + + + // integerSqrt: scheme-number -> scheme-number + var integerSqrt = function(x) { + if (! isInteger(x)) { + throwRuntimeError('integer-sqrt: the argument ' + x.toString() + + " is not an integer.", x); + } + if (typeof (x) === 'number') { + if(x < 0) { + return Complex.makeInstance(0, + Math.floor(Math.sqrt(-x))) + } else { + return Math.floor(Math.sqrt(x)); + } + } + return x.integerSqrt(); + }; + + + // gcd: scheme-number [scheme-number ...] -> scheme-number + var gcd = function(first, rest) { + if (! isInteger(first)) { + throwRuntimeError('gcd: the argument ' + first.toString() + + " is not an integer.", first); + } + var a = abs(first), t, b; + for(var i = 0; i < rest.length; i++) { + b = abs(rest[i]); + if (! isInteger(b)) { + throwRuntimeError('gcd: the argument ' + b.toString() + + " is not an integer.", b); + } + while (! _integerIsZero(b)) { + t = a; + a = b; + b = _integerModulo(t, b); + } + } + return a; + }; + + // lcm: scheme-number [scheme-number ...] -> scheme-number + var lcm = function(first, rest) { + if (! isInteger(first)) { + throwRuntimeError('lcm: the argument ' + first.toString() + + " is not an integer.", first); + } + var result = abs(first); + if (_integerIsZero(result)) { return 0; } + for (var i = 0; i < rest.length; i++) { + if (! isInteger(rest[i])) { + throwRuntimeError('lcm: the argument ' + rest[i].toString() + + " is not an integer.", rest[i]); + } + var divisor = _integerGcd(result, rest[i]); + if (_integerIsZero(divisor)) { + return 0; + } + result = divide(multiply(result, rest[i]), divisor); + } + return result; + }; + + + var quotient = function(x, y) { + if (! isInteger(x)) { + throwRuntimeError('quotient: the first argument ' + x.toString() + + " is not an integer.", x); + } + if (! isInteger(y)) { + throwRuntimeError('quotient: the second argument ' + y.toString() + + " is not an integer.", y); + } + return _integerQuotient(x, y); + }; + + + var remainder = function(x, y) { + if (! isInteger(x)) { + throwRuntimeError('remainder: the first argument ' + x.toString() + + " is not an integer.", x); + } + if (! isInteger(y)) { + throwRuntimeError('remainder: the second argument ' + y.toString() + + " is not an integer.", y); + } + return _integerRemainder(x, y); + }; + + + // Implementation of the hyperbolic functions + // http://en.wikipedia.org/wiki/Hyperbolic_cosine + var cosh = function(x) { + if (eqv(x, 0)) { + return FloatPoint.makeInstance(1.0); + } + return divide(add(exp(x), exp(negate(x))), + 2); + }; + + var sinh = function(x) { + return divide(subtract(exp(x), exp(negate(x))), + 2); + }; + + + + var makeComplexPolar = function(r, theta) { + // special case: if theta is zero, just return + // the scalar. + if (equals(theta, 0)) { + return r; + } + return Complex.makeInstance(multiply(r, cos(theta)), + multiply(r, sin(theta))); + }; + + + + ////////////////////////////////////////////////////////////////////// + + // Helpers + + + // IsFinite: scheme-number -> boolean + // Returns true if the scheme number is finite or not. + var isSchemeNumberFinite = function(n) { + if (typeof(n) === 'number') { + return isFinite(n); + } else { + return n.isFinite(); + } + }; + + // isOverflow: javascript-number -> boolean + // Returns true if we consider the number an overflow. + var MIN_FIXNUM = -(9e15); + var MAX_FIXNUM = (9e15); + var isOverflow = function(n) { + return (n < MIN_FIXNUM || MAX_FIXNUM < n); + }; + + + // negate: scheme-number -> scheme-number + // multiplies a number times -1. + var negate = function(n) { + if (typeof(n) === 'number') { + return -n; + } + return n.negate(); + }; + + + // halve: scheme-number -> scheme-number + // Divide a number by 2. + var halve = function(n) { + return divide(n, 2); + }; + + + // timesI: scheme-number scheme-number + // multiplies a number times i. + var timesI = function(x) { + return multiply(x, plusI); + }; + + + // fastExpt: computes n^k by squaring. + // n^k = (n^2)^(k/2) + // Assumes k is non-negative integer. + var fastExpt = function(n, k) { + var acc = 1; + while (true) { + if (_integerIsZero(k)) { + return acc; + } + if (equals(modulo(k, 2), 0)) { + n = multiply(n, n); + k = divide(k, 2); + } else { + acc = multiply(acc, n); + k = subtract(k, 1); + } + } + }; + + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + + // Integer operations + // Integers are either represented as fixnums or as BigIntegers. + + // makeIntegerBinop: (fixnum fixnum -> X) (BigInteger BigInteger -> X) -> X + // Helper to collect the common logic for coersing integer fixnums or bignums to a + // common type before doing an operation. + var makeIntegerBinop = function(onFixnums, onBignums, options) { + options = options || {}; + return (function(m, n) { + if (m instanceof Rational) { + m = numerator(m); + } else if (m instanceof Complex) { + m = realPart(m); + } + + if (n instanceof Rational) { + n = numerator(n); + }else if (n instanceof Complex) { + n = realPart(n); + } + + if (typeof(m) === 'number' && typeof(n) === 'number') { + var result = onFixnums(m, n); + if (! isOverflow(result) || + (options.ignoreOverflow)) { + return result; + } + } + if (m instanceof FloatPoint || n instanceof FloatPoint) { + if (options.doNotCoerseToFloating) { + return onFixnums(toFixnum(m), toFixnum(n)); + } + else { + return FloatPoint.makeInstance( + onFixnums(toFixnum(m), toFixnum(n))); + } + } + if (typeof(m) === 'number') { + m = makeBignum(m); + } + if (typeof(n) === 'number') { + n = makeBignum(n); + } + return onBignums(m, n); + }); + }; + + + var makeIntegerUnOp = function(onFixnums, onBignums, options) { + options = options || {}; + return (function(m) { + if (m instanceof Rational) { + m = numerator(m); + } else if (m instanceof Complex) { + m = realPart(m); + } + + if (typeof(m) === 'number') { + var result = onFixnums(m); + if (! isOverflow(result) || + (options.ignoreOverflow)) { + return result; + } + } + if (m instanceof FloatPoint) { + return onFixnums(toFixnum(m)); + } + if (typeof(m) === 'number') { + m = makeBignum(m); + } + return onBignums(m); + }); + }; + + + + // _integerModulo: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerModulo = makeIntegerBinop( + function(m, n) { + return m % n; + }, + function(m, n) { + return bnMod.call(m, n); + }); + + + // _integerGcd: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerGcd = makeIntegerBinop( + function(a, b) { + var t; + while (b !== 0) { + t = a; + a = b; + b = t % b; + } + return a; + }, + function(m, n) { + return bnGCD.call(m, n); + }); + + + // _integerIsZero: integer-scheme-number -> boolean + // Returns true if the number is zero. + var _integerIsZero = makeIntegerUnOp( + function(n){ + return n === 0; + }, + function(n) { + return bnEquals.call(n, BigInteger.ZERO); + } + ); + + + // _integerIsOne: integer-scheme-number -> boolean + var _integerIsOne = makeIntegerUnOp( + function(n) { + return n === 1; + }, + function(n) { + return bnEquals.call(n, BigInteger.ONE); + }); + + + + // _integerIsNegativeOne: integer-scheme-number -> boolean + var _integerIsNegativeOne = makeIntegerUnOp( + function(n) { + return n === -1; + }, + function(n) { + return bnEquals.call(n, BigInteger.NEGATIVE_ONE); + }); + + + + // _integerAdd: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerAdd = makeIntegerBinop( + function(m, n) { + return m + n; + }, + function(m, n) { + return bnAdd.call(m, n); + }); + + // _integerSubtract: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerSubtract = makeIntegerBinop( + function(m, n) { + return m - n; + }, + function(m, n) { + return bnSubtract.call(m, n); + }); + + // _integerMultiply: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerMultiply = makeIntegerBinop( + function(m, n) { + return m * n; + }, + function(m, n) { + return bnMultiply.call(m, n); + }); + + //_integerQuotient: integer-scheme-number integer-scheme-number -> integer-scheme-number + var _integerQuotient = makeIntegerBinop( + function(m, n) { + return ((m - (m % n))/ n); + }, + function(m, n) { + return bnDivide.call(m, n); + }); + + var _integerRemainder = makeIntegerBinop( + function(m, n) { + return m % n; + }, + function(m, n) { + return bnRemainder.call(m, n); + }); + + + // _integerDivideToFixnum: integer-scheme-number integer-scheme-number -> fixnum + var _integerDivideToFixnum = makeIntegerBinop( + function(m, n) { + return m / n; + }, + function(m, n) { + return toFixnum(m) / toFixnum(n); + }, + {ignoreOverflow: true, + doNotCoerseToFloating: true}); + + + // _integerEquals: integer-scheme-number integer-scheme-number -> boolean + var _integerEquals = makeIntegerBinop( + function(m, n) { + return m === n; + }, + function(m, n) { + return bnEquals.call(m, n); + }, + {doNotCoerseToFloating: true}); + + // _integerGreaterThan: integer-scheme-number integer-scheme-number -> boolean + var _integerGreaterThan = makeIntegerBinop( + function(m, n) { + return m > n; + }, + function(m, n) { + return bnCompareTo.call(m, n) > 0; + }, + {doNotCoerseToFloating: true}); + + // _integerLessThan: integer-scheme-number integer-scheme-number -> boolean + var _integerLessThan = makeIntegerBinop( + function(m, n) { + return m < n; + }, + function(m, n) { + return bnCompareTo.call(m, n) < 0; + }, + {doNotCoerseToFloating: true}); + + // _integerGreaterThanOrEqual: integer-scheme-number integer-scheme-number -> boolean + var _integerGreaterThanOrEqual = makeIntegerBinop( + function(m, n) { + return m >= n; + }, + function(m, n) { + return bnCompareTo.call(m, n) >= 0; + }, + {doNotCoerseToFloating: true}); + + // _integerLessThanOrEqual: integer-scheme-number integer-scheme-number -> boolean + var _integerLessThanOrEqual = makeIntegerBinop( + function(m, n) { + return m <= n; + }, + function(m, n) { + return bnCompareTo.call(m, n) <= 0; + }, + {doNotCoerseToFloating: true}); + + + + ////////////////////////////////////////////////////////////////////// + // The boxed number types are expected to implement the following + // interface. + // + // toString: -> string + + // level: number + + // liftTo: scheme-number -> scheme-number + + // isFinite: -> boolean + + // isInteger: -> boolean + // Produce true if this number can be coersed into an integer. + + // isRational: -> boolean + // Produce true if the number is rational. + + // isReal: -> boolean + // Produce true if the number is real. + + // isExact: -> boolean + // Produce true if the number is exact + + // toExact: -> scheme-number + // Produce an exact number. + + // toFixnum: -> javascript-number + // Produce a javascript number. + + // greaterThan: scheme-number -> boolean + // Compare against instance of the same type. + + // greaterThanOrEqual: scheme-number -> boolean + // Compare against instance of the same type. + + // lessThan: scheme-number -> boolean + // Compare against instance of the same type. + + // lessThanOrEqual: scheme-number -> boolean + // Compare against instance of the same type. + + // add: scheme-number -> scheme-number + // Add with an instance of the same type. + + // subtract: scheme-number -> scheme-number + // Subtract with an instance of the same type. + + // multiply: scheme-number -> scheme-number + // Multiply with an instance of the same type. + + // divide: scheme-number -> scheme-number + // Divide with an instance of the same type. + + // numerator: -> scheme-number + // Return the numerator. + + // denominator: -> scheme-number + // Return the denominator. + + // integerSqrt: -> scheme-number + // Produce the integer square root. + + // sqrt: -> scheme-number + // Produce the square root. + + // abs: -> scheme-number + // Produce the absolute value. + + // floor: -> scheme-number + // Produce the floor. + + // ceiling: -> scheme-number + // Produce the ceiling. + + // conjugate: -> scheme-number + // Produce the conjugate. + + // magnitude: -> scheme-number + // Produce the magnitude. + + // log: -> scheme-number + // Produce the log. + + // angle: -> scheme-number + // Produce the angle. + + // atan: -> scheme-number + // Produce the arc tangent. + + // cos: -> scheme-number + // Produce the cosine. + + // sin: -> scheme-number + // Produce the sine. + + // expt: scheme-number -> scheme-number + // Produce the power to the input. + + // exp: -> scheme-number + // Produce e raised to the given power. + + // acos: -> scheme-number + // Produce the arc cosine. + + // asin: -> scheme-number + // Produce the arc sine. + + // imaginaryPart: -> scheme-number + // Produce the imaginary part + + // realPart: -> scheme-number + // Produce the real part. + + // round: -> scheme-number + // Round to the nearest integer. + + // equals: scheme-number -> boolean + // Produce true if the given number of the same type is equal. + + + + ////////////////////////////////////////////////////////////////////// + + // Rationals + + + var Rational = function(n, d) { + this.n = n; + this.d = d; + }; + + + Rational.prototype.toString = function() { + if (_integerIsOne(this.d)) { + return this.n.toString() + ""; + } else { + return this.n.toString() + "/" + this.d.toString(); + } + }; + + + Rational.prototype.level = 1; + + + Rational.prototype.liftTo = function(target) { + if (target.level === 2) + return new FloatPoint( + _integerDivideToFixnum(this.n, this.d)); + if (target.level === 3) + return new Complex(this, 0); + return throwRuntimeError("invalid level of Number", this, target); + }; + + Rational.prototype.isFinite = function() { + return true; + }; + + Rational.prototype.equals = function(other) { + return (other instanceof Rational && + _integerEquals(this.n, other.n) && + _integerEquals(this.d, other.d)); + }; + + + + Rational.prototype.isInteger = function() { + return _integerIsOne(this.d); + }; + + Rational.prototype.isRational = function() { + return true; + }; + + Rational.prototype.isReal = function() { + return true; + }; + + + Rational.prototype.add = function(other) { + return Rational.makeInstance(_integerAdd(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)), + _integerMultiply(this.d, other.d)); + }; + + Rational.prototype.subtract = function(other) { + return Rational.makeInstance(_integerSubtract(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)), + _integerMultiply(this.d, other.d)); + }; + + Rational.prototype.negate = function() { + return Rational.makeInstance(-this.n, this.d) + }; + + Rational.prototype.multiply = function(other) { + return Rational.makeInstance(_integerMultiply(this.n, other.n), + _integerMultiply(this.d, other.d)); + }; + + Rational.prototype.divide = function(other) { + if (_integerIsZero(this.d) || _integerIsZero(other.n)) { + throwRuntimeError("/: division by zero", this, other); + } + return Rational.makeInstance(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)); + }; + + + Rational.prototype.toExact = function() { + return this; + }; + + Rational.prototype.toInexact = function() { + return FloatPoint.makeInstance(this.toFixnum()); + }; + + + Rational.prototype.isExact = function() { + return true; + }; + + Rational.prototype.isInexact = function() { + return false; + }; + + + Rational.prototype.toFixnum = function() { + return _integerDivideToFixnum(this.n, this.d); + }; + + Rational.prototype.numerator = function() { + return this.n; + }; + + Rational.prototype.denominator = function() { + return this.d; + }; + + Rational.prototype.greaterThan = function(other) { + return _integerGreaterThan(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)); + }; + + Rational.prototype.greaterThanOrEqual = function(other) { + return _integerGreaterThanOrEqual(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)); + }; + + Rational.prototype.lessThan = function(other) { + return _integerLessThan(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)); + }; + + Rational.prototype.lessThanOrEqual = function(other) { + return _integerLessThanOrEqual(_integerMultiply(this.n, other.d), + _integerMultiply(this.d, other.n)); + }; + + Rational.prototype.integerSqrt = function() { + var result = sqrt(this); + if (isRational(result)) { + return toExact(floor(result)); + } else if (isReal(result)) { + return toExact(floor(result)); + } else { + return Complex.makeInstance(toExact(floor(realPart(result))), + toExact(floor(imaginaryPart(result)))); + } + }; + + + Rational.prototype.sqrt = function() { + if (_integerGreaterThanOrEqual(this.n, 0)) { + var newN = sqrt(this.n); + var newD = sqrt(this.d); + if (equals(floor(newN), newN) && + equals(floor(newD), newD)) { + return Rational.makeInstance(newN, newD); + } else { + return FloatPoint.makeInstance(_integerDivideToFixnum(newN, newD)); + } + } else { + var newN = sqrt(negate(this.n)); + var newD = sqrt(this.d); + if (equals(floor(newN), newN) && + equals(floor(newD), newD)) { + return Complex.makeInstance( + 0, + Rational.makeInstance(newN, newD)); + } else { + return Complex.makeInstance( + 0, + FloatPoint.makeInstance(_integerDivideToFixnum(newN, newD))); + } + } + }; + + Rational.prototype.abs = function() { + return Rational.makeInstance(abs(this.n), + this.d); + }; + + + Rational.prototype.floor = function() { + var quotient = _integerQuotient(this.n, this.d); + if (_integerLessThan(this.n, 0)) { + return subtract(quotient, 1); + } else { + return quotient; + } + }; + + + Rational.prototype.ceiling = function() { + var quotient = _integerQuotient(this.n, this.d); + if (_integerLessThan(this.n, 0)) { + return quotient; + } else { + return add(quotient, 1); + } + }; + + Rational.prototype.conjugate = function() { + return this; + }; + + Rational.prototype.magnitude = Rational.prototype.abs; + + Rational.prototype.log = function(){ + return FloatPoint.makeInstance(Math.log(this.n / this.d)); + }; + + Rational.prototype.angle = function(){ + if (_integerIsZero(this.n)) + return 0; + if (_integerGreaterThan(this.n, 0)) + return 0; + else + return FloatPoint.pi; + }; + + Rational.prototype.tan = function(){ + return FloatPoint.makeInstance(Math.tan(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.atan = function(){ + return FloatPoint.makeInstance(Math.atan(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.cos = function(){ + return FloatPoint.makeInstance(Math.cos(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.sin = function(){ + return FloatPoint.makeInstance(Math.sin(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.expt = function(a){ + if (isExactInteger(a) && greaterThanOrEqual(a, 0)) { + return fastExpt(this, a); + } + return FloatPoint.makeInstance(Math.pow(_integerDivideToFixnum(this.n, this.d), + _integerDivideToFixnum(a.n, a.d))); + }; + + Rational.prototype.exp = function(){ + return FloatPoint.makeInstance(Math.exp(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.acos = function(){ + return FloatPoint.makeInstance(Math.acos(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.asin = function(){ + return FloatPoint.makeInstance(Math.asin(_integerDivideToFixnum(this.n, this.d))); + }; + + Rational.prototype.imaginaryPart = function(){ + return 0; + }; + + Rational.prototype.realPart = function(){ + return this; + }; + + + Rational.prototype.round = function() { + // FIXME: not correct when values are bignums + if (equals(this.d, 2)) { + // Round to even if it's a n/2 + var v = _integerDivideToFixnum(this.n, this.d); + var fl = Math.floor(v); + var ce = Math.ceil(v); + if (_integerIsZero(fl % 2)) { + return fl; + } + else { + return ce; + } + } else { + return Math.round(this.n / this.d); + } + }; + + + Rational.makeInstance = function(n, d) { + if (n === undefined) + throwRuntimeError("n undefined", n, d); + + if (d === undefined) { d = 1; } + + if (_integerLessThan(d, 0)) { + n = negate(n); + d = negate(d); + } + + var divisor = _integerGcd(abs(n), abs(d)); + n = _integerQuotient(n, divisor); + d = _integerQuotient(d, divisor); + + // Optimization: if we can get around construction the rational + // in favor of just returning n, do it: + if (_integerIsOne(d) || _integerIsZero(n)) { + return n; + } + + return new Rational(n, d); + }; + + + + // Floating Point numbers + var FloatPoint = function(n) { + this.n = n; + }; + FloatPoint = FloatPoint; + + + var NaN = new FloatPoint(Number.NaN); + var inf = new FloatPoint(Number.POSITIVE_INFINITY); + var neginf = new FloatPoint(Number.NEGATIVE_INFINITY); + + // We use these two constants to represent the floating-point coersion + // of bignums that can't be represented with fidelity. + var TOO_POSITIVE_TO_REPRESENT = new FloatPoint(Number.POSITIVE_INFINITY); + var TOO_NEGATIVE_TO_REPRESENT = new FloatPoint(Number.NEGATIVE_INFINITY); + + // Negative zero is a distinguished value representing -0.0. + // There should only be one instance for -0.0. + var NEGATIVE_ZERO = new FloatPoint(-0.0); + var INEXACT_ZERO = new FloatPoint(0.0); + + FloatPoint.pi = new FloatPoint(Math.PI); + FloatPoint.e = new FloatPoint(Math.E); + FloatPoint.nan = NaN; + FloatPoint.inf = inf; + FloatPoint.neginf = neginf; + + FloatPoint.makeInstance = function(n) { + if (isNaN(n)) { + return FloatPoint.nan; + } else if (n === Number.POSITIVE_INFINITY) { + return FloatPoint.inf; + } else if (n === Number.NEGATIVE_INFINITY) { + return FloatPoint.neginf; + } else if (n === 0) { + if ((1/n) === -Infinity) { + return NEGATIVE_ZERO; + } else { + return INEXACT_ZERO; + } + } + return new FloatPoint(n); + }; + + + FloatPoint.prototype.isExact = function() { + return false; + }; + + FloatPoint.prototype.isInexact = function() { + return true; + }; + + + FloatPoint.prototype.isFinite = function() { + return (isFinite(this.n) || + this === TOO_POSITIVE_TO_REPRESENT || + this === TOO_NEGATIVE_TO_REPRESENT); + }; + + + FloatPoint.prototype.toExact = function() { + // The precision of ieee is about 16 decimal digits, which we use here. + if (! isFinite(this.n) || isNaN(this.n)) { + throwRuntimeError("toExact: no exact representation for " + this, this); + } + + var stringRep = this.n.toString(); + var match = stringRep.match(/^(.*)\.(.*)$/); + if (match) { + var intPart = parseInt(match[1]); + var fracPart = parseInt(match[2]); + var tenToDecimalPlaces = Math.pow(10, match[2].length); + return Rational.makeInstance(Math.round(this.n * tenToDecimalPlaces), + tenToDecimalPlaces); + } + else { + return this.n; + } + }; + + FloatPoint.prototype.toInexact = function() { + return this; + }; + + FloatPoint.prototype.isInexact = function() { + return true; + }; + + + FloatPoint.prototype.level = 2; + + + FloatPoint.prototype.liftTo = function(target) { + if (target.level === 3) + return new Complex(this, 0); + return throwRuntimeError("invalid level of Number", this, target); + }; + + FloatPoint.prototype.toString = function() { + if (isNaN(this.n)) + return "+nan.0"; + if (this.n === Number.POSITIVE_INFINITY) + return "+inf.0"; + if (this.n === Number.NEGATIVE_INFINITY) + return "-inf.0"; + if (this === NEGATIVE_ZERO) + return "-0.0"; + var partialResult = this.n.toString(); + if (! partialResult.match('\\.')) { + return partialResult + ".0"; + } else { + return partialResult; + } + }; + + + FloatPoint.prototype.equals = function(other, aUnionFind) { + return ((other instanceof FloatPoint) && + ((this.n === other.n))); + }; + + + + FloatPoint.prototype.isRational = function() { + return this.isFinite(); + }; + + FloatPoint.prototype.isInteger = function() { + return this.isFinite() && this.n === Math.floor(this.n); + }; + + FloatPoint.prototype.isReal = function() { + return true; + }; + + + // sign: Number -> {-1, 0, 1} + var sign = function(n) { + if (lessThan(n, 0)) { + return -1; + } else if (greaterThan(n, 0)) { + return 1; + } else if (n === NEGATIVE_ZERO) { + return -1; + } else { + return 0; + } + }; + + + FloatPoint.prototype.add = function(other) { + if (this.isFinite() && other.isFinite()) { + return FloatPoint.makeInstance(this.n + other.n); + } else { + if (isNaN(this.n) || isNaN(other.n)) { + return NaN; + } else if (this.isFinite() && ! other.isFinite()) { + return other; + } else if (!this.isFinite() && other.isFinite()) { + return this; + } else { + return ((sign(this) * sign(other) === 1) ? + this : NaN); + }; + } + }; + + FloatPoint.prototype.subtract = function(other) { + if (this.isFinite() && other.isFinite()) { + return FloatPoint.makeInstance(this.n - other.n); + } else if (isNaN(this.n) || isNaN(other.n)) { + return NaN; + } else if (! this.isFinite() && ! other.isFinite()) { + if (sign(this) === sign(other)) { + return NaN; + } else { + return this; + } + } else if (this.isFinite()) { + return multiply(other, -1); + } else { // other.isFinite() + return this; + } + }; + + + FloatPoint.prototype.negate = function() { + return FloatPoint.makeInstance(-this.n); + }; + + FloatPoint.prototype.multiply = function(other) { + return FloatPoint.makeInstance(this.n * other.n); + }; + + FloatPoint.prototype.divide = function(other) { + return FloatPoint.makeInstance(this.n / other.n); + }; + + + FloatPoint.prototype.toFixnum = function() { + return this.n; + }; + + FloatPoint.prototype.numerator = function() { + var stringRep = this.n.toString(); + var match = stringRep.match(/^(.*)\.(.*)$/); + if (match) { + var afterDecimal = parseInt(match[2]); + var factorToInt = Math.pow(10, match[2].length); + var extraFactor = _integerGcd(factorToInt, afterDecimal); + var multFactor = factorToInt / extraFactor; + return FloatPoint.makeInstance( Math.round(this.n * multFactor) ); + } else { + return this; + } + }; + + FloatPoint.prototype.denominator = function() { + var stringRep = this.n.toString(); + var match = stringRep.match(/^(.*)\.(.*)$/); + if (match) { + var afterDecimal = parseInt(match[2]); + var factorToInt = Math.pow(10, match[2].length); + var extraFactor = _integerGcd(factorToInt, afterDecimal); + return FloatPoint.makeInstance( Math.round(factorToInt/extraFactor) ); + } else { + return FloatPoint.makeInstance(1); + } + }; + + + FloatPoint.prototype.floor = function() { + return FloatPoint.makeInstance(Math.floor(this.n)); + }; + + FloatPoint.prototype.ceiling = function() { + return FloatPoint.makeInstance(Math.ceil(this.n)); + }; + + + FloatPoint.prototype.greaterThan = function(other) { + return this.n > other.n; + }; + + FloatPoint.prototype.greaterThanOrEqual = function(other) { + return this.n >= other.n; + }; + + FloatPoint.prototype.lessThan = function(other) { + return this.n < other.n; + }; + + FloatPoint.prototype.lessThanOrEqual = function(other) { + return this.n <= other.n; + }; + + + FloatPoint.prototype.integerSqrt = function() { + if (this === NEGATIVE_ZERO) { return this; } + if (isInteger(this)) { + if(this.n >= 0) { + return FloatPoint.makeInstance(Math.floor(Math.sqrt(this.n))); + } else { + return Complex.makeInstance( + INEXACT_ZERO, + FloatPoint.makeInstance(Math.floor(Math.sqrt(-this.n)))); + } + } else { + throwRuntimeError("integerSqrt: can only be applied to an integer", this); + } + }; + + FloatPoint.prototype.sqrt = function() { + if (this.n < 0) { + var result = Complex.makeInstance( + 0, + FloatPoint.makeInstance(Math.sqrt(-this.n))); + return result; + } else { + return FloatPoint.makeInstance(Math.sqrt(this.n)); + } + }; + + FloatPoint.prototype.abs = function() { + return FloatPoint.makeInstance(Math.abs(this.n)); + }; + + + + FloatPoint.prototype.log = function(){ + if (this.n < 0) + return (new Complex(this, 0)).log(); + else + return FloatPoint.makeInstance(Math.log(this.n)); + }; + + FloatPoint.prototype.angle = function(){ + if (0 === this.n) + return 0; + if (this.n > 0) + return 0; + else + return FloatPoint.pi; + }; + + FloatPoint.prototype.tan = function(){ + return FloatPoint.makeInstance(Math.tan(this.n)); + }; + + FloatPoint.prototype.atan = function(){ + return FloatPoint.makeInstance(Math.atan(this.n)); + }; + + FloatPoint.prototype.cos = function(){ + return FloatPoint.makeInstance(Math.cos(this.n)); + }; + + FloatPoint.prototype.sin = function(){ + return FloatPoint.makeInstance(Math.sin(this.n)); + }; + + FloatPoint.prototype.expt = function(a){ + if (this.n === 1) { + if (a.isFinite()) { + return this; + } else if (isNaN(a.n)){ + return this; + } else { + return this; + } + } else { + return FloatPoint.makeInstance(Math.pow(this.n, a.n)); + } + }; + + FloatPoint.prototype.exp = function(){ + return FloatPoint.makeInstance(Math.exp(this.n)); + }; + + FloatPoint.prototype.acos = function(){ + return FloatPoint.makeInstance(Math.acos(this.n)); + }; + + FloatPoint.prototype.asin = function(){ + return FloatPoint.makeInstance(Math.asin(this.n)); + }; + + FloatPoint.prototype.imaginaryPart = function(){ + return 0; + }; + + FloatPoint.prototype.realPart = function(){ + return this; + }; + + + FloatPoint.prototype.round = function(){ + if (isFinite(this.n)) { + if (this === NEGATIVE_ZERO) { + return this; + } + if (Math.abs(Math.floor(this.n) - this.n) === 0.5) { + if (Math.floor(this.n) % 2 === 0) + return FloatPoint.makeInstance(Math.floor(this.n)); + return FloatPoint.makeInstance(Math.ceil(this.n)); + } else { + return FloatPoint.makeInstance(Math.round(this.n)); + } + } else { + return this; + } + }; + + + FloatPoint.prototype.conjugate = function() { + return this; + }; + + FloatPoint.prototype.magnitude = FloatPoint.prototype.abs; + + + + ////////////////////////////////////////////////////////////////////// + // Complex numbers + ////////////////////////////////////////////////////////////////////// + + var Complex = function(r, i){ + this.r = r; + this.i = i; + }; + + // Constructs a complex number from two basic number r and i. r and i can + // either be plt.type.Rational or plt.type.FloatPoint. + Complex.makeInstance = function(r, i){ + if (i === undefined) { i = 0; } + if (isExact(i) && isInteger(i) && _integerIsZero(i)) { + return r; + } + if (isInexact(r) || isInexact(i)) { + r = toInexact(r); + i = toInexact(i); + } + return new Complex(r, i); + }; + + Complex.prototype.toString = function() { + var realPart = this.r.toString(), imagPart = this.i.toString(); + if (imagPart[0] === '-' || imagPart[0] === '+') { + return realPart + imagPart + 'i'; + } else { + return realPart + "+" + imagPart + 'i'; + } + }; + + + Complex.prototype.isFinite = function() { + return isSchemeNumberFinite(this.r) && isSchemeNumberFinite(this.i); + }; + + + Complex.prototype.isRational = function() { + return isRational(this.r) && eqv(this.i, 0); + }; + + Complex.prototype.isInteger = function() { + return (isInteger(this.r) && + eqv(this.i, 0)); + }; + + Complex.prototype.toExact = function() { + return Complex.makeInstance( toExact(this.r), toExact(this.i) ); + }; + + Complex.prototype.toInexact = function() { + return Complex.makeInstance(toInexact(this.r), + toInexact(this.i)); + }; + + + Complex.prototype.isExact = function() { + return isExact(this.r) && isExact(this.i); + }; + + + Complex.prototype.isInexact = function() { + return isInexact(this.r) || isInexact(this.i); + }; + + + Complex.prototype.level = 3; + + + Complex.prototype.liftTo = function(target){ + throwRuntimeError("Don't know how to lift Complex number", this, target); + }; + + Complex.prototype.equals = function(other) { + var result = ((other instanceof Complex) && + (equals(this.r, other.r)) && + (equals(this.i, other.i))); + return result; + }; + + + + Complex.prototype.greaterThan = function(other) { + if (! this.isReal() || ! other.isReal()) { + throwRuntimeError(">: expects argument of type real number", this, other); + } + return greaterThan(this.r, other.r); + }; + + Complex.prototype.greaterThanOrEqual = function(other) { + if (! this.isReal() || ! other.isReal()) { + throwRuntimeError(">=: expects argument of type real number", this, other); + } + return greaterThanOrEqual(this.r, other.r); + }; + + Complex.prototype.lessThan = function(other) { + if (! this.isReal() || ! other.isReal()) { + throwRuntimeError("<: expects argument of type real number", this, other); + } + return lessThan(this.r, other.r); + }; + + Complex.prototype.lessThanOrEqual = function(other) { + if (! this.isReal() || ! other.isReal()) { + throwRuntimeError("<=: expects argument of type real number", this, other); + } + return lessThanOrEqual(this.r, other.r); + }; + + + Complex.prototype.abs = function(){ + if (!equals(this.i, 0).valueOf()) + throwRuntimeError("abs: expects argument of type real number", this); + return abs(this.r); + }; + + Complex.prototype.toFixnum = function(){ + if (!equals(this.i, 0).valueOf()) + throwRuntimeError("toFixnum: expects argument of type real number", this); + return toFixnum(this.r); + }; + + Complex.prototype.numerator = function() { + if (!this.isReal()) + throwRuntimeError("numerator: can only be applied to real number", this); + return numerator(this.n); + }; + + + Complex.prototype.denominator = function() { + if (!this.isReal()) + throwRuntimeError("floor: can only be applied to real number", this); + return denominator(this.n); + }; + + Complex.prototype.add = function(other){ + return Complex.makeInstance( + add(this.r, other.r), + add(this.i, other.i)); + }; + + Complex.prototype.subtract = function(other){ + return Complex.makeInstance( + subtract(this.r, other.r), + subtract(this.i, other.i)); + }; + + Complex.prototype.negate = function() { + return Complex.makeInstance(negate(this.r), + negate(this.i)); + }; + + + Complex.prototype.multiply = function(other){ + // If the other value is real, just do primitive division + if (other.isReal()) { + return Complex.makeInstance( + multiply(this.r, other.r), + multiply(this.i, other.r)); + } + var r = subtract( + multiply(this.r, other.r), + multiply(this.i, other.i)); + var i = add( + multiply(this.r, other.i), + multiply(this.i, other.r)); + return Complex.makeInstance(r, i); + }; + + + + + + Complex.prototype.divide = function(other){ + var a, b, c, d, r, x, y; + // If the other value is real, just do primitive division + if (other.isReal()) { + return Complex.makeInstance( + divide(this.r, other.r), + divide(this.i, other.r)); + } + + if (this.isInexact() || other.isInexact()) { + // http://portal.acm.org/citation.cfm?id=1039814 + // We currently use Smith's method, though we should + // probably switch over to Priest's method. + a = this.r; + b = this.i; + c = other.r; + d = other.i; + if (lessThanOrEqual(abs(d), abs(c))) { + r = divide(d, c); + x = divide(add(a, multiply(b, r)), + add(c, multiply(d, r))); + y = divide(subtract(b, multiply(a, r)), + add(c, multiply(d, r))); + } else { + r = divide(c, d); + x = divide(add(multiply(a, r), b), + add(multiply(c, r), d)); + y = divide(subtract(multiply(b, r), a), + add(multiply(c, r), d)); + } + return Complex.makeInstance(x, y); + } else { + var con = conjugate(other); + var up = multiply(this, con); + + // Down is guaranteed to be real by this point. + var down = realPart(multiply(other, con)); + + var result = Complex.makeInstance( + divide(realPart(up), down), + divide(imaginaryPart(up), down)); + return result; + } + }; + + Complex.prototype.conjugate = function(){ + var result = Complex.makeInstance( + this.r, + subtract(0, this.i)); + + return result; + }; + + Complex.prototype.magnitude = function(){ + var sum = add( + multiply(this.r, this.r), + multiply(this.i, this.i)); + return sqrt(sum); + }; + + Complex.prototype.isReal = function(){ + return eqv(this.i, 0); + }; + + Complex.prototype.integerSqrt = function() { + if (isInteger(this)) { + return integerSqrt(this.r); + } else { + throwRuntimeError("integerSqrt: can only be applied to an integer", this); + } + }; + + Complex.prototype.sqrt = function(){ + if (this.isReal()) + return sqrt(this.r); + // http://en.wikipedia.org/wiki/Square_root#Square_roots_of_negative_and_complex_numbers + var r_plus_x = add(this.magnitude(), this.r); + + var r = sqrt(halve(r_plus_x)); + + var i = divide(this.i, sqrt(multiply(r_plus_x, 2))); + + + return Complex.makeInstance(r, i); + }; + + Complex.prototype.log = function(){ + var m = this.magnitude(); + var theta = this.angle(); + var result = add( + log(m), + timesI(theta)); + return result; + }; + + Complex.prototype.angle = function(){ + if (this.isReal()) { + return angle(this.r); + } + if (equals(0, this.r)) { + var tmp = halve(FloatPoint.pi); + return greaterThan(this.i, 0) ? + tmp : negate(tmp); + } else { + var tmp = atan(divide(abs(this.i), abs(this.r))); + if (greaterThan(this.r, 0)) { + return greaterThan(this.i, 0) ? + tmp : negate(tmp); + } else { + return greaterThan(this.i, 0) ? + subtract(FloatPoint.pi, tmp) : subtract(tmp, FloatPoint.pi); + } + } + }; + + var plusI = Complex.makeInstance(0, 1); + var minusI = Complex.makeInstance(0, -1); + + + Complex.prototype.tan = function() { + return divide(this.sin(), this.cos()); + }; + + Complex.prototype.atan = function(){ + if (equals(this, plusI) || + equals(this, minusI)) { + return neginf; + } + return multiply( + plusI, + multiply( + FloatPoint.makeInstance(0.5), + log(divide( + add(plusI, this), + add( + plusI, + subtract(0, this)))))); + }; + + Complex.prototype.cos = function(){ + if (this.isReal()) + return cos(this.r); + var iz = timesI(this); + var iz_negate = negate(iz); + + return halve(add(exp(iz), exp(iz_negate))); + }; + + Complex.prototype.sin = function(){ + if (this.isReal()) + return sin(this.r); + var iz = timesI(this); + var iz_negate = negate(iz); + var z2 = Complex.makeInstance(0, 2); + var exp_negate = subtract(exp(iz), exp(iz_negate)); + var result = divide(exp_negate, z2); + return result; + }; + + + Complex.prototype.expt = function(y){ + if (isExactInteger(y) && greaterThanOrEqual(y, 0)) { + return fastExpt(this, y); + } + var expo = multiply(y, this.log()); + return exp(expo); + }; + + Complex.prototype.exp = function(){ + var r = exp(this.r); + var cos_a = cos(this.i); + var sin_a = sin(this.i); + + return multiply( + r, + add(cos_a, timesI(sin_a))); + }; + + Complex.prototype.acos = function(){ + if (this.isReal()) + return acos(this.r); + var pi_half = halve(FloatPoint.pi); + var iz = timesI(this); + var root = sqrt(subtract(1, sqr(this))); + var l = timesI(log(add(iz, root))); + return add(pi_half, l); + }; + + Complex.prototype.asin = function(){ + if (this.isReal()) + return asin(this.r); + + var oneNegateThisSq = + subtract(1, sqr(this)); + var sqrtOneNegateThisSq = sqrt(oneNegateThisSq); + return multiply(2, atan(divide(this, + add(1, sqrtOneNegateThisSq)))); + }; + + Complex.prototype.ceiling = function(){ + if (!this.isReal()) + throwRuntimeError("ceiling: can only be applied to real number", this); + return ceiling(this.r); + }; + + Complex.prototype.floor = function(){ + if (!this.isReal()) + throwRuntimeError("floor: can only be applied to real number", this); + return floor(this.r); + }; + + Complex.prototype.imaginaryPart = function(){ + return this.i; + }; + + Complex.prototype.realPart = function(){ + return this.r; + }; + + Complex.prototype.round = function(){ + if (!this.isReal()) + throwRuntimeError("round: can only be applied to real number", this); + return round(this.r); + }; + + + + var rationalRegexp = new RegExp("^([+-]?\\d+)/(\\d+)$"); + var complexRegexp = new RegExp("^([+-]?[\\d\\w/\\.]*)([+-])([\\d\\w/\\.]*)i$"); + var digitRegexp = new RegExp("^[+-]?\\d+$"); + var flonumRegexp = new RegExp("^([+-]?\\d*)\\.(\\d*)$"); + var scientificPattern = new RegExp("^([+-]?\\d*\\.?\\d*)[Ee](\\+?\\d+)$"); + + // fromString: string -> (scheme-number | false) + var fromString = function(x) { + var aMatch = x.match(rationalRegexp); + if (aMatch) { + return Rational.makeInstance(fromString(aMatch[1]), + fromString(aMatch[2])); + } + + var cMatch = x.match(complexRegexp); + if (cMatch) { + return Complex.makeInstance(fromString(cMatch[1] || "0"), + fromString(cMatch[2] + (cMatch[3] || "1"))); + } + + // Floating point tests + if (x === '+nan.0' || x === '-nan.0') + return FloatPoint.nan; + if (x === '+inf.0') + return FloatPoint.inf; + if (x === '-inf.0') + return FloatPoint.neginf; + if (x === "-0.0") { + return NEGATIVE_ZERO; + } + if (x.match(flonumRegexp) || x.match(scientificPattern)) { + return FloatPoint.makeInstance(Number(x)); + } + + // Finally, integer tests. + if (x.match(digitRegexp)) { + var n = Number(x); + if (isOverflow(n)) { + return makeBignum(x); + } else { + return n; + } + } else { + return false; + } + }; + + + + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + + // The code below comes from Tom Wu's BigInteger implementation: + + // Copyright (c) 2005 Tom Wu + // All Rights Reserved. + // See "LICENSE" for details. + + // Basic JavaScript BN library - subset useful for RSA encryption. + + // Bits per digit + var dbits; + + // JavaScript engine analysis + var canary = 0xdeadbeefcafe; + var j_lm = ((canary&0xffffff)==0xefcafe); + + // (public) Constructor + function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); + } + + // return new, unset BigInteger + function nbi() { return new BigInteger(null); } + + // am: Compute w_j += (x*this_i), propagate carries, + // c is initial carry, returns final carry. + // c < 3*dvalue, x < 2*dvalue, this_i < dvalue + // We need to select the fastest one that works in this environment. + + // am1: use a single mult and divide to get the high bits, + // max digit bits should be 26 because + // max internal value = 2*dvalue^2-2*dvalue (< 2^53) + function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; + } + // am2 avoids a big mult-and-extract completely. + // Max digit bits should be <= 30 because we do bitwise ops + // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) + function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; + } + // Alternately, set max digit bits to 28 since some + // browsers slow down when dealing with 32-bit numbers. + function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; + } + if(j_lm && (typeof(navigator) !== 'undefined' && navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; + } + else if(j_lm && (typeof(navigator) !== 'undefined' && navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; + } + else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; + } + + BigInteger.prototype.DB = dbits; + BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; + } + + // (protected) set from integer value x, -DV <= x < DV + function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; + } + + // return bigint initialized to value + function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + + // (protected) set from string and radix + function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; + } + + // (public) return string representation in given radix + function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r.push(int2char(d)); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r.push(int2char(d)); + } + } + return m?r.join(""):"0"; + } + + // (public) -this + function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + + // (public) |this| + function bnAbs() { return (this.s<0)?this.negate():this; } + + // (public) return + if this > a, - if this < a, 0 if equal + function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + if ( this.s < 0 ) { + r = a.t - i; + } + else { + r = i - a.t; + } + if(r != 0) return r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; + } + + // returns bit length of the integer x + function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; + } + + // (public) return the number of bits in "this" + function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); + } + + // (protected) r = this << n*DB + function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; + } + + // (protected) r = this >> n*DB + function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; + } + + // (protected) r = this << n + function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); + } + + // (protected) r = this >> n + function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); + } + + // (protected) r = this * a, r != this,a (HAC 14.12) + // "this" should be the larger one if appropriate. + function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); + } + + // (protected) r = this^2, r != this (HAC 14.16) + function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); + } + + + // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) + // r != q, this != m. q or r may be null. + function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); + } + + // (public) this mod a + function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; + } + + // Modular reduction using "classic" algorithm + function Classic(m) { this.m = m; } + function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; + } + function cRevert(x) { return x; } + function cReduce(x) { x.divRemTo(this.m,null,x); } + function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + Classic.prototype.convert = cConvert; + Classic.prototype.revert = cRevert; + Classic.prototype.reduce = cReduce; + Classic.prototype.mulTo = cMulTo; + Classic.prototype.sqrTo = cSqrTo; + + // (protected) return "-1/this % 2^DB"; useful for Mont. reduction + // justification: + // xy == 1 (mod m) + // xy = 1+km + // xy(2-xy) = (1+km)(1-km) + // x[y(2-xy)] = 1-k^2m^2 + // x[y(2-xy)] == 1 (mod m^2) + // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 + // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. + // JS multiply "overflows" differently from C/C++, so care is needed here. + function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; + } + + // Montgomery reduction + function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; + } + + // xR mod m + function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; + } + + // x/R mod m + function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; + } + + // x = x/R mod m (HAC 14.32) + function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); + } + + // r = "x^2/R mod m"; x != r + function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + // r = "xy/R mod m"; x,y != r + function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + + Montgomery.prototype.convert = montConvert; + Montgomery.prototype.revert = montRevert; + Montgomery.prototype.reduce = montReduce; + Montgomery.prototype.mulTo = montMulTo; + Montgomery.prototype.sqrTo = montSqrTo; + + // (protected) true iff this is even + function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + + // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) + function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); + } + + // (public) this^e % m, 0 <= e < 2^32 + function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); + } + + // protected + BigInteger.prototype.copyTo = bnpCopyTo; + BigInteger.prototype.fromInt = bnpFromInt; + BigInteger.prototype.fromString = bnpFromString; + BigInteger.prototype.clamp = bnpClamp; + BigInteger.prototype.dlShiftTo = bnpDLShiftTo; + BigInteger.prototype.drShiftTo = bnpDRShiftTo; + BigInteger.prototype.lShiftTo = bnpLShiftTo; + BigInteger.prototype.rShiftTo = bnpRShiftTo; + BigInteger.prototype.subTo = bnpSubTo; + BigInteger.prototype.multiplyTo = bnpMultiplyTo; + BigInteger.prototype.squareTo = bnpSquareTo; + BigInteger.prototype.divRemTo = bnpDivRemTo; + BigInteger.prototype.invDigit = bnpInvDigit; + BigInteger.prototype.isEven = bnpIsEven; + BigInteger.prototype.exp = bnpExp; + + // public + BigInteger.prototype.toString = bnToString; + BigInteger.prototype.negate = bnNegate; + BigInteger.prototype.abs = bnAbs; + BigInteger.prototype.compareTo = bnCompareTo; + BigInteger.prototype.bitLength = bnBitLength; + BigInteger.prototype.mod = bnMod; + BigInteger.prototype.modPowInt = bnModPowInt; + + // "constants" + BigInteger.ZERO = nbv(0); + BigInteger.ONE = nbv(1); + + // Copyright (c) 2005-2009 Tom Wu + // All Rights Reserved. + // See "LICENSE" for details. + + // Extended JavaScript BN functions, required for RSA private ops. + + // Version 1.1: new BigInteger("0", 10) returns "proper" zero + + // (public) + function bnClone() { var r = nbi(); this.copyTo(r); return r; } + + // (public) return value as integer + function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + + // (public) return value as short (assumes DB>=16) + function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + + // (protected) return x s.t. r^x < DV + function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + + // (public) 0 if this == 0, 1 if this > 0 + function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; + } + + // (protected) convert to radix string + function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; + } + + // (protected) convert from radix string + function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); + } + + // (protected) alternate constructor + function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = [], t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + if((d&0x80) != 0) d |= -256; + if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; + } + + function bnEquals(a) { return(this.compareTo(a)==0); } + function bnMin(a) { return(this.compareTo(a)<0)?this:a; } + function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + + // (protected) r = this op a (bitwise) + function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); + } + + // (public) this & a + function op_and(x,y) { return x&y; } + function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + + // (public) this | a + function op_or(x,y) { return x|y; } + function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + + // (public) this ^ a + function op_xor(x,y) { return x^y; } + function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + + // (public) this & ~a + function op_andnot(x,y) { return x&~y; } + function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + + // (public) ~this + function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; + } + + // (public) this << n + function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; + } + + // (public) this >> n + function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; + } + + // return index of lowest 1-bit in x, x < 2^31 + function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; + } + + // (public) returns index of lowest 1-bit (or -1 if none) + function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; + } + + // return number of 1 bits in x + function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; + } + + // (public) return number of set bits + function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; + } + + // (public) true iff nth bit is set + function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); + } + + // (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); + } + + // (public) this + a + function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + + // (public) this - a + function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + + // (public) this * a + function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + + // (public) this / a + function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + + // (public) this % a + function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + + // (public) [this/a,this%a] + function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return [q,r]; + } + + // (protected) this *= n, this >= 0, 1 < n < DV + function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); + } + + // (protected) this += n << w words, this >= 0 + function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } + } + + // A "null" reducer + function NullExp() {} + function nNop(x) { return x; } + function nMulTo(x,y,r) { x.multiplyTo(y,r); } + function nSqrTo(x,r) { x.squareTo(r); } + + NullExp.prototype.convert = nNop; + NullExp.prototype.revert = nNop; + NullExp.prototype.mulTo = nMulTo; + NullExp.prototype.sqrTo = nSqrTo; + + // (public) this^e + function bnPow(e) { return this.exp(e,new NullExp()); } + + // (protected) r = lower n words of "this * a", a.t <= n + // "this" should be the larger one if appropriate. + function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); + } + + // (protected) r = "this * a" without lower n words, n > 0 + // "this" should be the larger one if appropriate. + function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); + } + + // Barrett modular reduction + function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; + } + + function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } + } + + function barrettRevert(x) { return x; } + + // x = x mod m (HAC 14.42) + function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); + } + + // r = x^2 mod m; x != r + function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + + // r = x*y mod m; x,y != r + function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + + Barrett.prototype.convert = barrettConvert; + Barrett.prototype.revert = barrettRevert; + Barrett.prototype.reduce = barrettReduce; + Barrett.prototype.mulTo = barrettMulTo; + Barrett.prototype.sqrTo = barrettSqrTo; + + // (public) this^e % m (HAC 14.85) + function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = [], n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; + } + + // (protected) this % n, n < 2^26 + function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; + } + + // (public) 1/this % m (HAC 14.61) + function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; + } + + var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; + var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + + // (public) test primality with certainty >= 1-.5^t + function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); + } + + // (protected) true if probably prime (HAC 4.24, Miller-Rabin) + function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + a.fromInt(lowprimes[i]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; + } + + + + // protected + BigInteger.prototype.chunkSize = bnpChunkSize; + BigInteger.prototype.toRadix = bnpToRadix; + BigInteger.prototype.fromRadix = bnpFromRadix; + BigInteger.prototype.fromNumber = bnpFromNumber; + BigInteger.prototype.bitwiseTo = bnpBitwiseTo; + BigInteger.prototype.changeBit = bnpChangeBit; + BigInteger.prototype.addTo = bnpAddTo; + BigInteger.prototype.dMultiply = bnpDMultiply; + BigInteger.prototype.dAddOffset = bnpDAddOffset; + BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; + BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; + BigInteger.prototype.modInt = bnpModInt; + BigInteger.prototype.millerRabin = bnpMillerRabin; + + // public + BigInteger.prototype.clone = bnClone; + BigInteger.prototype.intValue = bnIntValue; + BigInteger.prototype.byteValue = bnByteValue; + BigInteger.prototype.shortValue = bnShortValue; + BigInteger.prototype.signum = bnSigNum; + BigInteger.prototype.toByteArray = bnToByteArray; + BigInteger.prototype.equals = bnEquals; + BigInteger.prototype.min = bnMin; + BigInteger.prototype.max = bnMax; + BigInteger.prototype.and = bnAnd; + BigInteger.prototype.or = bnOr; + BigInteger.prototype.xor = bnXor; + BigInteger.prototype.andNot = bnAndNot; + BigInteger.prototype.not = bnNot; + BigInteger.prototype.shiftLeft = bnShiftLeft; + BigInteger.prototype.shiftRight = bnShiftRight; + BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; + BigInteger.prototype.bitCount = bnBitCount; + BigInteger.prototype.testBit = bnTestBit; + BigInteger.prototype.setBit = bnSetBit; + BigInteger.prototype.clearBit = bnClearBit; + BigInteger.prototype.flipBit = bnFlipBit; + BigInteger.prototype.add = bnAdd; + BigInteger.prototype.subtract = bnSubtract; + BigInteger.prototype.multiply = bnMultiply; + BigInteger.prototype.divide = bnDivide; + BigInteger.prototype.remainder = bnRemainder; + BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; + BigInteger.prototype.modPow = bnModPow; + BigInteger.prototype.modInverse = bnModInverse; + BigInteger.prototype.pow = bnPow; + BigInteger.prototype.gcd = bnGCD; + BigInteger.prototype.isProbablePrime = bnIsProbablePrime; + + // BigInteger interfaces not implemented in jsbn: + + // BigInteger(int signum, byte[] magnitude) + // double doubleValue() + // float floatValue() + // int hashCode() + // long longValue() + // static BigInteger valueOf(long val) + + + + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////// + // END OF copy-and-paste of jsbn. + + + + BigInteger.NEGATIVE_ONE = BigInteger.ONE.negate(); + + + // Other methods we need to add for compatibilty with js-numbers numeric tower. + + // add is implemented above. + // subtract is implemented above. + // multiply is implemented above. + // equals is implemented above. + // abs is implemented above. + // negate is defined above. + + // makeBignum: string -> BigInteger + var makeBignum = function(s) { + if (typeof(s) === 'number') { s = s + ''; } + s = expandExponent(s); + return new BigInteger(s, 10); + }; + + var zerostring = function(n) { + var buf = []; + for (var i = 0; i < n; i++) { + buf.push('0'); + } + return buf.join(''); + }; + + + BigInteger.prototype.level = 0; + BigInteger.prototype.liftTo = function(target) { + if (target.level === 1) { + return new Rational(this, 1); + } + if (target.level === 2) { + var fixrep = this.toFixnum(); + if (fixrep === Number.POSITIVE_INFINITY) + return TOO_POSITIVE_TO_REPRESENT; + if (fixrep === Number.NEGATIVE_INFINITY) + return TOO_NEGATIVE_TO_REPRESENT; + return new FloatPoint(fixrep); + } + if (target.level === 3) { + return new Complex(this, 0); + } + return throwRuntimeError("invalid level for BigInteger lift", this, target); + }; + + BigInteger.prototype.isFinite = function() { + return true; + }; + + BigInteger.prototype.isInteger = function() { + return true; + }; + + BigInteger.prototype.isRational = function() { + return true; + }; + + BigInteger.prototype.isReal = function() { + return true; + }; + + BigInteger.prototype.isExact = function() { + return true; + }; + + BigInteger.prototype.isInexact = function() { + return false; + }; + + BigInteger.prototype.toExact = function() { + return this; + }; + + BigInteger.prototype.toInexact = function() { + return FloatPoint.makeInstance(this.toFixnum()); + }; + + BigInteger.prototype.toFixnum = function() { + var result = 0, str = this.toString(), i; + if (str[0] === '-') { + for (i=1; i < str.length; i++) { + result = result * 10 + Number(str[i]); + } + return -result; + } else { + for (i=0; i < str.length; i++) { + result = result * 10 + Number(str[i]); + } + return result; + } + }; + + + BigInteger.prototype.greaterThan = function(other) { + return this.compareTo(other) > 0; + }; + + BigInteger.prototype.greaterThanOrEqual = function(other) { + return this.compareTo(other) >= 0; + }; + + BigInteger.prototype.lessThan = function(other) { + return this.compareTo(other) < 0; + }; + + BigInteger.prototype.lessThanOrEqual = function(other) { + return this.compareTo(other) <= 0; + }; + + // divide: scheme-number -> scheme-number + // WARNING NOTE: we override the old version of divide. + BigInteger.prototype.divide = function(other) { + var quotientAndRemainder = bnDivideAndRemainder.call(this, other); + if (quotientAndRemainder[1].compareTo(BigInteger.ZERO) === 0) { + return quotientAndRemainder[0]; + } else { + var result = add(quotientAndRemainder[0], + Rational.makeInstance(quotientAndRemainder[1], other)); + return result; + } + }; + + BigInteger.prototype.numerator = function() { + return this; + }; + + BigInteger.prototype.denominator = function() { + return 1; + }; + + + (function() { + // Classic implementation of Newton-Ralphson square-root search, + // adapted for integer-sqrt. + // http://en.wikipedia.org/wiki/Newton's_method#Square_root_of_a_number + var searchIter = function(n, guess) { + while(!(lessThanOrEqual(sqr(guess),n) && + lessThan(n,sqr(add(guess, 1))))) { + guess = floor(divide(add(guess, + floor(divide(n, guess))), + 2)); + } + return guess; + }; + + // integerSqrt: -> scheme-number + BigInteger.prototype.integerSqrt = function() { + var n; + if(sign(this) >= 0) { + return searchIter(this, this); + } else { + n = this.negate(); + return Complex.makeInstance(0, searchIter(n, n)); + } + }; + })(); + + + (function() { + // Get an approximation using integerSqrt, and then start another + // Newton-Ralphson search if necessary. + BigInteger.prototype.sqrt = function() { + var approx = this.integerSqrt(), fix; + if (eqv(sqr(approx), this)) { + return approx; + } + fix = toFixnum(this); + if (isFinite(fix)) { + if (fix >= 0) { + return FloatPoint.makeInstance(Math.sqrt(fix)); + } else { + return Complex.makeInstance( + 0, + FloatPoint.makeInstance(Math.sqrt(-fix))); + } + } else { + return approx; + } + }; + })(); + + + + + + // sqrt: -> scheme-number + // http://en.wikipedia.org/wiki/Newton's_method#Square_root_of_a_number + // Produce the square root. + + // floor: -> scheme-number + // Produce the floor. + BigInteger.prototype.floor = function() { + return this; + } + + // ceiling: -> scheme-number + // Produce the ceiling. + BigInteger.prototype.ceiling = function() { + return this; + } + + // conjugate: -> scheme-number + // Produce the conjugate. + + // magnitude: -> scheme-number + // Produce the magnitude. + + // log: -> scheme-number + // Produce the log. + + // angle: -> scheme-number + // Produce the angle. + + // atan: -> scheme-number + // Produce the arc tangent. + + // cos: -> scheme-number + // Produce the cosine. + + // sin: -> scheme-number + // Produce the sine. + + + // expt: scheme-number -> scheme-number + // Produce the power to the input. + BigInteger.prototype.expt = function(n) { + return bnPow.call(this, n); + }; + + + + // exp: -> scheme-number + // Produce e raised to the given power. + + // acos: -> scheme-number + // Produce the arc cosine. + + // asin: -> scheme-number + // Produce the arc sine. + + BigInteger.prototype.imaginaryPart = function() { + return 0; + } + BigInteger.prototype.realPart = function() { + return this; + } + + // round: -> scheme-number + // Round to the nearest integer. + + + + + + ////////////////////////////////////////////////////////////////////// + // toRepeatingDecimal: jsnum jsnum {limit: number}? -> [string, string, string] + // + // Given the numerator and denominator parts of a rational, + // produces the repeating-decimal representation, where the first + // part are the digits before the decimal, the second are the + // non-repeating digits after the decimal, and the third are the + // remaining repeating decimals. + // + // An optional limit on the decimal expansion can be provided, in which + // case the search cuts off if we go past the limit. + // If this happens, the third argument returned becomes '...' to indicate + // that the search was prematurely cut off. + var toRepeatingDecimal = (function() { + var getResidue = function(r, d, limit) { + var digits = []; + var seenRemainders = {}; + seenRemainders[r] = true; + while(true) { + if (limit-- <= 0) { + return [digits.join(''), '...'] + } + + var nextDigit = quotient( + multiply(r, 10), d); + var nextRemainder = remainder( + multiply(r, 10), + d); + digits.push(nextDigit.toString()); + if (seenRemainders[nextRemainder]) { + r = nextRemainder; + break; + } else { + seenRemainders[nextRemainder] = true; + r = nextRemainder; + } + } + + var firstRepeatingRemainder = r; + var repeatingDigits = []; + while (true) { + var nextDigit = quotient(multiply(r, 10), d); + var nextRemainder = remainder( + multiply(r, 10), + d); + repeatingDigits.push(nextDigit.toString()); + if (equals(nextRemainder, firstRepeatingRemainder)) { + break; + } else { + r = nextRemainder; + } + }; + + var digitString = digits.join(''); + var repeatingDigitString = repeatingDigits.join(''); + + while (digitString.length >= repeatingDigitString.length && + (digitString.substring( + digitString.length - repeatingDigitString.length) + === repeatingDigitString)) { + digitString = digitString.substring( + 0, digitString.length - repeatingDigitString.length); + } + + return [digitString, repeatingDigitString]; + + }; + + return function(n, d, options) { + // default limit on decimal expansion; can be overridden + var limit = 512; + if (options && typeof(options.limit) !== 'undefined') { + limit = options.limit; + } + if (! isInteger(n)) { + throwRuntimeError('toRepeatingDecimal: n ' + n.toString() + + " is not an integer."); + } + if (! isInteger(d)) { + throwRuntimeError('toRepeatingDecimal: d ' + d.toString() + + " is not an integer."); + } + if (equals(d, 0)) { + throwRuntimeError('toRepeatingDecimal: d equals 0'); + } + if (lessThan(d, 0)) { + throwRuntimeError('toRepeatingDecimal: d < 0'); + } + var sign = (lessThan(n, 0) ? "-" : ""); + n = abs(n); + var beforeDecimalPoint = sign + quotient(n, d); + var afterDecimals = getResidue(remainder(n, d), d, limit); + return [beforeDecimalPoint].concat(afterDecimals); + }; + })(); + ////////////////////////////////////////////////////////////////////// + + + + + // External interface of js-numbers: + + Numbers['fromFixnum'] = fromFixnum; + Numbers['fromString'] = fromString; + Numbers['makeBignum'] = makeBignum; + Numbers['makeRational'] = Rational.makeInstance; + Numbers['makeFloat'] = FloatPoint.makeInstance; + Numbers['makeComplex'] = Complex.makeInstance; + Numbers['makeComplexPolar'] = makeComplexPolar; + + Numbers['pi'] = FloatPoint.pi; + Numbers['e'] = FloatPoint.e; + Numbers['nan'] = FloatPoint.nan; + Numbers['negative_inf'] = FloatPoint.neginf; + Numbers['inf'] = FloatPoint.inf; + Numbers['negative_one'] = -1; // Rational.NEGATIVE_ONE; + Numbers['zero'] = 0; // Rational.ZERO; + Numbers['one'] = 1; // Rational.ONE; + Numbers['i'] = plusI; + Numbers['negative_i'] = minusI; + Numbers['negative_zero'] = NEGATIVE_ZERO; + + Numbers['onThrowRuntimeError'] = onThrowRuntimeError; + Numbers['isSchemeNumber'] = isSchemeNumber; + Numbers['isRational'] = isRational; + Numbers['isReal'] = isReal; + Numbers['isExact'] = isExact; + Numbers['isInexact'] = isInexact; + Numbers['isInteger'] = isInteger; + + Numbers['toFixnum'] = toFixnum; + Numbers['toExact'] = toExact; + Numbers['toInexact'] = toInexact; + Numbers['add'] = add; + Numbers['subtract'] = subtract; + Numbers['multiply'] = multiply; + Numbers['divide'] = divide; + Numbers['equals'] = equals; + Numbers['eqv'] = eqv; + Numbers['approxEquals'] = approxEquals; + Numbers['greaterThanOrEqual'] = greaterThanOrEqual; + Numbers['lessThanOrEqual'] = lessThanOrEqual; + Numbers['greaterThan'] = greaterThan; + Numbers['lessThan'] = lessThan; + Numbers['expt'] = expt; + Numbers['exp'] = exp; + Numbers['modulo'] = modulo; + Numbers['numerator'] = numerator; + Numbers['denominator'] = denominator; + Numbers['integerSqrt'] = integerSqrt; + Numbers['sqrt'] = sqrt; + Numbers['abs'] = abs; + Numbers['quotient'] = quotient; + Numbers['remainder'] = remainder; + Numbers['floor'] = floor; + Numbers['ceiling'] = ceiling; + Numbers['conjugate'] = conjugate; + Numbers['magnitude'] = magnitude; + Numbers['log'] = log; + Numbers['angle'] = angle; + Numbers['tan'] = tan; + Numbers['atan'] = atan; + Numbers['cos'] = cos; + Numbers['sin'] = sin; + Numbers['tan'] = tan; + Numbers['acos'] = acos; + Numbers['asin'] = asin; + Numbers['cosh'] = cosh; + Numbers['sinh'] = sinh; + Numbers['imaginaryPart'] = imaginaryPart; + Numbers['realPart'] = realPart; + Numbers['round'] = round; + Numbers['sqr'] = sqr; + Numbers['gcd'] = gcd; + Numbers['lcm'] = lcm; + + Numbers['toRepeatingDecimal'] = toRepeatingDecimal; + + + + // The following exposes the class representations for easier + // integration with other projects. + Numbers['BigInteger'] = BigInteger; + Numbers['Rational'] = Rational; + Numbers['FloatPoint'] = FloatPoint; + Numbers['Complex'] = Complex; + + Numbers['MIN_FIXNUM'] = MIN_FIXNUM; + Numbers['MAX_FIXNUM'] = MAX_FIXNUM; + +})(); diff --git a/js-assembler/runtime-src/jshashtable-2.1_src.js b/js-assembler/runtime-src/jshashtable-2.1_src.js new file mode 100644 index 0000000..086f3de --- /dev/null +++ b/js-assembler/runtime-src/jshashtable-2.1_src.js @@ -0,0 +1,370 @@ +/** + * Copyright 2010 Tim Down. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * jshashtable + * + * jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable + * in the global scope. + * + * Author: Tim Down + * Version: 2.1 + * Build date: 21 March 2010 + * Website: http://www.timdown.co.uk/jshashtable + */ + +var Hashtable = (function() { + var FUNCTION = "function"; + + var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ? + function(arr, idx) { + arr.splice(idx, 1); + } : + + function(arr, idx) { + var itemsAfterDeleted, i, len; + if (idx === arr.length - 1) { + arr.length = idx; + } else { + itemsAfterDeleted = arr.slice(idx + 1); + arr.length = idx; + for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) { + arr[idx + i] = itemsAfterDeleted[i]; + } + } + }; + + function hashObject(obj) { + var hashCode; + if (typeof obj == "string") { + return obj; + } else if (typeof obj.hashCode == FUNCTION) { + // Check the hashCode method really has returned a string + hashCode = obj.hashCode(); + return (typeof hashCode == "string") ? hashCode : hashObject(hashCode); + } else if (typeof obj.toString == FUNCTION) { + return obj.toString(); + } else { + try { + return String(obj); + } catch (ex) { + // For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when + // passed to String() + return Object.prototype.toString.call(obj); + } + } + } + + function equals_fixedValueHasEquals(fixedValue, variableValue) { + return fixedValue.equals(variableValue); + } + + function equals_fixedValueNoEquals(fixedValue, variableValue) { + return (typeof variableValue.equals == FUNCTION) ? + variableValue.equals(fixedValue) : (fixedValue === variableValue); + } + + function createKeyValCheck(kvStr) { + return function(kv) { + if (kv === null) { + throw new Error("null is not a valid " + kvStr); + } else if (typeof kv == "undefined") { + throw new Error(kvStr + " must not be undefined"); + } + }; + } + + var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); + + /*----------------------------------------------------------------------------------------------------------------*/ + + function Bucket(hash, firstKey, firstValue, equalityFunction) { + this[0] = hash; + this.entries = []; + this.addEntry(firstKey, firstValue); + + if (equalityFunction !== null) { + this.getEqualityFunction = function() { + return equalityFunction; + }; + } + } + + var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; + + function createBucketSearcher(mode) { + return function(key) { + var i = this.entries.length, entry, equals = this.getEqualityFunction(key); + while (i--) { + entry = this.entries[i]; + if ( equals(key, entry[0]) ) { + switch (mode) { + case EXISTENCE: + return true; + case ENTRY: + return entry; + case ENTRY_INDEX_AND_VALUE: + return [ i, entry[1] ]; + } + } + } + return false; + }; + } + + function createBucketLister(entryProperty) { + return function(aggregatedArr) { + var startIndex = aggregatedArr.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + aggregatedArr[startIndex + i] = this.entries[i][entryProperty]; + } + }; + } + + Bucket.prototype = { + getEqualityFunction: function(searchValue) { + return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; + }, + + getEntryForKey: createBucketSearcher(ENTRY), + + getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE), + + removeEntryForKey: function(key) { + var result = this.getEntryAndIndexForKey(key); + if (result) { + arrayRemoveAt(this.entries, result[0]); + return result[1]; + } + return null; + }, + + addEntry: function(key, value) { + this.entries[this.entries.length] = [key, value]; + }, + + keys: createBucketLister(0), + + values: createBucketLister(1), + + getEntries: function(entries) { + var startIndex = entries.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + // Clone the entry stored in the bucket before adding to array + entries[startIndex + i] = this.entries[i].slice(0); + } + }, + + containsKey: createBucketSearcher(EXISTENCE), + + containsValue: function(value) { + var i = this.entries.length; + while (i--) { + if ( value === this.entries[i][1] ) { + return true; + } + } + return false; + } + }; + + /*----------------------------------------------------------------------------------------------------------------*/ + + // Supporting functions for searching hashtable buckets + + function searchBuckets(buckets, hash) { + var i = buckets.length, bucket; + while (i--) { + bucket = buckets[i]; + if (hash === bucket[0]) { + return i; + } + } + return null; + } + + function getBucketForHash(bucketsByHash, hash) { + var bucket = bucketsByHash[hash]; + + // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype + return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; + } + + /*----------------------------------------------------------------------------------------------------------------*/ + + function Hashtable(hashingFunctionParam, equalityFunctionParam) { + var that = this; + var buckets = []; + var bucketsByHash = {}; + + var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject; + var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null; + + this.put = function(key, value) { + checkKey(key); + checkValue(value); + var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null; + + // Check if a bucket exists for the bucket key + bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it already contains this key + bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so replace old value and we're done. + oldValue = bucketEntry[1]; + bucketEntry[1] = value; + } else { + // The bucket does not contain an entry for this key, so add one + bucket.addEntry(key, value); + } + } else { + // No bucket exists for the key, so create one and put our key/value mapping in + bucket = new Bucket(hash, key, value, equalityFunction); + buckets[buckets.length] = bucket; + bucketsByHash[hash] = bucket; + } + return oldValue; + }; + + this.get = function(key) { + checkKey(key); + + var hash = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it contains this key + var bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so return the value. + return bucketEntry[1]; + } + } + return null; + }; + + this.containsKey = function(key) { + checkKey(key); + var bucketKey = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, bucketKey); + + return bucket ? bucket.containsKey(key) : false; + }; + + this.containsValue = function(value) { + checkValue(value); + var i = buckets.length; + while (i--) { + if (buckets[i].containsValue(value)) { + return true; + } + } + return false; + }; + + this.clear = function() { + buckets.length = 0; + bucketsByHash = {}; + }; + + this.isEmpty = function() { + return !buckets.length; + }; + + var createBucketAggregator = function(bucketFuncName) { + return function() { + var aggregated = [], i = buckets.length; + while (i--) { + buckets[i][bucketFuncName](aggregated); + } + return aggregated; + }; + }; + + this.keys = createBucketAggregator("keys"); + this.values = createBucketAggregator("values"); + this.entries = createBucketAggregator("getEntries"); + + this.remove = function(key) { + checkKey(key); + + var hash = hashingFunction(key), bucketIndex, oldValue = null; + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + + if (bucket) { + // Remove entry from this bucket for this key + oldValue = bucket.removeEntryForKey(key); + if (oldValue !== null) { + // Entry was removed, so check if bucket is empty + if (!bucket.entries.length) { + // Bucket is empty, so remove it from the bucket collections + bucketIndex = searchBuckets(buckets, hash); + arrayRemoveAt(buckets, bucketIndex); + delete bucketsByHash[hash]; + } + } + } + return oldValue; + }; + + this.size = function() { + var total = 0, i = buckets.length; + while (i--) { + total += buckets[i].entries.length; + } + return total; + }; + + this.each = function(callback) { + var entries = that.entries(), i = entries.length, entry; + while (i--) { + entry = entries[i]; + callback(entry[0], entry[1]); + } + }; + + this.putAll = function(hashtable, conflictCallback) { + var entries = hashtable.entries(); + var entry, key, value, thisValue, i = entries.length; + var hasConflictCallback = (typeof conflictCallback == FUNCTION); + while (i--) { + entry = entries[i]; + key = entry[0]; + value = entry[1]; + + // Check for a conflict. The default behaviour is to overwrite the value for an existing key + if ( hasConflictCallback && (thisValue = that.get(key)) ) { + value = conflictCallback(key, thisValue, value); + } + that.put(key, value); + } + }; + + this.clone = function() { + var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam); + clone.putAll(that); + return clone; + }; + } + + return Hashtable; +})(); \ No newline at end of file diff --git a/js-assembler/runtime-src/json2.js b/js-assembler/runtime-src/json2.js new file mode 100644 index 0000000..a1a3b17 --- /dev/null +++ b/js-assembler/runtime-src/json2.js @@ -0,0 +1,482 @@ +/* + http://www.JSON.org/json2.js + 2010-03-20 + + Public Domain. + + NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + + See http://www.JSON.org/js.html + + + This code should be minified before deployment. + See http://javascript.crockford.com/jsmin.html + + USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO + NOT CONTROL. + + + This file creates a global JSON object containing two methods: stringify + and parse. + + JSON.stringify(value, replacer, space) + value any JavaScript value, usually an object or array. + + replacer an optional parameter that determines how object + values are stringified for objects. It can be a + function or an array of strings. + + space an optional parameter that specifies the indentation + of nested structures. If it is omitted, the text will + be packed without extra whitespace. If it is a number, + it will specify the number of spaces to indent at each + level. If it is a string (such as '\t' or ' '), + it contains the characters used to indent at each level. + + This method produces a JSON text from a JavaScript value. + + When an object value is found, if the object contains a toJSON + method, its toJSON method will be called and the result will be + stringified. A toJSON method does not serialize: it returns the + value represented by the name/value pair that should be serialized, + or undefined if nothing should be serialized. The toJSON method + will be passed the key associated with the value, and this will be + bound to the value + + For example, this would serialize Dates as ISO strings. + + Date.prototype.toJSON = function (key) { + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + return this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z'; + }; + + You can provide an optional replacer method. It will be passed the + key and value of each member, with this bound to the containing + object. The value that is returned from your method will be + serialized. If your method returns undefined, then the member will + be excluded from the serialization. + + If the replacer parameter is an array of strings, then it will be + used to select the members to be serialized. It filters the results + such that only members with keys listed in the replacer array are + stringified. + + Values that do not have JSON representations, such as undefined or + functions, will not be serialized. Such values in objects will be + dropped; in arrays they will be replaced with null. You can use + a replacer function to replace those with JSON values. + JSON.stringify(undefined) returns undefined. + + The optional space parameter produces a stringification of the + value that is filled with line breaks and indentation to make it + easier to read. + + If the space parameter is a non-empty string, then that string will + be used for indentation. If the space parameter is a number, then + the indentation will be that many spaces. + + Example: + + text = JSON.stringify(['e', {pluribus: 'unum'}]); + // text is '["e",{"pluribus":"unum"}]' + + + text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); + // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + + text = JSON.stringify([new Date()], function (key, value) { + return this[key] instanceof Date ? + 'Date(' + this[key] + ')' : value; + }); + // text is '["Date(---current time---)"]' + + + JSON.parse(text, reviver) + This method parses a JSON text to produce an object or array. + It can throw a SyntaxError exception. + + The optional reviver parameter is a function that can filter and + transform the results. It receives each of the keys and values, + and its return value is used instead of the original value. + If it returns what it received, then the structure is not modified. + If it returns undefined then the member is deleted. + + Example: + + // Parse the text. Values that look like ISO date strings will + // be converted to Date objects. + + myData = JSON.parse(text, function (key, value) { + var a; + if (typeof value === 'string') { + a = +/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); + if (a) { + return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], + +a[5], +a[6])); + } + } + return value; + }); + + myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { + var d; + if (typeof value === 'string' && + value.slice(0, 5) === 'Date(' && + value.slice(-1) === ')') { + d = new Date(value.slice(5, -1)); + if (d) { + return d; + } + } + return value; + }); + + + This is a reference implementation. You are free to copy, modify, or + redistribute. +*/ + +/*jslint evil: true, strict: false */ + +/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, + call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (!this.JSON) { + this.JSON = {}; +} + +(function () { + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + if (typeof Date.prototype.toJSON !== 'function') { + + Date.prototype.toJSON = function (key) { + + return isFinite(this.valueOf()) ? + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z' : null; + }; + + String.prototype.toJSON = + Number.prototype.toJSON = + Boolean.prototype.toJSON = function (key) { + return this.valueOf(); + }; + } + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? + '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : + '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === 'object' && + typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : + gap ? '[\n' + gap + + partial.join(',\n' + gap) + '\n' + + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + k = rep[i]; + if (typeof k === 'string') { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : + gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + + mind + '}' : '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== 'function') { + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with '()' and 'new' +// because they can cause invocation, and '=' because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we +// replace all simple value tokens with ']' characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or ']' or +// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/. +test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). +replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). +replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + } +}()); diff --git a/js-assembler/runtime-src/primitives.js b/js-assembler/runtime-src/primitives.js new file mode 100644 index 0000000..c416f62 --- /dev/null +++ b/js-assembler/runtime-src/primitives.js @@ -0,0 +1,4678 @@ +var primitive = {}; + +/** + +Note: all primitives in this file should be written so that it's easy +to syntactically pull out all of the implemented primitives. Make +sure that any new primitive is written as: + + PRIMITIVES[name-of-primitive] = ... + +That way, we can do a simple grep. + +*/ + + + +(function() { + + +var CALL = types.internalCall; + +var PAUSE = types.internalPause; + + + + + + + + +var PRIMITIVES = {}; + +var PrimProc = types.PrimProc; +var CasePrimitive = types.CasePrimitive; +var makeOptionPrimitive = types.makeOptionPrimitive; + +////////////////////////////////////////////////////////////////////// + +// Helper Functions + +var id = function(x) { return x; }; + +var sub1 = function(x) { + check(x, isNumber, 'sub1', 'number', 1, [x]); + return jsnums.subtract(x, 1); +} + +var add1 = function(x) { + check(x, isNumber, 'add1', 'number', 1, [x]); + return jsnums.add(x, 1); +} + +var callWithValues = function(f, vals) { + if (vals instanceof types.ValuesWrapper) { + return CALL(f, vals.elts, id); + } + else { + return CALL(f, [vals], id); + } +}; + + +// onSingleResult: x (x -> y) -> y +// Applies f on x, but first checks that x is a single value. +// If it isn't, raises an arity error. +var onSingleResult = function(x, f) { + if (x instanceof types.ValuesWrapper) { + if (x.elts.length === 1) { + return f(x.elts[0]); + } else { + var argsStr = helpers.map(function(x) { return "~s"; }, x.elts).join(' '); + raise(types.incompleteExn( + types.exnFailContractArity, + helpers.format( + 'context expected 1 value, received ~s values: ' + argsStr, + [x.elts.length].concat(x.elts)))); + } + } else { + return f(x); + } +}; + + +var procedureArity = function(proc) { + check(proc, isFunction, 'procedure-arity', 'procedure', 1, [proc]); + + var singleCaseArity = function(aCase) { + if (aCase instanceof types.ContinuationClosureValue) { + return types.arityAtLeast(0); + } + else if (aCase.isRest) { + return types.arityAtLeast(aCase.numParams); + } + else { + return aCase.numParams; + } + } + + if ( proc instanceof PrimProc || + proc instanceof types.ClosureValue || + proc instanceof types.ContinuationClosureValue ) { + return singleCaseArity(proc); + } + else { + var cases; + if ( proc instanceof CasePrimitive ) { + cases = proc.cases; + } + else if ( proc instanceof types.CaseLambdaValue ) { + cases = proc.closures; + } + else { + throw types.internalError('procedure-arity given wrong type that passed isFunction!', false); + } + + var ret = []; + for (var i = 0; i < cases.length; i++) { + ret.push( singleCaseArity(cases[i]) ); + } + ret = normalizeArity(ret); + return ret.length == 1 ? ret[0] : types.list(ret); + } +}; + +var normalizeArity = function(arity) { + var newArity = arity.slice(0); + var sortFunc = function(x, y) { + if ( types.isArityAtLeast(x) ) { + if ( types.isArityAtLeast(y) ) { + return types.arityAtLeastValue(x) - types.arityAtLeastValue(y); + } + else { + return types.arityAtLeastValue(x) - y - 0.5; + } + } + else { + if ( types.isArityAtLeast(y) ) { + return x - types.arityAtLeastValue(y) + 0.5; + } + else { + return x - y; + } + } + }; + newArity.sort(sortFunc); + + for (var i = 0; i < newArity.length-1; i++) { + if ( types.isArityAtLeast(newArity[i]) ) { + return newArity.slice(0, i+1); + } + } + return newArity; +}; + + +var procArityContains = helpers.procArityContains; + + + + + +var length = function(lst) { + checkList(lst, 'length', 1, [lst]); + var ret = 0; + for (; !lst.isEmpty(); lst = lst.rest()) { + ret = ret+1; + } + return ret; +} + +var append = function(initArgs) { + if (initArgs.length == 0) { + return types.EMPTY; + } + var args = initArgs.slice(0, initArgs.length-1); + var lastArg = initArgs[initArgs.length - 1]; + arrayEach(args, function(x, i) {checkList(x, 'append', i+1, initArgs);}); + + var ret = lastArg; + for (var i = args.length-1; i >= 0; i--) { + ret = args[i].append(ret); + } + return ret; +} + +var foldHelp = function(f, acc, args) { + if ( args[0].isEmpty() ) { + return acc; + } + + var fArgs = []; + var argsRest = []; + for (var i = 0; i < args.length; i++) { + fArgs.push(args[i].first()); + argsRest.push(args[i].rest()); + } + fArgs.push(acc); + return CALL(f, fArgs, + function(result) { + return foldHelp(f, result, argsRest); + }); +} + +var quicksort = function(functionName) { + return function(initList, comp) { + checkList(initList, functionName, 1, arguments); + check(comp, procArityContains(2), functionName, 'procedure (arity 2)', 2, arguments); + + var quicksortHelp = function(k) { + return function(lst) { + if ( lst.isEmpty() ) { + return k(types.EMPTY); + } + + var compYes = new PrimProc('compYes', 1, false, false, + function(x) { return CALL(comp, [x, lst.first()], id); }); + var compNo = new PrimProc('compNo', 1, false, false, + function(x) { return CALL(comp, [x, lst.first()], + function(res) { return !res; }); + }); + + return CALL(PRIMITIVES['filter'], + [compYes, lst.rest()], + quicksortHelp(function(sorted1) { + return CALL(PRIMITIVES['filter'], + [compNo, lst.rest()], + quicksortHelp(function(sorted2) { + return k( append([sorted1, + types.list([lst.first()]), + sorted2]) ); + })); + })); + }; + } + return quicksortHelp(id)(initList); + }; +} + +var compare = function(args, comp) { + var curArg = args[0]; + for (var i = 1; i < args.length; i++) { + if ( !comp(curArg, args[i]) ) { + return false; + } + curArg = args[i]; + } + return true; +} + +// isAlphabeticString: string -> boolean +var isAlphabeticString = function(s) { + for(var i = 0; i < s.length; i++) { + if (! ((s.charAt(i) >= "a" && s.charAt(i) <= "z") || + (s.charAt(i) >= "A" && s.charAt(i) <= "Z"))) { + return false; + } + } + return true; +}; + + +var isMutableString = function(s) { + return isString(s) && typeof s != 'string'; +}; + + +var isNumericString = function(s) { + for (var i = 0; i < s.length; i++) { + if ( ! (s.charAt(i) >= '0' && s.charAt(i) <= '9') ) { + return false; + } + } + return true; +} + +// isWhitespaceString: string -> boolean +var isWhitespaceString = (function() { + var pat = new RegExp("^\\s*$"); + return function(s) { + return (s.match(pat) ? true : false); + } +}()); + + + + +var isImmutable = function(x) { + return ((isString(x) || + isByteString(x) || + isVector(x) || + isHash(x) || + isBox(x)) && + !x.mutable); +}; + + + +var assocListToHash = helpers.assocListToHash; + +var raise = helpers.raise; + + + + + // On any numeric error, throw a contract error. + jsnums.onThrowRuntimeError = function(msg, x, y) { + raise(types.incompleteExn( + types.exnFailContract, + helpers.format("~a: ~s ~s", [msg, x, y]), + [])); + }; + + + + +var checkAndGetGuard = function(funName, guard, numberOfGuardArgs) { + if ( !guard ) { + return false; + } + + // Check the number of arguments on the guard + if ( !procArityContains(numberOfGuardArgs)(guard) ) { + raise(types.incompleteExn( + types.exnFailContract, + helpers.format( + '~a: guard procedure does not accept ~a arguments ' + + '(one more than the number constructor arguments): ~s', + [funName, numberOfGuardArgs, guard]), + [])); + } + + // if the guard has the right number of arguments, + // then construct a javascript function to call it + return function(args, name, k) { + args = args.concat([name]); + return CALL(guard, args, + function(res) { + if ( res instanceof types.ValuesWrapper ) { + return k(res.elts); + } + else { + return k([res]); + } + }); + }; +}; + + + + + +var getMakeStructTypeReturns = function(aStructType) { + var name = aStructType.name; + return new types.ValuesWrapper( + [aStructType, + (new types.StructConstructorProc(aStructType, + 'make-'+name, + aStructType.numberOfArgs, + false, + false, + aStructType.constructor)), + (new types.StructPredicateProc(aStructType, name+'?', 1, false, false, aStructType.predicate)), + (new types.StructAccessorProc(aStructType, + name+'-ref', + 2, + false, + false, + function(x, i) { + check(x, aStructType.predicate, name+'-ref', 'struct:'+name, 1, arguments); + check(i, isNatural, name+'-ref', 'non-negative exact integer', 2, arguments); + + var numFields = aStructType.numberOfFields; + if ( jsnums.greaterThanOrEqual(i, numFields) ) { + var msg = (name+'-ref: slot index for not in ' + + '[0, ' + (numFields-1) + ']: ' + i); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return aStructType.accessor(x, jsnums.toFixnum(i)); + })), + (new types.StructMutatorProc(aStructType, + name+'-set!', + 3, + false, + false, + function(x, i, v) { + check(x, aStructType.predicate, name+'-set!', 'struct:'+name, 1, arguments); + check(i, isNatural, name+'-set!', 'non-negative exact integer', 2, arguments); + + var numFields = aStructType.numberOfFields; + if ( jsnums.greaterThanOrEqual(i, numFields) ) { + var msg = (name+'-set!: slot index for not in ' + + '[0, ' + (numFields-1) + ']: ' + i); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + aStructType.mutator(x, jsnums.toFixnum(i), v) + })) ]); +}; + + + + +////////////////////////////////////////////////////////////////////// + + +var isNumber = jsnums.isSchemeNumber; +var isReal = jsnums.isReal; +var isRational = jsnums.isRational; +var isComplex = isNumber; +var isInteger = jsnums.isInteger; + +var isNatural = function(x) { + return jsnums.isExact(x) && isInteger(x) && jsnums.greaterThanOrEqual(x, 0); +}; + +var isNonNegativeReal = function(x) { + return isReal(x) && jsnums.greaterThanOrEqual(x, 0); +}; + +var isSymbol = types.isSymbol; +var isChar = types.isChar; +var isString = types.isString; +var isPair = types.isPair; +var isEmpty = function(x) { return x === types.EMPTY; }; +var isList = helpers.isList; +var isListOf = helpers.isListOf; + +var isVector = types.isVector; +var isBox = types.isBox; +var isHash = types.isHash; +var isByteString = types.isByteString; + +var isByte = function(x) { + return (isNatural(x) && + jsnums.lessThanOrEqual(x, 255)); +} + +var isBoolean = function(x) { + return (x === true || x === false); +} + +var isFunction = types.isFunction; + +var isEqual = function(x, y) { + return types.isEqual(x, y, new types.UnionFind()); +} + +var isEq = function(x, y) { + return x === y; +} + +var isEqv = function(x, y) { + if (isNumber(x) && isNumber(y)) { + return jsnums.eqv(x, y); + } + else if (isChar(x) && isChar(y)) { + return x.val === y.val; + } + return x === y; +} + + + +var isAssocList = function(x) { + return isPair(x) && isPair(x.rest()) && isEmpty(x.rest().rest()); +}; + + +var isCompoundEffect = function(x) { + return ( types.isEffect(x) || isListOf(x, isCompoundEffect) ); +}; + +var isJsValue = types.isJsValue; + +var isJsObject = function(x) { + return isJsValue(x) && typeof(x.val) == 'object'; +}; + +var isJsFunction = function(x) { + return isJsValue(x) && typeof(x.val) == 'function'; +}; + + + +var arrayEach = function(arr, f) { + for (var i = 0; i < arr.length; i++) { + f.call(null, arr[i], i); + } +} + +//var throwCheckError = helpers.throwCheckError; +var check = helpers.check; + +var checkList = function(x, functionName, position, args) { + if ( !isList(x) ) { + helpers.throwCheckError([functionName, + 'list', + helpers.ordinalize(position), + x], + position, + args); + } +} + +var checkListOf = helpers.checkListOf; + +var checkListOfLength = function(lst, n, functionName, position, args) { + if ( !isList(lst) || (length(lst) < n) ) { + helpers.throwCheckError([functionName, + 'list with ' + n + ' or more elements', + helpers.ordinalize(position), + lst], + position, + args); + } +} + +var checkAllSameLength = function(lists, functionName, args) { + if (lists.length == 0) + return; + + var len = length(lists[0]); + arrayEach(lists, + function(lst, i) { + if (length(lst) != len) { + var argsStr = helpers.map(function(x) { return " ~s"; }, args).join(''); + var msg = helpers.format(functionName + ': all lists must have the same size; arguments were:' + argsStr, + args); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + }); +} + + +////////////////////////////////////////////////////////////////////// + + +// Special moby-specific primitives + +PRIMITIVES['verify-boolean-branch-value'] = + new PrimProc('verify-boolean-branch-value', + 2, + false, + false, + function(x, aLoc) { + if (x !== true && x !== false) { + // FIXME: should throw structure + // make-moby-error-type:branch-value-not-boolean + // instead. + throw new Error("the value " + sys.inspect(x) + " is not boolean type at " + aLoc); + } + return x; + }) + +PRIMITIVES['throw-cond-exhausted-error'] = + new PrimProc('throw-cond-exhausted-error', + 1, + false, + false, + function(aLoc) { + // FIXME: should throw structure + // make-moby-error-type:conditional-exhausted + // instead. + throw types.schemeError(types.incompleteExn(types.exnFail, "cond: all question results were false", [])); + }); + + +PRIMITIVES['print-values'] = + new PrimProc('print-values', + 0, + true, + true, + function(state, values) { + var printed = false; + for (var i = 0; i < values.length; i++) { + if (values[i] !== types.VOID) { + if (printed) { + state.getDisplayHook()("\n"); + } + state.getPrintHook()(values[i]); + printed = true; + } + } + if (printed) { + state.getDisplayHook()("\n"); + } + state.v = types.VOID; + }); + + + + + +////////////////////////////////////////////////////////////////////// + +var defaultPrint = + new PrimProc('print', + 1, + false, + true, + function(state, x) { + state.getPrintHook()(types.toWrittenString(x)); + state.v = types.VOID; + }); + + +PRIMITIVES['write'] = + new CasePrimitive('write', + [new PrimProc('write', 1, false, true, function(aState, x) { + aState.getPrintHook()(x); + aState.v = types.VOID; + }), + new PrimProc('write', 2, false, true, function(aState, x, port) { + throw types.internalError('write to a port not implemented yet.', false); + }) ]); + + + +PRIMITIVES['display'] = + new CasePrimitive('display', + [new PrimProc('display', 1, false, true, function(state, x) { + state.getDisplayHook()(x); + state.v = types.VOID; + }), + new PrimProc('display', 2, false, true, function(state, x, port) { + // FIXME + throw types.internalError("display to a port not implemented yet.", false); + } )]); + + + +PRIMITIVES['newline'] = + new CasePrimitive('newline', + [new PrimProc('newline', 0, false, true, function(state) { + state.getDisplayHook()('\n'); + state.v = types.VOID; + }), + new PrimProc('newline', 1, false, false, function(port) { + // FIXME + throw types.internalError("newline to a port not implemented yet.", false); + } )]); + + + +PRIMITIVES['current-print'] = + new PrimProc('current-print', + 0, + false, false, + function() { + return defaultPrint; + }); + + +PRIMITIVES['current-continuation-marks'] = + // FIXME: should be CasePrimitive taking either 0 or 1 arguments + new PrimProc('current-continuation-marks', + 0, + false, true, + function(aState) { + aState.v = state.captureCurrentContinuationMarks(aState); + }); + +PRIMITIVES['continuation-mark-set?'] = + new PrimProc('continuation-mark-set?', + 1, + false, + false, + types.isContinuationMarkSet); + +PRIMITIVES['continuation-mark-set->list'] = + new PrimProc('continuation-mark-set->list', + 2, + false, + true, + function(state, markSet, keyV) { + check(markSet, + types.isContinuationMarkSet, + 'continuation-mark-set->list', + 'continuation-mark-set', + 1, + [markSet, keyV]); + state.v = types.list(markSet.ref(keyV)); + }); + + + +PRIMITIVES['for-each'] = + new PrimProc('for-each', + 2, + true, false, + function(f, firstArg, arglists) { + var allArgs = [f, firstArg].concat(arglists); + arglists.unshift(firstArg); + check(f, isFunction, 'for-each', 'procedure', 1, allArgs); + arrayEach(arglists, function(lst, i) {checkList(lst, 'for-each', i+2, allArgs);}); + checkAllSameLength(arglists, 'for-each', allArgs); + check(f, procArityContains(arglists.length), 'for-each', 'procedure (arity ' + arglists.length + ')', 1, allArgs); + + var forEachHelp = function(args) { + if (args[0].isEmpty()) { + return types.VOID; + } + + var argsFirst = []; + var argsRest = []; + for (var i = 0; i < args.length; i++) { + argsFirst.push(args[i].first()); + argsRest.push(args[i].rest()); + } + + return CALL(f, argsFirst, + function(result) { return forEachHelp(argsRest); }); + } + + return forEachHelp(arglists); + }); + + +PRIMITIVES['make-thread-cell'] = + new CasePrimitive('make-thread-cell', [ + new PrimProc("make-thread-cell", + 1, false, false, + function(x) { + return new types.ThreadCell(x, false); + } + ), + new PrimProc("make-thread-cell", + 2, false, false, + function(x, y) { + return new types.ThreadCell(x, y); + } + )]); + + + +PRIMITIVES['make-continuation-prompt-tag'] = + new CasePrimitive('make-continuation-prompt-tag', + [ + new PrimProc("make-continuation-prompt-tag", + 0, false, false, + function() { + return new types.ContinuationPromptTag(); + } + ), + new PrimProc("make-continuation-prompt-tag", + 1, false, false, + function(x) { + check(x, isSymbol, 'make-continuation-prompt-tag', + 'symbol', 1, arguments); + return new types.ContinuationPromptTag(x); + } + )]); + + + +PRIMITIVES['call-with-continuation-prompt'] = + new PrimProc('call-with-continuation-prompt', + 1, + true, true, + function(aState, proc, args) { + + // First check that proc is a procedure. + var allArgs = [proc].concat(args); + check(proc, isFunction, 'call-with-continuation-prompt', 'procedure', 1, allArgs); + + // Do other argument parsing stuff... + var promptTag; + var handler; + var procArgs; + if (args.length === 0) { + promptTag = types.defaultContinuationPromptTag; + handler = types.defaultContinuationPromptTagHandler; + procArgs = args.slice(0); + } else if (args.length === 1) { + promptTag = args[0]; + handler = types.defaultContinuationPromptTagHandler; + procArgs = args.slice(1); + + } else if (args.length >= 2) { + promptTag = args[0]; + handler = args[1]; + procArgs = args.slice(2); + } + + // If the handler is false, default to () + if (handler === false) { + handler = defaultCallWithContinuationPromptHandler; + } + + // Add the prompt. + aState.pushControl(new control.PromptControl(aState.vstack.length, + promptTag, + handler)); + // Within the context of the prompt, do the procedure application. + aState.pushControl( + new control.ApplicationControl( + new control.ConstantControl(proc), + helpers.map(function(op) { + return new control.ConstantControl(op)}, + procArgs))); + }); + +PRIMITIVES['default-continuation-prompt-tag'] = + new PrimProc('default-continuation-prompt-tag', + 0, + false, false, + function() { + return types.defaultContinuationPromptTag; + }); + + +PRIMITIVES['continuation-prompt-tag?'] = + new PrimProc('continuation-prompt-tag?', + 1, + false, false, + types.isContinuationPromptTag); + + + + + +// Implements the default handler for a continuation prompt, if one isn't provided +// by call-with-continuation-prompt. +var defaultCallWithContinuationPromptHandler = + new PrimProc('default-call-with-continuation-prompt-handler', + 1, + false, + true, + function(aState, abortThunk) { + // The default handler accepts a single abort thunk + // argument, and then re-installs the prompt and continues + // with the abort thunk. + // (call-with-continuation-prompt abort-thunk prompt-tag #f) + aState.pushControl( + new control.ApplicationControl( + new control.ConstantControl(PRIMITIVES['call-with-continuation-prompt']), + helpers.map(function(op) { + return new control.ConstantControl(op)}, + [abortThunk, promptTag, false]))); + }); + + +PRIMITIVES['abort-current-continuation'] = + new PrimProc('abort-current-continuation', + 1, + true, true, + function(aState, promptTag, args) { + control.setupAbortToPrompt(aState, promptTag, args); + }); + + + + + + + + + + + + +PRIMITIVES['make-struct-type'] = + makeOptionPrimitive( + 'make-struct-type', + 4, + [false, + types.EMPTY, + false, + false, + types.EMPTY, + false, + false], + true, + function(userArgs, + aState, + name, + superType, + initFieldCnt, + autoFieldCnt, + autoV, + props, // FIXME: currently ignored + inspector, // FIXME: currently ignored + procSpec, // FIXME: currently ignored + immutables, // FIXME: currently ignored + guard, + constructorName // FIXME: currently ignored + ) { + check(name, isSymbol, 'make-struct-type', 'symbol', 1, userArgs); + check(superType, function(x) { return x === false || types.isStructType(x); }, + 'make-struct-type', 'struct-type or #f', 2, userArgs); + check(initFieldCnt, isNatural, 'make-struct-type', 'exact non-negative integer', 3, userArgs); + check(autoFieldCnt, isNatural, 'make-struct-type', 'exact non-negative integer', 4, userArgs); + // TODO: check props + // TODO: check inspector + // TODO: check procSpect + checkListOf(immutables, isNatural, 'make-struct-type', 'exact non-negative integer', 9, userArgs); + check(guard, function(x) { return x === false || isFunction(x); }, + 'make-struct-type', 'procedure or #f', 10, userArgs); + + var numberOfGuardArgs = initFieldCnt + 1 + (superType ? superType.numberOfArgs : 0); + var aStructType = + types.makeStructureType(name.toString(), + superType, + jsnums.toFixnum(initFieldCnt), + jsnums.toFixnum(autoFieldCnt), + autoV, + checkAndGetGuard('make-struct-type', guard, numberOfGuardArgs)); + + aState.v = getMakeStructTypeReturns(aStructType); + }); + + +PRIMITIVES['make-struct-field-accessor'] = + makeOptionPrimitive( + 'make-struct-field-accessor', + 2, + [false], + false, + function(userArgs, accessor, fieldPos, fieldName) { + check(accessor, function(x) { return x instanceof types.StructAccessorProc && x.numParams > 1; }, + 'make-struct-field-accessor', 'accessor procedure that requires a field index', 1, userArgs); + check(fieldPos, isNatural, 'make-struct-field-accessor', 'exact non-negative integer', 2, userArgs); + check(fieldName, function(x) { return x === false || isSymbol(x); }, + 'make-struct-field-accessor', 'symbol or #f', 3, userArgs); + + var procName = accessor.type.name + '-' + + (fieldName ? fieldName.toString() : 'field' + fieldPos.toString()); + + return new types.StructAccessorProc(accessor.type, procName, 1, false, false, + function(x) { + check(x, accessor.type.predicate, procName, 'struct:'+accessor.type.name, 1); + return accessor.impl(x, fieldPos); + }); + }); + + + +PRIMITIVES['make-struct-field-mutator'] = + makeOptionPrimitive( + 'make-struct-field-mutator', + 2, + [false], + false, + function(userArgs, mutator, fieldPos, fieldName) { + check(mutator, function(x) { return x instanceof types.StructMutatorProc && x.numParams > 1; }, + 'make-struct-field-mutator', 'mutator procedure that requires a field index', 1, userArgs); + check(fieldPos, isNatural, 'make-struct-field-mutator', 'exact non-negative integer', 2, userArgs); + check(fieldName, function(x) { return x === false || isSymbol(x); }, + 'make-struct-field-mutator', 'symbol or #f', 3, userArgs); + + var procName = mutator.type.name + '-' + + (fieldName ? fieldName.toString() : 'field' + fieldPos.toString()); + + return new types.StructMutatorProc(mutator.type, procName, 2, false, false, + function(x, v) { + check(x, mutator.type.predicate, procName, 'struct:'+mutator.type.name, 1, arguments); + return mutator.impl(x, fieldPos, v); + }); + }); + + +PRIMITIVES['struct-type?'] = + new PrimProc('struct-type?', 1, false, false, types.isStructType); + +PRIMITIVES['struct-constructor-procedure?'] = + new PrimProc('struct-constructor-procedure?', 1, false, false, + function(x) { + return x instanceof types.StructConstructorProc; }); + +PRIMITIVES['struct-predicate-procedure?'] = + new PrimProc('struct-predicate-procedure?', 1, false, false, + function(x) { + return x instanceof types.StructPredicateProc; }); + +PRIMITIVES['struct-accessor-procedure?'] = + new PrimProc('struct-accessor-procedure?', 1, false, false, + function(x) { + return x instanceof types.StructAccessorProc; }); + +PRIMITIVES['struct-mutator-procedure?'] = + new PrimProc('struct-mutator-procedure?', 1, false, false, + function(x) { + return (x instanceof types.StructMutatorProc); }); + + + +PRIMITIVES['procedure-arity'] = new PrimProc('procedure-arity', 1, false, false, procedureArity); + + +PRIMITIVES['procedure-arity-includes?'] = + new PrimProc('procedure-arity-includes?', + 2, + false, + false, + function(proc, k) { + check(proc, isFunction, 'procedure-arity-includes?', 'procedure', 1, [proc, k]); + check(k, isNatural, 'procedure-arity-includes?', 'exact non-negative integer', 2, [proc, k]); + return helpers.procArityContains(k)(proc); + }); + + +PRIMITIVES['make-arity-at-least'] = + new PrimProc('make-arity-at-least', + 1, + false, + false, + types.arityAtLeast); + +PRIMITIVES['arity-at-least?'] = + new PrimProc('arity-at-least?', + 1, + false, false, + function(x) { + return types.isArityAtLeast(x); + }); + +PRIMITIVES['arity-at-least-value'] = + new PrimProc('arity-at-least-value', + 1, + false, false, + function(x) { + check(x, types.isArityAtLeast, 'arity-at-least-value', + 'arity-at-least', 1, [x]); + return types.arityAtLeastValue(x); + }); + + +PRIMITIVES['apply'] = + new PrimProc('apply', + 2, + true, false, + function(f, firstArg, args) { + var allArgs = [f, firstArg].concat(args); + check(f, isFunction, 'apply', 'procedure', 1, allArgs); + args.unshift(firstArg); + + var lastArg = args.pop(); + checkList(lastArg, 'apply', args.length+2, allArgs); + var args = args.concat(helpers.schemeListToArray(lastArg)); + + return CALL(f, args, id); + }); + + +PRIMITIVES['values'] = + new PrimProc('values', + 0, + true, false, + function(args) { + if (args.length === 1) { + return args[0]; + } + return new types.ValuesWrapper(args); + }); + + +PRIMITIVES['call-with-values'] = + new PrimProc('call-with-values', + 2, + false, false, + function(g, r) { + check(g, procArityContains(0), 'call-with-values', 'procedure (arity 0)', 1, arguments); + check(r, isFunction, 'call-with-values', 'procedure', 2, arguments); + + return CALL(g, [], + function(res) { + return callWithValues(r, res); + }); + }); + + +PRIMITIVES['compose'] = + new PrimProc('compose', + 0, + true, false, + function(procs) { + arrayEach(procs, function(p, i) {check(p, isFunction, 'compose', 'procedure', i+1, procs);}); + + if (procs.length == 0) { + return PRIMITIVES['values']; + } + var funList = types.list(procs).reverse(); + + var composeHelp = function(x, fList) { + if ( fList.isEmpty() ) { + return x; + } + + return CALL(new PrimProc('', 1, false, false, + function(args) { + return callWithValues(fList.first(), args); + }), + [x], + function(result) { + return composeHelp(result, fList.rest()); + }); + } + return new PrimProc('', 0, true, false, + function(args) { + if (args.length === 1) { + return composeHelp(args[0], funList); + } + return composeHelp(new types.ValuesWrapper(args), funList); + }); + }); + + +PRIMITIVES['current-inexact-milliseconds'] = + new PrimProc('current-inexact-milliseconds', + 0, + false, false, + function() { + return jsnums.makeFloat((new Date()).valueOf()); + }); + + +PRIMITIVES['current-seconds'] = + new PrimProc('current-seconds', + 0, + false, false, + function() { + return Math.floor( (new Date()).getTime() / 1000 ); + }); + + +PRIMITIVES['current-inspector'] = + new PrimProc('current-inspector', + 0, + false, false, + function() { + return false; + }); + + + +PRIMITIVES['not'] = + new PrimProc('not', + 1, + false, false, + function(x) { + return x === false; + }); + + +PRIMITIVES['void'] = + new PrimProc('void', 0, true, false, + function(args) { + return types.VOID; + }); + + +PRIMITIVES['random'] = + new CasePrimitive('random', + [new PrimProc('random', 0, false, false, + function() {return types.float(Math.random());}), + new PrimProc('random', 1, false, false, + function(n) { + check(n, isNatural, 'random', 'non-negative exact integer', 1, arguments); + return Math.floor(Math.random() * jsnums.toFixnum(n)); + }) ]); + + +PRIMITIVES['sleep'] = + new CasePrimitive('sleep', + [new PrimProc('sleep', 0, false, false, function() { return types.VOID; }), + new PrimProc('sleep', + 1, + false, false, + function(secs) { + check(secs, isNonNegativeReal, 'sleep', 'non-negative real number', 1); + + var millisecs = jsnums.toFixnum( jsnums.multiply(secs, 1000) ); + return PAUSE(function(caller, success, fail) { + setTimeout(function() { success(types.VOID); }, + millisecs); + }); + }) ]); + + +PRIMITIVES['identity'] = new PrimProc('identity', 1, false, false, id); + + +PRIMITIVES['raise'] = + new PrimProc('raise', + 1, + false, + false, + raise); + +PRIMITIVES['error'] = + new PrimProc('error', + 1, + true, false, + function(arg1, args) { + var allArgs = [arg1].concat(args); + check(arg1, function(x) {return isSymbol(x) || isString(x);}, + 'error', 'symbol or string', 1, allArgs); + + if ( isSymbol(arg1) ) { + if ( args.length === 0 ) { + raise( types.incompleteExn(types.exnFail, "error: " + arg1.val, []) ); + } + var formatStr = args.shift(); + check(formatStr, isString, 'error', 'string', 2, allArgs); + + args.unshift(arg1); + raise( types.incompleteExn(types.exnFail, helpers.format('~s: '+formatStr.toString(), args), []) ); + } + else { + var msgBuffer = [arg1.toString()]; + for (var i = 0; i < args.length; i++) { + msgBuffer.push( types.toWrittenString(args[i]) ); + } + raise( types.incompleteExn(types.exnFail, msgBuffer.join(''), []) ); + } + }); + + + +PRIMITIVES['make-exn'] = new PrimProc('make-exn', 2, false, false, types.exn); + +PRIMITIVES['exn-message'] = + new PrimProc('exn-message', + 1, + false, false, + function(exn) { + check(exn, types.isExn, 'exn-message', 'exn', 1, [exn]); + return types.exnMessage(exn); + }); + + +PRIMITIVES['exn-continuation-marks'] = + new PrimProc('exn-continuation-marks', + 1, + false, false, + function(exn) { + check(exn, types.isExn, 'exn-continuation-marks', 'exn', 1, [exn]); + return types.exnContMarks(exn); + }); + + +PRIMITIVES['make-exn:fail'] = new PrimProc('make-exn:fail', 2, false, false, types.exnFail); + + +PRIMITIVES['make-exn:fail:contract'] = new PrimProc('make-exn:fail:contract', 2, false, false, types.exnFailContract); + + PRIMITIVES['make-exn:fail:contract:arity'] = + new PrimProc('make-exn:fail:contract:arity', + 2, + false, + false, + types.exnFailContractArity); + + + PRIMITIVES['make-exn:fail:contract:variable'] = + new PrimProc('make-exn:fail:contract:variable', + 3, + false, + false, + types.exnFailContractVariable); + + + +PRIMITIVES['make-exn:fail:contract:divide-by-zero'] = + new PrimProc('make-exn:fail:contract:divide-by-zero', + 2, + false, + false, + types.exnFailContractDivisionByZero); + + +PRIMITIVES['exn?'] = + new PrimProc('exn?', + 1, + false, + false, + types.isExn); + + +PRIMITIVES['exn:fail?'] = + new PrimProc('exn:fail?', + 1, + false, + false, + types.isExnFail); + + +PRIMITIVES['exn:fail:contract?'] = + new PrimProc('exn:fail:contract?', + 1, + false, + false, + types.isExnFailContract); + + +PRIMITIVES['exn:fail:contract:arity?'] = + new PrimProc('exn:fail:contract:arity?', + 1, + false, + false, + types.isExnFailContractArity); + + +PRIMITIVES['exn:fail:contract:variable?'] = + new PrimProc('exn:fail:contract:variable?', + 1, + false, + false, + types.isExnFailContractVariable); + + +PRIMITIVES['exn:fail:contract:divide-by-zero?'] = + new PrimProc('exn:fail:contract:divide-by-zero?', + 1, + false, + false, + types.isExnFailContractDivisionByZero); + + + + +/*********************** + *** Math Primitives *** + ***********************/ + + +PRIMITIVES['*'] = + new PrimProc('*', + 0, + true, false, + function(args) { + arrayEach(args, function(x, i) {check(x, isNumber, '*', 'number', i+1, args);}); + + var result = types.rational(1); + for(var i = 0; i < args.length; i++) { + result = jsnums.multiply(args[i], result); + } + return result; + }); + + + +PRIMITIVES['-'] = + new PrimProc("-", + 1, + true, false, + function(x, args) { + var allArgs = [x].concat(args); + check(x, isNumber, '-', 'number', 1, allArgs); + arrayEach(args, function(y, i) {check(y, isNumber, '-', 'number', i+2, allArgs);}); + + if (args.length == 0) { + return jsnums.subtract(0, x); + } + var result = x; + for (var i = 0; i < args.length; i++) { + result = jsnums.subtract(result, args[i]); + } + return result; + }); + + +PRIMITIVES['+'] = + new PrimProc("+", + 0, + true, false, + function(args) { + arrayEach(args, function(x, i) {check(x, isNumber, '+', 'number', i+1, args);}); + + if (args.length == 0) { + return 0; + } + var result = args[0]; + for (var i = 1; i < args.length; i++) { + result = jsnums.add(result, args[i]); + } + return result; + }); + + +PRIMITIVES['='] = + new PrimProc("=", + 2, + true, false, + function(x, y, args) { + args.unshift(y); + args.unshift(x); + arrayEach(args, function(z, i) {check(z, isNumber, '=', 'number', i+1, args);}); + + return compare(args, jsnums.equals); + }); + + +PRIMITIVES['=~'] = + new PrimProc('=~', + 3, + false, false, + function(x, y, range) { + check(x, isReal, '=~', 'real', 1, arguments); + check(y, isReal, '=~', 'real', 2, arguments); + check(range, isNonNegativeReal, '=~', 'non-negative-real', 3, arguments); + + return jsnums.lessThanOrEqual(jsnums.abs(jsnums.subtract(x, y)), range); + }); + + +PRIMITIVES['/'] = + new PrimProc('/', + 1, + true, false, + function(x, args) { + var allArgs = [x].concat(args); + check(x, isNumber, '/', 'number', 1, allArgs); + arrayEach(args, function(y, i) {check(y, isNumber, '/', 'number', i+2, allArgs);}); + + if (args.length == 0) { + if ( jsnums.eqv(x, 0) ) { + raise( types.incompleteExn(types.exnFailContractDivisionByZero, '/: division by zero', []) ); + } + return jsnums.divide(1, x); + } + + var res = x; + for (var i = 0; i < args.length; i++) { + if ( jsnums.eqv(args[i], 0) ) { + raise( types.incompleteExn(types.exnFailContractDivisionByZero, '/: division by zero', []) ); + } + res = jsnums.divide(res, args[i]); + } + return res; + }); + + + +PRIMITIVES['sub1'] = + new PrimProc("sub1", + 1, + false, false, + sub1); + +PRIMITIVES['add1'] = + new PrimProc("add1", + 1, + false, false, + add1); + + +PRIMITIVES['<'] = + new PrimProc('<', + 2, + true, false, + function(x, y, args) { + args.unshift(y); + args.unshift(x); + arrayEach(args, function(z, i) {check(z, isNumber, '<', 'number', i+1, args);}); + + return compare(args, jsnums.lessThan); + }); + + +PRIMITIVES['>'] = + new PrimProc('>', + 2, + true, false, + function(x, y, args) { + args.unshift(y); + args.unshift(x); + arrayEach(args, function(z, i) {check(z, isNumber, '>', 'number', i+1, args);}); + + return compare(args, jsnums.greaterThan); + }); + + +PRIMITIVES['<='] = + new PrimProc('<=', + 2, + true, false, + function(x, y, args) { + args.unshift(y); + args.unshift(x); + arrayEach(args, function(z, i) {check(z, isNumber, '<=', 'number', i+1, args);}); + + return compare(args, jsnums.lessThanOrEqual); + }); + + +PRIMITIVES['>='] = + new PrimProc('>=', + 2, + true, false, + function(x, y, args) { + args.unshift(y); + args.unshift(x); + arrayEach(args, function(z, i) {check(z, isNumber, '>=', 'number', i+1, args);}); + + return compare(args, jsnums.greaterThanOrEqual); + }); + + + + +PRIMITIVES['abs'] = + new PrimProc('abs', + 1, + false, false, + function(x) { + check(x, isReal, 'abs', 'real', 1); + return jsnums.abs(x); + }); + + +PRIMITIVES['quotient'] = + new PrimProc('quotient', + 2, + false, false, + function(x, y) { + check(x, isInteger, 'quotient', 'integer', 1, arguments); + check(y, isInteger, 'quotient', 'integer', 2, arguments); + + return jsnums.quotient(x, y); + }); + + +PRIMITIVES['remainder'] = + new PrimProc('remainder', + 2, + false, false, + function(x, y) { + check(x, isInteger, 'remainder', 'integer', 1, arguments); + check(y, isInteger, 'remainder', 'integer', 2, arguments); + + return jsnums.remainder(x, y); + }); + + +PRIMITIVES['modulo'] = + new PrimProc('modulo', + 2, + false, false, + function(x, y) { + check(x, isInteger, 'modulo', 'integer', 1, arguments); + check(y, isInteger, 'modulo', 'integer', 2, arguments); + + return jsnums.modulo(x, y); + }); + + +PRIMITIVES['max'] = + new PrimProc('max', + 1, + true, false, + function(x, args) { + args.unshift(x); +// check(x, isReal, 'max', 'real', 1, allArgs); + arrayEach(args, function(y, i) {check(y, isReal, 'max', 'real', i+1, args);}); + + var curMax = x; + for (var i = 1; i < args.length; i++) { + if ( jsnums.greaterThan(args[i], curMax) ) { + curMax = args[i]; + } + } + return curMax; + }); + + +PRIMITIVES['min'] = + new PrimProc('min', + 1, + true, false, + function(x, args) { + args.unshift(x); +// check(x, isReal, 'min', 'real', 1); + arrayEach(args, function(y, i) {check(y, isReal, 'min', 'real', i+1, args);}); + + var curMin = x; + for (var i = 1; i < args.length; i++) { + if ( jsnums.lessThan(args[i], curMin) ) { + curMin = args[i]; + } + } + return curMin; + }); + + +PRIMITIVES['gcd'] = + new PrimProc('gcd', + 1, + true, false, + function(x, args) { + var allArgs = [x].concat(args); + check(x, isInteger, 'gcd', 'integer', 1, allArgs); + arrayEach(args, function(y, i) {check(y, isInteger, 'gcd', 'integer', i+2, allArgs);}); + + return jsnums.gcd(x, args); + }); + +PRIMITIVES['lcm'] = + new PrimProc('lcm', + 1, + true, false, + function(x, args) { + var allArgs = [x].concat(args); + check(x, isInteger, 'lcm', 'integer', 1, allArgs); + arrayEach(args, function(y, i) {check(y, isInteger, 'lcm', 'integer', i+2, allArgs);}); + + return jsnums.lcm(x, args); + }); + + +PRIMITIVES['floor'] = + new PrimProc('floor', + 1, + false, false, + function(x) { + check(x, isReal, 'floor', 'real', 1); + return jsnums.floor(x); + }); + + +PRIMITIVES['ceiling'] = + new PrimProc('ceiling', + 1, + false, false, + function(x) { + check(x, isReal, 'ceiling', 'real', 1); + return jsnums.ceiling(x); + }); + + +PRIMITIVES['round'] = + new PrimProc('round', + 1, + false, false, + function(x) { + check(x, isReal, 'round', 'real', 1); + return jsnums.round(x); + }); + +PRIMITIVES['truncate'] = + new PrimProc('truncate', + 1, + false, false, + function(x) { + check(x, isReal, 'truncate', 'real', 1); + if (jsnums.lessThan(x, 0)) { + return jsnums.ceiling(x); + } else { + return jsnums.floor(x); + } + }); + + + +PRIMITIVES['numerator'] = + new PrimProc('numerator', + 1, + false, false, + function(x) { + check(x, isRational, 'numerator', 'rational number', 1); + return jsnums.numerator(x); + }); + + +PRIMITIVES['denominator'] = + new PrimProc('denominator', + 1, + false, false, + function(x) { + check(x, isRational, 'denominator', 'rational number', 1); + return jsnums.denominator(x); + }); + + +PRIMITIVES['expt'] = + new PrimProc("expt", + 2, + false, false, + function(x, y) { + check(x, isNumber, 'expt', 'number', 1, arguments); + check(y, isNumber, 'expt', 'number', 2, arguments); + return jsnums.expt(x, y); + }); + + +PRIMITIVES['exp'] = + new PrimProc('exp', + 1, + false, false, + function(x) { + check(x, isNumber, 'exp', 'number', 1); + return jsnums.exp(x); + }); + + +PRIMITIVES['log'] = + new PrimProc('log', + 1, + false, false, + function(x) { + check(x, isNumber, 'log', 'number', 1); + return jsnums.log(x); + }); + + +PRIMITIVES['sin'] = + new PrimProc('sin', + 1, + false, false, + function(x) { + check(x, isNumber, 'sin', 'number', 1); + return jsnums.sin(x); + }); + + +PRIMITIVES['cos'] = + new PrimProc('cos', + 1, + false, false, + function(x) { + check(x, isNumber, 'cos', 'number', 1); + return jsnums.cos(x); + }); + + +PRIMITIVES['tan'] = + new PrimProc('tan', + 1, + false, false, + function(x) { + check(x, isNumber, 'tan', 'number', 1); + return jsnums.tan(x); + }); + + +PRIMITIVES['asin'] = + new PrimProc('asin', + 1, + false, false, + function(x) { + check(x, isNumber, 'asin', 'number', 1); + return jsnums.asin(x); + }); + + +PRIMITIVES['acos'] = + new PrimProc('acos', + 1, + false, false, + function(x) { + check(x, isNumber, 'acos', 'number', 1); + return jsnums.acos(x); + }); + + +PRIMITIVES['atan'] = + new CasePrimitive('atan', + [new PrimProc('atan', + 1, + false, false, + function(x) { + check(x, isNumber, 'atan', 'number', 1); + return jsnums.atan(x); + }), + new PrimProc('atan', + 2, + false, false, + function(x, y) { + check(x, isReal, 'atan', 'number', 1); + check(y, isReal, 'atan', 'number', 1); + return jsnums.makeFloat( + Math.atan2(jsnums.toFixnum(x), + jsnums.toFixnum(y))); + })]); + + +PRIMITIVES['sinh'] = + new PrimProc('sinh', + 1, + false, false, + function(x) { + check(x, isNumber, 'sinh', 'number', 1); + return jsnums.sinh(x); + }); + + +PRIMITIVES['cosh'] = + new PrimProc('cosh', + 1, + false, false, + function(x) { + check(x, isNumber, 'cosh', 'number', 1); + return jsnums.cosh(x); + }); + + +PRIMITIVES['sqr'] = + new PrimProc('sqr', + 1, + false, false, + function(x) { + check(x, isNumber, 'sqr', 'number', 1); + return jsnums.sqr(x); + }); + + +PRIMITIVES['sqrt'] = + new PrimProc('sqrt', + 1, + false, false, + function(x) { + check(x, isNumber, 'sqrt', 'number', 1); + return jsnums.sqrt(x); + }); + + +PRIMITIVES['integer-sqrt'] = + new PrimProc('integer-sqrt', + 1, + false, false, + function(x) { + check(x, isInteger, 'integer-sqrt', 'integer', 1); + return jsnums.integerSqrt(x); + }); + + +PRIMITIVES['make-rectangular'] = + new PrimProc('make-rectangular', + 2, + false, false, + function(x, y) { + check(x, isReal, 'make-rectangular', 'real', 1, arguments); + check(y, isReal, 'make-rectangular', 'real', 2, arguments); + return types.complex(x, y); + }); + +PRIMITIVES['make-polar'] = + new PrimProc('make-polar', + 2, + false, false, + function(x, y) { + check(x, isReal, 'make-polar', 'real', 1, arguments); + check(x, isReal, 'make-polar', 'real', 2, arguments); + return jsnums.makeComplexPolar(x, y); + }); + + +PRIMITIVES['real-part'] = + new PrimProc('real-part', + 1, + false, false, + function(x) { + check(x, isNumber, 'real-part', 'number', 1); + return jsnums.realPart(x); + }); + + +PRIMITIVES['imag-part'] = + new PrimProc('imag-part', + 1, + false, false, + function(x) { + check(x, isNumber, 'imag-part', 'number', 1); + return jsnums.imaginaryPart(x); + }); + + +PRIMITIVES['angle'] = + new PrimProc('angle', + 1, + false, false, + function(x) { + check(x, isNumber, 'angle', 'number', 1); + return jsnums.angle(x); + }); + + +PRIMITIVES['magnitude'] = + new PrimProc('magnitude', + 1, + false, false, + function(x) { + check(x, isNumber, 'magnitude', 'number', 1); + return jsnums.magnitude(x); + }); + + +PRIMITIVES['conjugate'] = + new PrimProc('conjugate', + 1, + false, false, + function(x) { + check(x, isNumber, 'conjugate', 'number', 1); + return jsnums.conjugate(x); + }); + + + PRIMITIVES['sgn'] = + new PrimProc('sgn', + 1, + false, false, + function(x) { + check(x, isReal, 'sgn', 'real number', 1); + if (jsnums.isInexact(x)) { + if ( jsnums.greaterThan(x, 0) ) { + return jsnums.makeFloat(1); + } else if ( jsnums.lessThan(x, 0) ) { + return jsnums.makeFloat(-1); + } else { + return jsnums.makeFloat(0); + } + } else { + if ( jsnums.greaterThan(x, 0) ) { + return 1; + } else if ( jsnums.lessThan(x, 0) ) { + return -1; + } else { + return 0; + } + } + }); + + +PRIMITIVES['inexact->exact'] = + new PrimProc('inexact->exact', + 1, + false, false, + function (x) { + check(x, isNumber, 'inexact->exact', 'number', 1); + try { + return jsnums.toExact(x); + } catch(e) { + raise( types.exnFailContract('inexact->exact: no exact representation for ' + + types.toWrittenString(x), + false) ); + } + }); + + +PRIMITIVES['exact->inexact'] = + new PrimProc('exact->inexact', + 1, + false, false, + function (x) { + check(x, isNumber, 'exact->inexact', 'number', 1); + return jsnums.toInexact(x); + }); + + +PRIMITIVES['number->string'] = + new PrimProc('number->string', + 1, + false, false, + function(x) { + check(x, isNumber, 'number->string', 'number', 1); + return types.string(x.toString()); + }); + + +PRIMITIVES['string->number'] = + new PrimProc('string->number', + 1, + false, false, + function(str) { + check(str, isString, 'string->number', 'string', 1); + return jsnums.fromString(str.toString()); + }); + + +PRIMITIVES['xml->s-exp'] = + new PrimProc('xml->s-exp', + 1, + false, false, + function(str) { + check(str, isString, 'xml->s-exp', 'string', 1); + str = str.toString(); + if (str.length == 0) { + return types.string(''); + } + + var xmlDoc; + try { + //Internet Explorer + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(s); + // FIXME: check parse errors + } + catch(e) { + var parser = new DOMParser(); + xmlDoc = parser.parseFromString(s, "text/xml"); + // FIXME: check parse errors + } + + var parseAttributes = function(attrs) { + var result = types.EMPTY; + for (var i = 0; i < attrs.length; i++) { + var keyValue = types.cons(types.symbol(attrs.item(i).nodeName), + types.cons(attrs.item(i).nodeValue, + types.EMPTY)); + result = types.cons(keyValue, result); + } + return types.cons(types.symbol("@"), result).reverse(); + }; + + var parse = function(node) { + if (node.nodeType == Node.ELEMENT_NODE) { + var result = types.EMPTY; + var child = node.firstChild; + while (child != null) { + var nextResult = parse(child); + if (isString(nextResult) && + !result.isEmpty() && + isString(result.first())) { + result = types.cons(result.first() + nextResult, + result.rest()); + } else { + result = types.cons(nextResult, result); + } + child = child.nextSibling; + } + result = result.reverse(); + result = types.cons(parseAttributes(node.attributes), + result); + result = types.cons( + types.symbol(node.nodeName), + result); + return result; + } else if (node.nodeType == Node.TEXT_NODE) { + return node.textContent; + } else if (node.nodeType == Node.CDATA_SECTION_NODE) { + return node.data; + } else { + return types.EMPTY; + } + }; + var result = parse(xmlDoc.firstChild); + return result; + }); + + + + +/****************** + *** Predicates *** + ******************/ + +PRIMITIVES['procedure?'] = new PrimProc('procedure?', 1, false, false, isFunction); + +PRIMITIVES['pair?'] = new PrimProc('pair?', 1, false, false, isPair); +PRIMITIVES['cons?'] = new PrimProc('cons?', 1, false, false, isPair); +PRIMITIVES['empty?'] = new PrimProc('empty?', 1, false, false, isEmpty); +PRIMITIVES['null?'] = new PrimProc('null?', 1, false, false, isEmpty); + +PRIMITIVES['undefined?'] = new PrimProc('undefined?', 1, false, false, function(x) { return x === types.UNDEFINED; }); +PRIMITIVES['void?'] = new PrimProc('void?', 1, false, false, function(x) { return x === types.VOID; }); + + +PRIMITIVES['immutable?'] = new PrimProc('immutable?', 1, false, false, isImmutable); + +PRIMITIVES['symbol?'] = new PrimProc('symbol?', 1, false, false, isSymbol); +PRIMITIVES['string?'] = new PrimProc('string?', 1, false, false, isString); +PRIMITIVES['char?'] = new PrimProc('char?', 1, false, false, isChar); +PRIMITIVES['boolean?'] = new PrimProc('boolean?', 1, false, false, isBoolean); +PRIMITIVES['vector?'] = new PrimProc('vector?', 1, false, false, isVector); +PRIMITIVES['struct?'] = new PrimProc('struct?', 1, false, false, types.isStruct); +PRIMITIVES['eof-object?'] = new PrimProc('eof-object?', 1, false, false, function(x) { return x === types.EOF; }); +PRIMITIVES['posn?'] = new PrimProc('posn?', 1, false, false, types.isPosn); +PRIMITIVES['bytes?'] = new PrimProc('bytes?', 1, false, false, isByteString); +PRIMITIVES['byte?'] = new PrimProc('byte?', 1, false, false, isByte); + +PRIMITIVES['number?'] = new PrimProc('number?', 1, false, false, isNumber); +PRIMITIVES['complex?'] = new PrimProc('complex?', 1, false, false, isComplex); +PRIMITIVES['real?'] = new PrimProc('real?', 1, false, false, isReal); +PRIMITIVES['rational?'] = new PrimProc('rational?', 1, false, false, isRational); +PRIMITIVES['integer?'] = new PrimProc('integer?', 1, false, false, isInteger); + +PRIMITIVES['exact?'] = + new PrimProc('exact?', 1, false, false, + function(x) { + check(x, isNumber, 'exact?', 'number', 1); + return jsnums.isExact(x); + }); +PRIMITIVES['inexact?'] = + new PrimProc('inexact?', 1, false, false, + function(x) { + check(x, isNumber, 'inexact?', 'number', 1); + return jsnums.isInexact(x); + }); + +PRIMITIVES['odd?'] = + new PrimProc('odd?', + 1, + false, false, + function(x) { + check(x, isInteger, 'odd?', 'integer', 1); + return jsnums.equals(jsnums.modulo(x, 2), 1); + }); +PRIMITIVES['even?'] = + new PrimProc('even?', + 1, + false, false, + function(x) { + check(x, isInteger, 'even?', 'integer', 1); + return jsnums.equals(jsnums.modulo(x, 2), 0); + }); + +PRIMITIVES['zero?'] = + new PrimProc("zero?", + 1, + false, false, + function(x) { + return jsnums.equals(0, x) + }); + +PRIMITIVES['positive?'] = + new PrimProc('positive?', + 1, + false, false, + function(x) { + check(x, isReal, 'positive?', 'real', 1); + return jsnums.greaterThan(x, 0); + }); +PRIMITIVES['negative?'] = + new PrimProc('negative?', + 1, + false, false, + function(x) { + check(x, isReal, 'negative?', 'real', 1); + return jsnums.lessThan(x, 0); + }); + +PRIMITIVES['box?'] = new PrimProc('box?', 1, false, false, isBox); + +PRIMITIVES['hash?'] = new PrimProc('hash?', 1, false, false, isHash); + + +PRIMITIVES['eq?'] = new PrimProc('eq?', 2, false, false, isEq); +PRIMITIVES['eqv?'] = new PrimProc('eqv?', 2, false, false, isEqv); +PRIMITIVES['equal?'] = new PrimProc('equal?', 2, false, false, isEqual); +PRIMITIVES['equal~?'] = + new PrimProc('equal~?', + 3, + false, false, + function(x, y, range) { + check(range, isNonNegativeReal, 'equal~?', 'non-negative-real', 3, arguments); + + return (isEqual(x, y) || + (isReal(x) && isReal(y) && + jsnums.lessThanOrEqual(jsnums.abs(jsnums.subtract(x, y)), range))); + }); + + +PRIMITIVES['false?'] = new PrimProc('false?', 1, false, false, function(x) { return x === false; }); +PRIMITIVES['boolean=?'] = + new PrimProc('boolean=?', + 2, + false, false, + function(x, y) { + check(x, isBoolean, 'boolean=?', 'boolean', 1, arguments); + check(y, isBoolean, 'boolean=?', 'boolean', 2, arguments); + return x === y; + }); + +PRIMITIVES['symbol=?'] = + new PrimProc('symbol=?', + 2, + false, false, + function(x, y) { + check(x, isSymbol, 'symbol=?', 'symbol', 1, arguments); + check(y, isSymbol, 'symbol=?', 'symbol', 2, arguments); + return isEqual(x, y); + }); + + +PRIMITIVES['js-value?'] = new PrimProc('js-value?', 1, false, false, isJsValue); +PRIMITIVES['js-object?'] = new PrimProc('js-object?', 1, false, false, isJsObject); +PRIMITIVES['js-function?'] = new PrimProc('js-function?', 1, false, false, isJsFunction); + + +/*********************** + *** List Primitives *** + ***********************/ + +PRIMITIVES['cons'] = + new PrimProc('cons', + 2, + false, false, + function(f, r) { +// checkList(r, "cons", 2); + return types.cons(f, r); + }); + + +PRIMITIVES['car'] = + new PrimProc('car', + 1, + false, false, + function(lst) { + check(lst, isPair, 'car', 'pair', 1); + return lst.first(); + }); + +PRIMITIVES['cdr'] = + new PrimProc('cdr', + 1, + false, false, + function (lst) { + check(lst, isPair, 'cdr', 'pair', 1); + return lst.rest(); + }); + +PRIMITIVES['caar'] = + new PrimProc('caar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return (isPair(x) && isPair(x.first())); }, + 'caar', 'caarable value', 1); + return lst.first().first(); + }); + +PRIMITIVES['cadr'] = + new PrimProc('cadr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return isPair(x) && isPair(x.rest()); }, + 'cadr', 'cadrable value', 1); + return lst.rest().first(); + }); + +PRIMITIVES['cdar'] = + new PrimProc('cdar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return isPair(x) && isPair(x.first()); }, + 'cdar', 'cdarable value', 1); + return lst.first().rest(); + }); + +PRIMITIVES['cddr'] = + new PrimProc('cddr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return isPair(x) && isPair(x.rest()); }, + 'cddr', 'cddrable value', 1); + return lst.rest().rest(); + }); + +PRIMITIVES['caaar'] = + new PrimProc('caaar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.first()) && + isPair(x.first().first()) ); }, + 'caaar', 'caaarable value', 1); + return lst.first().first().first(); + }); + +PRIMITIVES['caadr'] = + new PrimProc('caadr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.rest()) && + isPair(x.rest().first()) ); }, + 'caadr', 'caadrable value', 1); + return lst.rest().first().first(); + }); + +PRIMITIVES['cadar'] = + new PrimProc('cadar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.first()) && + isPair(x.first().rest()) ); }, + 'cadar', 'cadarable value', 1); + return lst.first().rest().first(); + }); + +PRIMITIVES['cdaar'] = + new PrimProc('cdaar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.first()) && + isPair(x.first().first()) ); }, + 'cdaar', 'cdaarable value', 1); + return lst.first().first().rest(); + }); + +PRIMITIVES['cdadr'] = + new PrimProc('cdadr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.rest()) && + isPair(x.rest().first()) ); }, + 'cdadr', 'cdadrable value', 1); + return lst.rest().first().rest(); + }); + +PRIMITIVES['cddar'] = + new PrimProc('cddar', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.first()) && + isPair(x.first().rest()) ); }, + 'cddar', 'cddarable value', 1); + return lst.first().rest().rest(); + }); + +PRIMITIVES['caddr'] = + new PrimProc('caddr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.rest()) && + isPair(x.rest().rest()) ); }, + 'caddr', 'caddrable value', 1); + return lst.rest().rest().first(); + }); + +PRIMITIVES['cdddr'] = + new PrimProc('cdddr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.rest()) && + isPair(x.rest().rest()) ); }, + 'cdddr', 'cdddrable value', 1); + return lst.rest().rest().rest(); + }); + +PRIMITIVES['cadddr'] = + new PrimProc('cadddr', + 1, + false, false, + function(lst) { + check(lst, function(x) { return ( isPair(x) && + isPair(x.rest()) && + isPair(x.rest().rest()) && + isPair(x.rest().rest().rest()) ); }, + 'cadddr', 'cadddrable value', 1); + return lst.rest().rest().rest().first(); + }); + + +PRIMITIVES['rest'] = + new PrimProc('rest', + 1, + false, false, + function(lst) { + check(lst, function(x) { return isList(x) && !isEmpty(x); }, + 'rest', 'non-empty list', 1); + return lst.rest(); + }); + +PRIMITIVES['first'] = + new PrimProc('first', + 1, + false, false, + function(lst) { + check(lst, function(x) { return isList(x) && !isEmpty(x); }, + 'first', 'non-empty list', 1); + return lst.first(); + }); + +PRIMITIVES['second'] = + new PrimProc('second', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 2, 'second', 1); + return lst.rest().first(); + }); + +PRIMITIVES['third'] = + new PrimProc('third', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 3, 'third', 1); + return lst.rest().rest().first(); + }); + +PRIMITIVES['fourth'] = + new PrimProc('fourth', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 4, 'fourth', 1); + return lst.rest().rest().rest().first(); + }); + +PRIMITIVES['fifth'] = + new PrimProc('fifth', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 5, 'fifth', 1); + return lst.rest().rest().rest().rest().first(); + }); + +PRIMITIVES['sixth'] = + new PrimProc('sixth', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 6, 'sixth', 1); + return lst.rest().rest().rest().rest().rest().first(); + }); + +PRIMITIVES['seventh'] = + new PrimProc( + 'seventh', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 7, 'seventh', 1); + return lst.rest().rest().rest().rest().rest().rest().first(); + }); + +PRIMITIVES['eighth'] = + new PrimProc('eighth', + 1, + false, false, + function(lst) { + checkListOfLength(lst, 8, 'eighth', 1); + return lst.rest().rest().rest().rest().rest().rest().rest().first(); + }); + + +PRIMITIVES['length'] = + new PrimProc('length', + 1, + false, false, + function(lst) { + return jsnums.makeRational(length(lst)); + }); + + +PRIMITIVES['list?'] = new PrimProc('list?', 1, false, false, isList); + + +PRIMITIVES['list'] = + new PrimProc('list', + 0, + true, false, + types.list); + + +PRIMITIVES['list*'] = + new PrimProc('list*', + 1, + true, false, + function(anItem, otherItems) { + if (otherItems.length == 0) { + return anItem; + } + var allArgs = [anItem].concat(otherItems); + + var result = allArgs[allArgs.length - 1]; + for (var i = allArgs.length - 2 ; i >= 0; i--) { + result = types.cons(allArgs[i], result); + } + return result; + +// var lastListItem = otherItems.pop(); +// checkList(lastListItem, 'list*', otherItems.length+2, allArgs); + +// otherItems.unshift(anItem); +// return append([types.list(otherItems), lastListItem]); + }); + + +PRIMITIVES['list-ref'] = + new PrimProc('list-ref', + 2, + false, false, + function(origList, num) { + check(num, isNatural, 'list-ref', 'non-negative exact integer', 2, arguments); + + var lst = origList; + var n = jsnums.toFixnum(num); + for (var i = 0; i < n; i++) { + // According to the documentation of list-ref, we don't actually + // check the whole thing as a list. We rather do it as we walk + // along the cons chain. + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('list-ref: index ' + n + + ' is too large for list (not a proper list): ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + if (lst.isEmpty()) { + var msg = ('list-ref: index ' + n + + ' is too large for list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + lst = lst.rest(); + } + + + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('list-ref: index ' + n + + ' is too large for list (not a proper list): ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + return lst.first(); + }); + +PRIMITIVES['list-tail'] = + new PrimProc('list-tail', + 2, + false, false, + function(origList, num) { + check(num, isNatural, 'list-tail', 'non-negative exact integer', 2, arguments); + + var lst = origList; + var n = jsnums.toFixnum(num); + for (var i = 0; i < n; i++) { + // According to the documentation of list-tail, we don't actually + // check the whole thing as a list. We rather do it as we walk + // along the cons chain. + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('list-tail: index ' + n + + ' is too large for list (not a proper list): ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + if (lst.isEmpty()) { + var msg = ('list-tail: index ' + n + + ' is too large for list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + lst = lst.rest(); + } + return lst; + }); + + +PRIMITIVES['append'] = + new PrimProc('append', + 0, + true, false, + append); + + +PRIMITIVES['reverse'] = + new PrimProc('reverse', + 1, + false, false, + function(lst) { + checkList(lst, 'reverse', 1); + return lst.reverse(); + }); + + +PRIMITIVES['map'] = + new PrimProc('map', + 2, + true, false, + function(f, lst, arglists) { + var allArgs = [f, lst].concat(arglists); + arglists.unshift(lst); + check(f, isFunction, 'map', 'procedure', 1, allArgs); + arrayEach(arglists, function(x, i) {checkList(x, 'map', i+2, allArgs);}); + checkAllSameLength(arglists, 'map', allArgs); + + check(f, procArityContains(arglists.length), 'map', 'procedure (arity ' + arglists.length + ')', 1, allArgs); + + var mapHelp = function(f, args, acc) { + if (args[0].isEmpty()) { + return acc.reverse(); + } + + var argsFirst = []; + var argsRest = []; + for (var i = 0; i < args.length; i++) { + argsFirst.push(args[i].first()); + argsRest.push(args[i].rest()); + } + var result = CALL(f, argsFirst, + function(result) { + return onSingleResult(result, + function(result) { + return mapHelp(f, argsRest, types.cons(result, acc)); + }); + }); + return result; + } + return mapHelp(f, arglists, types.EMPTY); + }); + + +PRIMITIVES['andmap'] = + new PrimProc('andmap', + 2, + true, false, + function(f, lst, arglists) { + var allArgs = [f, lst].concat(arglists); + arglists.unshift(lst); + check(f, isFunction, 'andmap', 'procedure', 1, allArgs); + arrayEach(arglists, function(x, i) {checkList(x, 'andmap', i+2, allArgs);}); + checkAllSameLength(arglists, 'andmap', allArgs); + check(f, procArityContains(arglists.length), 'andmap', 'procedure (arity ' + arglists.length + ')', 1, allArgs); + + var andmapHelp = function(f, args) { + if ( args[0].isEmpty() ) { + return true; + } + + var argsFirst = []; + var argsRest = []; + for (var i = 0; i < args.length; i++) { + argsFirst.push(args[i].first()); + argsRest.push(args[i].rest()); + } + + return CALL(f, argsFirst, + function(result) { + if (argsRest[0].isEmpty()) { + return result; + } + return onSingleResult(result, + function(result) { + + return result && andmapHelp(f, argsRest); + }); + }); + } + return andmapHelp(f, arglists); + }); + + +PRIMITIVES['ormap'] = + new PrimProc('ormap', + 2, + true, false, + function(f, lst, arglists) { + var allArgs = [f, lst].concat(arglists); + arglists.unshift(lst); + check(f, isFunction, 'ormap', 'procedure', 1, allArgs); + arrayEach(arglists, function(x, i) {checkList(x, 'ormap', i+2, allArgs);}); + checkAllSameLength(arglists, 'ormap', allArgs); + + check(f, procArityContains(arglists.length), 'ormap', 'procedure (arity ' + arglists.length + ')', 1, allArgs); + + var ormapHelp = function(f, args) { + if ( args[0].isEmpty() ) { + return false; + } + + var argsFirst = []; + var argsRest = []; + for (var i = 0; i < args.length; i++) { + argsFirst.push(args[i].first()); + argsRest.push(args[i].rest()); + } + + return CALL(f, argsFirst, + function(result) { + if (argsRest[0].isEmpty()) { + return result; + } + return onSingleResult( + result, + function(result) { + return result || ormapHelp(f, argsRest); + }); + }); + } + return ormapHelp(f, arglists); + }); + + +PRIMITIVES['memq'] = + new PrimProc('memq', + 2, + false, false, + function(item, origList) { + var lst = origList; + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('memq: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + + if ( isEq(item, lst.first()) ) { + return lst; + } + lst = lst.rest(); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('memq: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['memv'] = + new PrimProc('memv', + 2, + false, false, + function(item, origList) { + var lst = origList; + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('memv: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + if ( isEqv(item, lst.first()) ) { + return lst; + } + lst = lst.rest(); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('memv: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['member'] = + new PrimProc('member', + 2, + false, false, + function(item, origList) { + var lst = origList; + //checkList(lst, 'member', 2, arguments); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('member: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + if ( isEqual(item, lst.first()) ) { + return lst; + } + lst = lst.rest(); + + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('member: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['memf'] = + new PrimProc('memf', + 2, + false, false, + function(f, initList) { + check(f, isFunction, 'memf', 'procedure', 1, arguments); + checkList(initList, 'memf', 2, arguments); + + var memfHelp = function(lst) { + if ( lst.isEmpty() ) { + return false; + } + + return CALL(f, [lst.first()], + function(result) { + if (result) { + return lst; + } + return memfHelp(lst.rest()); + }); + } + return memfHelp(initList); + }); + + +PRIMITIVES['assq'] = + new PrimProc('assq', + 2, + false, false, + function(item, origList) { + var lst = origList; + // checkListOf(lst, isPair, 'assq', 'pair', 2, arguments); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assq: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + if (! isPair(lst.first())) { + var msg = ('assq: non-pair found in list: ' + + types.toWrittenString(lst.first()) +' in ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + if ( isEq(item, lst.first().first()) ) { + return lst.first(); + } + lst = lst.rest(); + + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assq: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['assv'] = + new PrimProc('assv', + 2, + false, false, + function(item, origList) { + //checkListOf(lst, isPair, 'assv', 'pair', 2, arguments); + var lst = origList; + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assv: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + if (! isPair(lst.first())) { + var msg = ('assv: non-pair found in list: ' + + types.toWrittenString(lst.first()) +' in ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + if ( isEqv(item, lst.first().first()) ) { + return lst.first(); + } + lst = lst.rest(); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assv: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['assoc'] = + new PrimProc('assoc', + 2, + false, false, + function(item, origList) { + var lst = origList; + //checkListOf(lst, isPair, 'assoc', 'pair', 2, arguments); + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assoc: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + while ( !lst.isEmpty() ) { + if (! isPair(lst.first())) { + var msg = ('assoc: non-pair found in list: ' + + types.toWrittenString(lst.first()) +' in ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + if ( isEqual(item, lst.first().first()) ) { + return lst.first(); + } + lst = lst.rest(); + + if (! isPair(lst) && lst !== types.EMPTY) { + var msg = ('assoc: not a proper list: ' + + types.toWrittenString(origList)); + raise( types.incompleteExn(types.exnFailContract, + msg, + []) ); + } + } + return false; + }); + + +PRIMITIVES['remove'] = + new PrimProc('remove', + 2, + false, false, + function(item, lst) { + checkList(lst, 'remove', 2, arguments); + var originalLst = lst; + var result = types.EMPTY; + while ( !lst.isEmpty() ) { + if ( isEqual(item, lst.first()) ) { + return append([result.reverse(), lst.rest()]); + } else { + result = types.cons(lst.first(), result); + lst = lst.rest(); + } + } + return originalLst; + }); + + +PRIMITIVES['filter'] = + new PrimProc('filter', + 2, + false, false, + function(f, lst) { + check(f, procArityContains(1), 'filter', 'procedure (arity 1)', 1, arguments); + checkList(lst, 'filter', 2); + + var filterHelp = function(f, lst, acc) { + if ( lst.isEmpty() ) { + return acc.reverse(); + } + + return CALL(f, [lst.first()], + function(result) { + if (result) { + return filterHelp(f, lst.rest(), + types.cons(lst.first(), acc)); + } + else { + return filterHelp(f, lst.rest(), acc); + } + }); + } + return filterHelp(f, lst, types.EMPTY); + }); + +PRIMITIVES['foldl'] = + new PrimProc('foldl', + 3, + true, false, + function(f, initAcc, lst, arglists) { + arglists.unshift(lst); + var allArgs = [f, initAcc].concat(arglists); + check(f, isFunction, 'foldl', 'procedure', 1, allArgs); + arrayEach(arglists, function(x, i) {checkList(x, 'foldl', i+3, allArgs);}); + checkAllSameLength(arglists, 'foldl', allArgs); + + return foldHelp(f, initAcc, arglists); + }); + +PRIMITIVES['foldr'] = + new PrimProc('foldr', + 3, + true, false, + function(f, initAcc, lst, arglists) { + arglists.unshift(lst); + var allArgs = [f, initAcc].concat(arglists); + check(f, isFunction, 'foldr', 'procedure', 1, allArgs); + arrayEach(arglists, function(x, i) {checkList(x, 'foldr', i+3, allArgs);}); + checkAllSameLength(arglists, 'foldr', allArgs); + + for (var i = 0; i < arglists.length; i++) { + arglists[i] = arglists[i].reverse(); + } + + return foldHelp(f, initAcc, arglists); + }); + + +PRIMITIVES['quicksort'] = new PrimProc('quicksort', 2, false, false, quicksort('quicksort')); +PRIMITIVES['sort'] = new PrimProc('sort', 2, false, false, quicksort('sort')); + + + +PRIMITIVES['argmax'] = + new PrimProc('argmax', + 2, + false, false, + function(f, initList) { + var args = arguments + check(f, isFunction, 'argmax', 'procedure', 1, args); + check(initList, isPair, 'argmax', 'non-empty list', 2, args); + + var argmaxHelp = function(lst, curMaxVal, curMaxElt) { + if ( lst.isEmpty() ) { + return curMaxElt; + } + + return CALL(f, [lst.first()], + function(result) { + check(result, isReal, 'argmax', + 'procedure that returns real numbers', 1, args); + if (jsnums.greaterThan(result, curMaxVal)) { + return argmaxHelp(lst.rest(), result, lst.first()); + } + else { + return argmaxHelp(lst.rest(), curMaxVal, curMaxElt); + } + }); + } + return CALL(f, [initList.first()], + function(result) { + check(result, isReal, 'argmax', 'procedure that returns real numbers', 1, args); + return argmaxHelp(initList.rest(), result, initList.first()); + }); + }); + + +PRIMITIVES['argmin'] = + new PrimProc('argmin', + 2, + false, false, + function(f, initList) { + var args = arguments; + check(f, isFunction, 'argmin', 'procedure', 1, args); + check(initList, isPair, 'argmin', 'non-empty list', 2, args); + + var argminHelp = function(lst, curMaxVal, curMaxElt) { + if ( lst.isEmpty() ) { + return curMaxElt; + } + + return CALL(f, [lst.first()], + function(result) { + check(result, isReal, 'argmin', + 'procedure that returns real numbers', 1, args); + if (jsnums.lessThan(result, curMaxVal)) { + return argminHelp(lst.rest(), result, lst.first()); + } + else { + return argminHelp(lst.rest(), curMaxVal, curMaxElt); + } + }); + } + return CALL(f, [initList.first()], + function(result) { + check(result, isReal, 'argmin', 'procedure that returns real numbers', 1, args); + return argminHelp(initList.rest(), result, initList.first()); + }); + }); + + +PRIMITIVES['build-list'] = + new PrimProc('build-list', + 2, + false, false, + function(num, f) { + check(num, isNatural, 'build-list', 'non-negative exact integer', 1, arguments); + check(f, isFunction, 'build-list', 'procedure', 2, arguments); + + var buildListHelp = function(n, acc) { + if ( jsnums.greaterThanOrEqual(n, num) ) { + return acc.reverse(); + } + + return CALL(f, [n], + function (result) { + return buildListHelp(n+1, types.cons(result, acc)); + }); + } + return buildListHelp(0, types.EMPTY); + }); + + +/********************** + *** Box Primitives *** + **********************/ + + +PRIMITIVES['box'] = new PrimProc('box', 1, false, false, types.box); + +PRIMITIVES['box-immutable'] = new PrimProc('box-immutable', 1, false, false, types.boxImmutable); + +PRIMITIVES['unbox'] = + new PrimProc('unbox', + 1, + false, false, + function(box) { + check(box, isBox, 'unbox', 'box', 1); + return box.unbox(); + }); + + +PRIMITIVES['set-box!'] = + new PrimProc('set-box!', + 2, + false, false, + function(box, newVal) { + check(box, function(x) { return isBox(x) && x.mutable; }, 'set-box!', 'mutable box', 1, arguments); + box.set(newVal); + return types.VOID; + }); + + + +/**************************** + *** Hashtable Primitives *** + ****************************/ + + +PRIMITIVES['make-hash'] = + new CasePrimitive('make-hash', + [new PrimProc('make-hash', 0, false, false, function() { return types.hash(types.EMPTY); }), + new PrimProc('make-hash', + 1, + false, false, + function(lst) { + checkListOf(lst, isPair, 'make-hash', 'list of pairs', 1); + return types.hash(lst); + }) ]); + +PRIMITIVES['make-hasheq'] = + new CasePrimitive('make-hasheq', + [new PrimProc('make-hasheq', 0, false, false, function() { return types.hashEq(types.EMPTY); }), + new PrimProc('make-hasheq', + 1, + false, false, + function(lst) { + checkListOf(lst, isPair, 'make-hasheq', 'list of pairs', 1); + return types.hashEq(lst); + }) ]); + +PRIMITIVES['hash-set!'] = + new PrimProc('hash-set!', + 3, + false, false, + function(obj, key, val) { + check(obj, isHash, 'hash-set!', 'hash', 1, arguments); + obj.hash.put(key, val); + return types.VOID; + }); + +PRIMITIVES['hash-ref'] = + new CasePrimitive('hash-ref', + [new PrimProc('hash-ref', + 2, + false, false, + function(obj, key) { + check(obj, isHash, 'hash-ref', 'hash', 1, arguments); + + if ( !obj.hash.containsKey(key) ) { + var msg = 'hash-ref: no value found for key: ' + types.toWrittenString(key); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return obj.hash.get(key); + }), + new PrimProc('hash-ref', + 3, + false, false, + function(obj, key, defaultVal) { + check(obj, isHash, 'hash-ref', 'hash', 1, arguments); + + if (obj.hash.containsKey(key)) { + return obj.hash.get(key); + } + else { + if (isFunction(defaultVal)) { + return CALL(defaultVal, [], id); + } + return defaultVal; + } + }) ]); + +PRIMITIVES['hash-remove!'] = + new PrimProc('hash-remove', + 2, + false, false, + function(obj, key) { + check(obj, isHash, 'hash-remove!', 'hash', 1, arguments); + obj.hash.remove(key); + return types.VOID; + }); + +PRIMITIVES['hash-map'] = + new PrimProc('hash-map', + 2, + false, false, + function(ht, f) { + check(ht, isHash, 'hash-map', 'hash', 1, arguments); + check(f, isFunction, 'hash-map', 'procedure', 2, arguments); + + var keys = ht.hash.keys(); + var hashMapHelp = function(i, acc) { + if (i >= keys.length) { + return acc; + } + + var val = ht.hash.get(keys[i]); + return CALL(f, [keys[i], val], + function(result) { + return hashMapHelp(i+1, types.cons(result, acc)); + }); + } + return hashMapHelp(0, types.EMPTY); + }); + + +PRIMITIVES['hash-for-each'] = + new PrimProc('hash-for-each', + 2, + false, false, + function(ht, f) { + check(ht, isHash, 'hash-for-each', 'hash', 1, arguments); + check(f, isFunction, 'hash-for-each', 'procedure', 2, arguments); + + var keys = ht.hash.keys(); + var hashForEachHelp = function(i) { + if (i >= keys.length) { + return types.VOID; + } + + var val = ht.hash.get(keys[i]); + return CALL(f, [keys[i], val], + function(result) { + return hashForEachHelp(i+1); + }); + } + return hashForEachHelp(0); + }); + + + +/************************* + *** String Primitives *** + *************************/ + + +var makeStringImpl = function(n, c) { + check(n, isNatural, 'make-string', 'non-negative exact integer', 1, arguments); + check(c, isChar, 'make-string', 'char', 2, arguments); + var ret = []; + for (var i = 0; jsnums.lessThan(i, n); i++) { + ret.push(c.val); + } + return types.string(ret); +}; + +PRIMITIVES['make-string'] = + new CasePrimitive( + 'make-string', + [new PrimProc('make-string', + 2, + false, false, + makeStringImpl), + new PrimProc('make-string', + 1, + false, false, + function(n) { + return makeStringImpl(n, types.char(String.fromCharCode(0))); + })]); + + + +PRIMITIVES['replicate'] = + new PrimProc('replicate', + 2, + false, false, + function(n, str) { + check(n, isNatural, 'replicate', 'non-negative exact integer', 1, arguments); + check(str, isString, 'replicate', 'string', 2, arguments); + + var ret = ""; + var primStr = str.toString(); + for (var i = 0; jsnums.lessThan(i, n); i++) { + ret += primStr; + } + return types.string(ret); + }); + + +PRIMITIVES['string'] = + new PrimProc('string', + 0, + true, false, + function(chars) { + arrayEach(chars, function(c, i) {check(c, isChar, 'string', 'char', i+1, chars);}); + + var ret = []; + for (var i = 0; i < chars.length; i++) { + ret.push(chars[i].val); + } + return types.string(ret); + }); + + +PRIMITIVES['string-length'] = + new PrimProc('string-length', 1, false, false, + function(str) { + check(str, isString, 'string-length', 'string', 1); + return str.toString().length; + }); + + +PRIMITIVES['string-ref'] = + new PrimProc('string-ref', + 2, + false, false, + function(str, num) { + check(str, isString, 'string-ref', 'string', 1, arguments); + check(num, isNatural, 'string-ref', 'non-negative exact integer', 2, arguments); + + str = str.toString(); + var n = jsnums.toFixnum(num); + if (n >= str.length) { + var msg = ('string-ref: index ' + n + ' out of range ' + + '[0, ' + (str.length-1) + '] for string: ' + + types.toWrittenString(str)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return types.char(str.charAt(n)); + }); + + +PRIMITIVES['string=?'] = + new PrimProc('string=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + arrayEach(strs, function(str, i) {check(str, isString, 'string=?', 'string', i+1, strs);}); + + return compare(strs, function(strA, strB) {return strA.toString() === strB.toString();}); + }); + + +PRIMITIVES['string-ci=?'] = + new PrimProc('string-ci=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + + for(var i = 0; i < strs.length; i++) { + check(strs[i], isString, 'string-ci=?', 'string', i+1, strs); + strs[i] = strs[i].toString().toLowerCase(); + } + + return compare(strs, function(strA, strB) {return strA === strB;}); + }); + + +PRIMITIVES['string?'] = + new PrimProc('string>?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + arrayEach(strs, function(str, i) {check(str, isString, 'string>?', 'string', i+1, strs);}); + + return compare(strs, function(strA, strB) {return strA.toString() > strB.toString();}); + }); + + +PRIMITIVES['string<=?'] = + new PrimProc('string<=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + arrayEach(strs, function(str, i) {check(str, isString, 'string<=?', 'string', i+1, strs);}); + + return compare(strs, function(strA, strB) {return strA.toString() <= strB.toString();}); + }); + + +PRIMITIVES['string>=?'] = + new PrimProc('string>=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + arrayEach(strs, function(str, i) {check(str, isString, 'string>=?', 'string', i+1, strs);}); + + return compare(strs, function(strA, strB) {return strA.toString() >= strB.toString();}); + }); + + +PRIMITIVES['string-ci?'] = + new PrimProc('string-ci>?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + + for (var i = 0; i < strs.length; i++) { + check(strs[i], isString, 'string-ci>?', 'string', i+1, strs); + strs[i] = strs[i].toString().toLowerCase(); + } + + return compare(strs, function(strA, strB) {return strA > strB;}); + }); + + +PRIMITIVES['string-ci<=?'] = + new PrimProc('string-ci<=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + + for (var i = 0; i < strs.length; i++) { + check(strs[i], isString, 'string-ci<=?', 'string', i+1, strs); + strs[i] = strs[i].toString().toLowerCase(); + } + + return compare(strs, function(strA, strB) {return strA <= strB;}); + }); + + +PRIMITIVES['string-ci>=?'] = + new PrimProc('string-ci>=?', + 2, + true, false, + function(str1, str2, strs) { + strs.unshift(str2); + strs.unshift(str1); + + for (var i = 0; i < strs.length; i++) { + check(strs[i], isString, 'string-ci>=?', 'string', i+1, strs); + strs[i] = strs[i].toString().toLowerCase(); + } + + return compare(strs, function(strA, strB) {return strA >= strB;}); + }); + + +PRIMITIVES['substring'] = + new CasePrimitive('substring', + [new PrimProc('substring', + 2, + false, false, + function(str, theStart) { + check(str, isString, 'substring', 'string', 1, arguments); + check(theStart, isNatural, 'substring', 'non-negative exact integer', 2, arguments); + str = str.toString(); + var start = jsnums.toFixnum(theStart); + if (start > str.length) { + var msg = ('substring: starting index ' + start + ' out of range ' + + '[0, ' + str.length + '] for string: ' + types.toWrittenString(str)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + else { + return types.string( str.substring(jsnums.toFixnum(start)) ); + } + }), + new PrimProc('substring', + 3, + false, false, + function(str, theStart, theEnd) { + check(str, isString, 'substring', 'string', 1, arguments); + check(theStart, isNatural, 'substring', 'non-negative exact integer', 2, arguments); + check(theEnd, isNatural, 'substring', 'non-negative exact integer', 3, arguments); + str = str.toString(); + var start = jsnums.toFixnum(theStart); + var end = jsnums.toFixnum(theEnd); + if (start > str.length) { + var msg = ('substring: starting index ' + start + ' out of range ' + + '[0, ' + str.length + '] for string: ' + types.toWrittenString(str)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + if (end < start || end > str.length) { + var msg = ('substring: ending index ' + end + ' out of range ' + '[' + start + + ', ' + str.length + '] for string: ' + types.toWrittenString(str)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return types.string( str.substring(start, end) ); + }) ]); + + +PRIMITIVES['string-append'] = + new PrimProc("string-append", + 0, + true, false, + function(args) { + arrayEach(args, + function(str, i) { + check(str, isString, 'string-append', 'string', i+1, args); + }); + + for (var i = 0; i < args.length; i++) { + args[i] = args[i].toString(); + } + return types.string(args.join("")); + }); + + +PRIMITIVES['string->list'] = + new PrimProc('string->list', + 1, + false, false, + function(str) { + check(str, isString, 'string->list', 'string', 1); + str = str.toString(); + var lst = types.EMPTY; + for (var i = str.length-1; i >= 0; i--) { + lst = types.cons(types.char(str.charAt(i)), lst); + } + return lst; + }); + + +PRIMITIVES['list->string'] = + new PrimProc('list->string', + 1, + false, false, + function(lst) { + checkListOf(lst, isChar, 'list->string', 'char', 1); + + var ret = []; + while( !lst.isEmpty() ) { + ret.push(lst.first().val); + lst = lst.rest(); + } + return types.string(ret); + }); + + +PRIMITIVES['string-copy'] = + new PrimProc('string-copy', + 1, + false, false, + function(str) { + check(str, isString, 'string-copy', 'string', 1); + return types.string(str.toString()); + }); + + + +PRIMITIVES['string->symbol'] = + new PrimProc('string->symbol', + 1, + false, false, + function(str) { + check(str, isString, 'string->symbol', 'string', 1); + return types.symbol(str.toString()); + }); + + +PRIMITIVES['symbol->string'] = + new PrimProc('symbol->string', + 1, + false, false, + function(symb) { + check(symb, isSymbol, 'symbol->string', 'symbol', 1); + return types.string(symb.toString()); + }); + + +PRIMITIVES['format'] = + new PrimProc('format', 1, true, false, + function(formatStr, args) { + check(formatStr, isString, 'format', 'string', 1, [formatStr].concat(args)); + formatStr = formatStr.toString(); + return types.string( helpers.format(formatStr, args, 'format') ); + }); + + +PRIMITIVES['printf'] = + new PrimProc('printf', 1, true, true, + function(state, formatStr, args) { + check(formatStr, isString, 'printf', 'string', 1, [formatStr].concat(args)); + formatStr = formatStr.toString(); + var msg = helpers.format(formatStr, args, 'printf'); + state.getDisplayHook()(msg); + state.v = types.VOID; + }); + + +PRIMITIVES['string->int'] = + new PrimProc('string->int', + 1, + false, false, + function(str) { + check(str, function(s) {return isString(s) && s.length == 1;}, + 'string->int', '1-letter string', 1); + str = str.toString(); + return str.charCodeAt(0); + }); + + +PRIMITIVES['int->string'] = + new PrimProc('int->string', + 1, + false, false, + function(num) { + check(num, function(x) { + if ( !isInteger(x) ) { + return false; + } + var n = jsnums.toFixnum(x); + return ((n >= 0 && n < 55296) || + (n > 57343 && n <= 1114111)); + }, + 'int->string', + 'exact integer in [0,55295] or [57344,1114111]', + 1); + + return types.string( String.fromCharCode(jsnums.toFixnum(num)) ); + }); + + +PRIMITIVES['explode'] = + new PrimProc('explode', + 1, + false, false, + function(str) { + check(str, isString, 'explode', 'string', 1); + str = str.toString(); + var ret = types.EMPTY; + for (var i = str.length-1; i >= 0; i--) { + ret = types.cons( types.string(str.charAt(i)), ret ); + } + return ret; + }); + +PRIMITIVES['implode'] = + new PrimProc('implode', + 1, + false, false, + function(lst) { + checkListOf(lst, function(x) { return isString(x) && x.length == 1; }, + 'implode', 'list of 1-letter strings', 1); + var ret = []; + while ( !lst.isEmpty() ) { + ret.push( lst.first().toString() ); + lst = lst.rest(); + } + return types.string(ret); + }); + + +PRIMITIVES['string-alphabetic?'] = + new PrimProc('string-alphabetic?', + 1, + false, false, + function(str) { + check(str, isString, 'string-alphabetic?', 'string', 1); + str = str.toString(); + return isAlphabeticString(str); + }); + + +PRIMITIVES['string-ith'] = + new PrimProc('string-ith', + 2, + false, false, + function(str, num) { + check(str, isString, 'string-ith', 'string', 1, arguments); + check(num, function(x) { return isNatural(x) && jsnums.lessThan(x, str.length); }, 'string-ith', + 'exact integer in [0, length of the given string minus 1 (' + (str.length-1) + ')]', 2, arguments); + str = str.toString(); + return types.string( str.charAt(jsnums.toFixnum(num)) ); + }); + + +PRIMITIVES['string-lower-case?'] = + new PrimProc('string-lower-case?', + 1, + false, false, + function(str) { + check(str, isString, 'string-lower-case?', 'string', 1); + var primStr = str.toString(); + return isAlphabeticString(str) && primStr.toLowerCase() === primStr; + }); + + +PRIMITIVES['string-numeric?'] = + new PrimProc('string-numeric?', + 1, + false, false, + function(str) { + check(str, isString, 'string-numeric?', 'string', 1); + str = str.toString(); + return isNumericString(str); + }); + + +PRIMITIVES['string-upper-case?'] = + new PrimProc('string-upper-case?', + 1, + false, false, + function(str) { + check(str, isString, 'string-upper-case?', 'string', 1); + var primStr = str.toString(); + return isAlphabeticString(str) && primStr.toUpperCase() === primStr; + }); + + +PRIMITIVES['string-whitespace?'] = + new PrimProc('string-whitespace?', + 1, + false, false, + function(str) { + check(str, isString, 'string-whitespace?', 'string', 1); + str = str.toString(); + return isWhitespaceString(str); + }); + + +PRIMITIVES['build-string'] = + new PrimProc('build-string', + 2, + false, false, + function(num, f) { + check(num, isNatural, 'build-string', 'non-negative exact integer', 1, arguments); + check(f, isFunction, 'build-string', 'procedure', 2, arguments); + + var buildStringHelp = function(n, acc) { + if ( jsnums.greaterThanOrEqual(n, num) ) { + return types.string(acc); + } + + return CALL(f, [n], + function(res) { + check(res, isChar, 'build-string', + 'procedure that returns a char', 2); + acc.push(res.val) + return buildStringHelp(n+1, acc); + }); + } + return buildStringHelp(0, []); + }); + + +PRIMITIVES['string->immutable-string'] = + new PrimProc('string->immutable-string', + 1, + false, false, + function(str) { + check(str, isString, 'string->immutable-string', 'string', 1); + return str.toString(); + }); + + +PRIMITIVES['string-set!'] = + new PrimProc('string-set!', + 3, + false, false, + function(str, k, c) { + check(str, function(x) { return isMutableString(x); }, + 'string-set!', 'mutable string', 1, arguments); + check(k, isNatural, 'string-set!', 'non-negative exact integer', 2, arguments); + check(c, isChar, 'string-set!', 'char', 3, arguments); + + if ( jsnums.greaterThanOrEqual(k, str.length) ) { + var msg = ('string-set!: index ' + k + ' out of range ' + + '[0, ' + (str.length-1) + '] for string: ' + + types.toWrittenString(str)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + str.set(jsnums.toFixnum(k), c.val); + return types.VOID; + }); + + +PRIMITIVES['string-fill!'] = + new PrimProc('string-fill!', + 2, + false, false, + function(str, c) { + check(str, function(x) { return isMutableString(x); }, + 'string-fill!', 'mutable string', 1, arguments); + check(c, isChar, 'string-fill!', 'char', 2, arguments); + + for (var i = 0; i < str.length; i++) { + str.set(i, c.val); + } + return types.VOID; + }); + + + +////////////////////////////////////////////////////////////////////// +// Immutable cyclic data +PRIMITIVES['make-reader-graph'] = + new PrimProc('make-reader-graph', 1, false, false, + function(x) { + var result = types.readerGraph(x, types.makeLowLevelEqHash(), 0); + return result; + }); + + +PRIMITIVES['make-placeholder'] = + new PrimProc('make-placeholder', 1, false, false, + function(x) { return types.placeholder(x); }); + + +PRIMITIVES['placeholder-set!'] = + new PrimProc('placeholder-set!', 2, false, false, + function(pl, x) { + check(pl, types.isPlaceholder, + "placeholder-set!", "placeholder", 1); + pl.set(x); + return types.VOID; + }); + +PRIMITIVES['placeholder-get'] = + new PrimProc('placeholder-get', 1, false, false, + function(pl) { + check(pl, types.isPlaceholder, + "placeholder-get", "placeholder", 1); + return pl.get(); + }); + + + + +////////////////////////////////////////////////////////////////////// + + + + +/****************************** + *** Byte String Primitives *** + ******************************/ + + +PRIMITIVES['make-bytes'] = + new CasePrimitive('make-bytes', + [new PrimProc('make-bytes', + 1, + false, false, + function(k) { + check(k, isNatural, 'make-bytes', 'non-negative exact integer', 1); + + var ret = []; + for (var i = 0; i < jsnums.toFixnum(k); i++) { + ret.push(0); + } + return types.bytes(ret, true); + }), + new PrimProc('make-bytes', + 2, + false, false, + function(k, b) { + check(k, isNatural, 'make-bytes', 'non-negative exact integer', 1, arguments); + check(b, isByte, 'make-bytes', 'byte', 2, arguments); + + var ret = []; + for (var i = 0; i < jsnums.toFixnum(k); i++) { + ret.push(b); + } + return types.bytes(ret, true); + }) ]); + + +PRIMITIVES['bytes'] = + new PrimProc('bytes', + 0, + true, false, + function(args) { + arrayEach(args, function(b, i) {check(b, isByte, 'bytes', 'byte', i+1, args);}); + return types.bytes(args, true); + }); + + +PRIMITIVES['bytes->immutable-bytes'] = + new PrimProc('bytes->immutable-bytes', + 1, + false, false, + function(bstr) { + check(bstr, isByteString, 'bytes->immutable-bytes', 'byte string', 1); + if ( bstr.mutable ) { + return bstr.copy(false); + } + else { + return bstr; + } + }); + + +PRIMITIVES['bytes-length'] = + new PrimProc('bytes-length', + 1, + false, false, + function(bstr) { + check(bstr, isByteString, 'bytes-length', 'byte string', 1); + return bstr.length(); + }); + + +PRIMITIVES['bytes-ref'] = + new PrimProc('bytes-ref', + 2, + false, false, + function(bstr, num) { + check(bstr, isByteString, 'bytes-ref', 'byte string', 1, arguments); + check(num, isNatural, 'bytes-ref', 'non-negative exact integer', 2, arguments); + + var n = jsnums.toFixnum(num); + if ( n >= bstr.length() ) { + var msg = ('bytes-ref: index ' + n + ' out of range ' + + '[0, ' + (bstr.length-1) + '] for byte-string: ' + + types.toWrittenString(bstr)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return bstr.get(n); + }); + + +PRIMITIVES['bytes-set!'] = + new PrimProc('bytes-set!', + 3, + false, false, + function(bstr, num, b) { + check(bstr, function(x) { return isByteString(x) && x.mutable; }, + 'bytes-set!', 'mutable byte string', 1, arguments); + check(num, isNatural, 'bytes-set!', 'non-negative exact integer', 2, arguments); + check(b, isByte, 'bytes-set!', 'byte', 3, arguments); + + var n = jsnums.toFixnum(num); + if ( n >= bstr.length() ) { + var msg = ('bytes-set!: index ' + n + ' out of range ' + + '[0, ' + (bstr.length-1) + '] for byte-string: ' + + types.toWrittenString(bstr)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + bstr.set(n, b); + return types.VOID; + }); + + +PRIMITIVES['subbytes'] = + new CasePrimitive('subbytes', + [new PrimProc('subbytes', + 2, + false, false, + function(bstr, theStart) { + check(bstr, isByteString, 'subbytes', 'bytes string', 1, arguments); + check(theStart, isNatural, 'subbytes', 'non-negative exact integer', 2, arguments); + + var start = jsnums.toFixnum(theStart); + if (start > bstr.length()) { + var msg = ('subbytes: starting index ' + start + ' out of range ' + + '[0, ' + bstr.length + '] for byte-string: ' + + types.toWrittenString(bstr)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + else { + return bstr.subbytes(jsnums.toFixnum(start)); + } + }), + new PrimProc('subbytes', + 3, + false, false, + function(bstr, theStart, theEnd) { + check(bstr, isByteString, 'subbytes', 'byte string', 1, arguments); + check(theStart, isNatural, 'subbytes', 'non-negative exact integer', 2, arguments); + check(theEnd, isNatural, 'subbytes', 'non-negative exact integer', 3, arguments); + + var start = jsnums.toFixnum(theStart); + var end = jsnums.toFixnum(theEnd); + if (start > bstr.length()) { + var msg = ('subbytes: starting index ' + start + ' out of range ' + + '[0, ' + bstr.length() + '] for byte-string: ' + + types.toWrittenString(bstr)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + if (end < start || end > bstr.length()) { + var msg = ('subbytes: ending index ' + end + ' out of range ' + '[' + start + + ', ' + bstr.length() + '] for byte-string: ' + + types.toWrittenString(bstr)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + else { + return bstr.subbytes(start, end); + } + }) ]); + + +PRIMITIVES['bytes-copy'] = + new PrimProc('bytes-copy', + 1, + false, false, + function(bstr) { + check(bstr, isByteString, 'bytes-copy', 'byte string', 1); + return bstr.copy(true); + }); + + +PRIMITIVES['bytes-fill!'] = + new PrimProc('bytes-fill!', + 2, + false, false, + function(bstr, b) { + check(bstr, function(x) { return isByteString(x) && x.mutable; }, + 'bytes-fill!', 'mutable byte string', 1, arguments); + check(b, isByte, 'bytes-fill!', 'byte', 2, arguments); + + for (var i = 0; i < bstr.length(); i++) { + bstr.set(i, b); + } + return types.VOID; + }); + + +PRIMITIVES['bytes-append'] = + new PrimProc('bytes-append', + 0, + true, false, + function(args) { + arrayEach(args, function(x, i) { check(x, isByteString, 'bytes-append', 'byte string', i+1, args); }); + + var ret = []; + for (var i = 0; i < args.length; i++) { + ret = ret.concat(args[i].bytes); + } + return types.bytes(ret, true); + }); + + +PRIMITIVES['bytes->list'] = + new PrimProc('bytes->list', + 1, + false, false, + function(bstr) { + check(bstr, isByteString, 'bytes->list', 'byte string', 1); + + var ret = types.EMPTY; + for (var i = bstr.length()-1; i >= 0; i--) { + ret = types.cons(bstr.get(i), ret); + } + return ret; + }); + + +PRIMITIVES['list->bytes'] = + new PrimProc('list->bytes', + 1, + false, false, + function(lst) { + checkListOf(lst, isByte, 'list->bytes', 'byte', 1); + + var ret = []; + while ( !lst.isEmpty() ) { + ret.push(lst.first()); + lst = lst.rest(); + } + return types.bytes(ret, true); + }); + + +PRIMITIVES['bytes=?'] = + new PrimProc('bytes=?', + 2, + true, false, + function(bstr1, bstr2, bstrs) { + bstrs.unshift(bstr2); + bstrs.unshift(bstr1); + arrayEach(bstrs, function(x, i) { check(x, isByteString, 'bytes=?', 'byte string', i+1, bstrs); }); + + return compare(bstrs, function(bstrA, bstrB) { return bstrA.toString() === bstrB.toString(); }); + }); + + +PRIMITIVES['bytes?'] = + new PrimProc('bytes>?', + 2, + true, false, + function(bstr1, bstr2, bstrs) { + bstrs.unshift(bstr2); + bstrs.unshift(bstr1); + arrayEach(bstrs, function(x, i) { check(x, isByteString, 'bytes>?', 'byte string', i+1, bstrs); }); + + return compare(bstrs, function(bstrA, bstrB) { return bstrA.toString() > bstrB.toString(); }); + }); + + + + +/************************* + *** Vector Primitives *** + *************************/ + +var makeVectorImpl = function(size, content) { + check(size, isNatural, 'make-vector', 'non-negative exact integer', 1, arguments); + var s = jsnums.toFixnum(size); + var ret = []; + for (var i = 0; i < s; i++) { + ret.push(content); + } + return types.vector(ret); +}; + +PRIMITIVES['make-vector'] = new CasePrimitive + ("make-vector", + [new PrimProc('make-vector', + 2, + false, false, + makeVectorImpl), + new PrimProc('make-vector', + 1, + false, false, + function(size) { return makeVectorImpl(size, jsnums.fromFixnum(0)); })]); + + +PRIMITIVES['vector'] = + new PrimProc('vector', + 0, + true, false, + function(args) { + return types.vector(args); + }); + + +PRIMITIVES['vector-length'] = + new PrimProc('vector-length', + 1, + false, false, + function(vec) { + check(vec, isVector, 'vector-length', 'vector', 1); + return vec.length(); + }); + + +PRIMITIVES['vector-ref'] = + new PrimProc('vector-ref', + 2, + false, false, + function(vec, index) { + check(vec, isVector, 'vector-ref', 'vector', 1, arguments); + check(index, isNatural, 'vector-ref', 'non-negative exact integer', 2, arguments); + + var i = jsnums.toFixnum(index); + if (i >= vec.length()) { + var msg = ('vector-ref: index ' + i + ' out of range ' + + '[0, ' + (vec.length()-1) + '] for vector: ' + + types.toWrittenString(vec)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + return vec.ref(i); + }); + + +PRIMITIVES['vector-set!'] = + new PrimProc('vector-set!', + 3, + false, false, + function(vec, index, val) { + check(vec, isVector, 'vector-set!', 'vector', 1, arguments); + check(index, isNatural, 'vector-set!', 'non-negative exact integer', 2, arguments); + + var i = jsnums.toFixnum(index); + if (i >= vec.length()) { + var msg = ('vector-set!: index ' + i + ' out of range ' + + '[0, ' + (vec.length()-1) + '] for vector: ' + + types.toWrittenString(vec)); + raise( types.incompleteExn(types.exnFailContract, msg, []) ); + } + vec.set(i, val); + return types.VOID; + }); + + +PRIMITIVES['vector->list'] = + new PrimProc('vector->list', + 1, + false, false, + function(vec) { + check(vec, isVector, 'vector->list', 'vector', 1); + return vec.toList(); + }); + + +PRIMITIVES['list->vector'] = + new PrimProc('list->vector', + 1, + false, false, + function(lst) { + checkList(lst, 'list->vector', 1); + return types.vector( helpers.schemeListToArray(lst) ); + }); + + +PRIMITIVES['build-vector'] = + new PrimProc('build-vector', + 2, + false, false, + function(num, f) { + check(num, isNatural, 'build-vector', 'non-negative exact integer', 1, arguments); + check(f, isFunction, 'build-vector', 'procedure', 2, arguments); + + var buildVectorHelp = function(n, acc) { + if ( jsnums.greaterThanOrEqual(n, num) ) { + return types.vector(acc); + } + + return CALL(f, [n], + function (result) { + acc.push(result) + return buildVectorHelp(n+1, acc); + }); + } + return buildVectorHelp(0, []); + }); + + + +/*********************** + *** Char Primitives *** + ***********************/ + + +PRIMITIVES['char=?'] = + new PrimProc('char=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char=?', 'char', i+1, chars);}); + + return compare(chars, function(c1, c2) {return c1.val === c2.val;}); + }); + + +PRIMITIVES['char?'] = + new PrimProc('char>?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char>?', 'char', i+1, chars);}); + + return compare(chars, function(c1, c2) {return c1.val > c2.val;}); + }); + + +PRIMITIVES['char<=?'] = + new PrimProc('char<=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char<=?', 'char', i+1, chars);}); + + return compare(chars, function(c1, c2) {return c1.val <= c2.val;}); + }); + + +PRIMITIVES['char>=?'] = + new PrimProc('char>=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char>=?', 'char', i+1, chars);}); + + return compare(chars, function(c1, c2) {return c1.val >= c2.val;}); + }); + + +PRIMITIVES['char-ci=?'] = + new PrimProc('char-ci=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char-ci=?', 'char', i+1, chars);}); + + return compare(chars, + function(c1, c2) { + return c1.val.toLowerCase() === c2.val.toLowerCase(); + }); + }); + + +PRIMITIVES['char-ci?'] = + new PrimProc('char-ci>?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char-ci>?', 'char', i+1, chars);}); + + return compare(chars, + function(c1, c2) { + return c1.val.toLowerCase() > c2.val.toLowerCase(); + }); + }); + + +PRIMITIVES['char-ci<=?'] = + new PrimProc('char-ci<=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char-ci<=?', 'char', i+1, chars);}); + + return compare(chars, + function(c1, c2) { + return c1.val.toLowerCase() <= c2.val.toLowerCase(); + }); + }); + + +PRIMITIVES['char-ci>=?'] = + new PrimProc('char-ci>=?', + 2, + true, false, + function(char1, char2, chars) { + chars.unshift(char2); + chars.unshift(char1); + arrayEach(chars, function(c, i) {check(c, isChar, 'char-ci>=?', 'char', i+1, chars);}); + + return compare(chars, + function(c1, c2) { + return c1.val.toLowerCase() >= c2.val.toLowerCase(); + }); + }); + + +PRIMITIVES['char-alphabetic?'] = + new PrimProc('char-alphabetic?', + 1, + false, false, + function(c) { + check(c, isChar, 'char-alphabetic?', 'char', 1); + return isAlphabeticString(c.val); + }); + + +PRIMITIVES['char-numeric?'] = + new PrimProc('char-numeric?', + 1, + false, false, + function(c) { + check(c, isChar, 'char-numeric?', 'char', 1); + return (c.val >= '0' && c.val <= '9'); + }); + + +PRIMITIVES['char-whitespace?'] = + new PrimProc('char-whitespace?', + 1, + false, false, + function(c) { + check(c, isChar, 'char-whitespace?', 'char', 1); + return isWhitespaceString(c.val); + }); + + +PRIMITIVES['char-upper-case?'] = + new PrimProc('char-upper-case?', + 1, + false, false, + function(c) { + check(c, isChar, 'char-upper-case?', 'char', 1); + return (isAlphabeticString(c.val) && c.val.toUpperCase() === c.val); + }); + + +PRIMITIVES['char-lower-case?'] = + new PrimProc('char-lower-case?', + 1, + false, false, + function(c) { + check(c, isChar, 'char-lower-case?', 'char', 1); + return (isAlphabeticString(c.val) && c.val.toLowerCase() === c.val); + }); + + +PRIMITIVES['char->integer'] = + new PrimProc('char->integer', + 1, + false, false, + function(c) { + check(c, isChar, 'char->integer', 'char', 1); + return c.val.charCodeAt(0); + }); + + +PRIMITIVES['integer->char'] = + new PrimProc('integer->char', + 1, + false, false, + function(num) { + check(num, function(x) { + if ( !isNatural(x) ) { + return false; + } + var n = jsnums.toFixnum(x); + return ((n >= 0 && n < 55296) || + (n > 57343 && n <= 1114111)); + }, + 'integer->char', + 'exact integer in [0,#x10FFFF], not in [#xD800,#xDFFF]', + 1); + + return types.char( String.fromCharCode(jsnums.toFixnum(num)) ); + }); + + +PRIMITIVES['char-upcase'] = + new PrimProc('char-upcase', + 1, + false, false, + function(c) { + check(c, isChar, 'char-upcase', 'char', 1); + return types.char( c.val.toUpperCase() ); + }); + + +PRIMITIVES['char-downcase'] = + new PrimProc('char-downcase', + 1, + false, false, + function(c) { + check(c, isChar, 'char-downcase', 'char', 1); + return types.char( c.val.toLowerCase() ); + }); + + + + + +var callCCPrim = new types.PrimProc('call/cc', + 1, + false, true, + function(aState, f) { + var continuationClosure = + state.captureContinuationClosure(aState); + aState.pushValue(continuationClosure); + aState.v = f; + aState.pushControl( + new control.CallControl(1)); + }); + +PRIMITIVES['call/cc'] = callCCPrim; +PRIMITIVES['call-with-current-continuation'] = callCCPrim; + + + + +////////////////////////////////////////////////////////////////////// + + + + + + + + +////////////////////////////////////////////////////////////////////// + +var GENSYM_COUNTER = 0; + +var gensymImpl = function(x) { + check(x, + function(x) { return isString(x) || isSymbol(x) }, + 'gensym', 'symbol or string', + 1); + return types.symbol(x.toString() + '' + (GENSYM_COUNTER++)); +}; + +PRIMITIVES['gensym'] = + new CasePrimitive( + 'gensym', + [new PrimProc('gensym', + 1, + false, + false, + gensymImpl), + new PrimProc('gensym', + 0, + false, + false, + function() { return gensymImpl('g') })]); + + + + + + + + + + + +/*************************** + *** Primitive Constants *** + ***************************/ + + +PRIMITIVES['eof'] = types.EOF; +PRIMITIVES['e'] = jsnums.e; +PRIMITIVES['empty'] = types.EMPTY; +PRIMITIVES['false'] = false; +PRIMITIVES['true'] = true; +PRIMITIVES['pi'] = jsnums.pi; +PRIMITIVES['null'] = types.EMPTY; + + + + + + +////////////////////////////////////////////////////////////////////// +/** Parameters **/ +var PARAMZ = {}; + +PARAMZ['exception-handler-key'] = types.exceptionHandlerKey; + + + +///////////////////////////////////////////////////////////////////////////////////////////// + +// getPrimitive: string (string | undefined) -> scheme-value +primitive.getPrimitive = function(name, resolvedModuleName) { + if (resolvedModuleName === undefined) { + return PRIMITIVES[name]; + } + + if (resolvedModuleName === types.symbol("moby/kernel")) { + return PRIMITIVES[name]; + } + + if (resolvedModuleName === types.symbol("moby/paramz")) { + return PARAMZ[name]; + } + + if (types.isEqual(resolvedModuleName, + types.list([types.symbol("quote"), types.symbol("#%kernel")]))) { + return PRIMITIVES[name]; + } + + if (types.isEqual(resolvedModuleName, + types.list([types.symbol("quote"), types.symbol("#%paramz")]))) { + return PARAMZ[name]; + } + + // FIXME: if we get to this point, this should be treated as an internal error... + return PRIMITIVES[name]; +}; + +primitive.isPrimitive = function(x) { + return x instanceof PrimProc; +}; + + + + + +})(); + diff --git a/js-assembler/runtime-src/types.js b/js-assembler/runtime-src/types.js new file mode 100644 index 0000000..2da6eab --- /dev/null +++ b/js-assembler/runtime-src/types.js @@ -0,0 +1,2532 @@ +////////////////////////////////////////////////////////////////////// +// helper functions + +//var jsnums = require('./js-numbers'); + + +var types = {}; + + +(function () { + +////////////////////////////////////////////////////////////////////// + + +var appendChild = function(parent, child) { + parent.appendChild(child); +}; + + +////////////////////////////////////////////////////////////////////// + + + +getEqHashCode = helpers.getEqHashCode; + + + + +// Union/find for circular equality testing. + +var UnionFind = function() { + // this.parenMap holds the arrows from an arbitrary pointer + // to its parent. + this.parentMap = makeLowLevelEqHash(); +} + +// find: ptr -> UnionFindNode +// Returns the representative for this ptr. +UnionFind.prototype.find = function(ptr) { + var parent = (this.parentMap.containsKey(ptr) ? + this.parentMap.get(ptr) : ptr); + if (parent === ptr) { + return parent; + } else { + var rep = this.find(parent); + // Path compression: + this.parentMap.put(ptr, rep); + return rep; + } +}; + +// merge: ptr ptr -> void +// Merge the representative nodes for ptr1 and ptr2. +UnionFind.prototype.merge = function(ptr1, ptr2) { + this.parentMap.put(this.find(ptr1), this.find(ptr2)); +}; + + + + + +////////////////////////////////////////////////////////////////////// + + +StructType = function(name, type, numberOfArgs, numberOfFields, firstField, + applyGuard, constructor, predicate, accessor, mutator) { + this.name = name; + this.type = type; + this.numberOfArgs = numberOfArgs; + this.numberOfFields = numberOfFields; + this.firstField = firstField; + + this.applyGuard = applyGuard; + this.constructor = constructor; + this.predicate = predicate; + this.accessor = accessor; + this.mutator = mutator; +}; + +StructType.prototype.toString = function(cache) { + return '#'; +}; + +StructType.prototype.isEqual = function(other, aUnionFind) { + return this === other; +}; + + +var makeStructureType = function(theName, parentType, initFieldCnt, autoFieldCnt, autoV, guard) { + var defaultGuard = function(args, name, k) { return k(args); }; + + // If no parent type given, then the parent type is Struct + if ( !parentType ) { + parentType = ({ type: Struct, + numberOfArgs: 0, + numberOfFields: 0, + firstField: 0, + applyGuard: defaultGuard }); + } + // if there's no guard, use the default one + if (!guard) { + guard = defaultGuard; + } + + var numParentArgs = parentType.numberOfArgs; + + // Create a new struct type inheriting from the parent + var aStruct = function(name, args) { + parentType.type.call(this, name, args); + for (var i = 0; i < initFieldCnt; i++) { + this._fields.push(args[i+numParentArgs]); + } + for (var i = 0; i < autoFieldCnt; i++) { + this._fields.push(autoV); + } + }; + aStruct.prototype = helpers.heir(parentType.type.prototype); + + + + // Set type, necessary for equality checking + aStruct.prototype.type = aStruct; + + // construct and return the new type + var newType = new StructType(theName, + aStruct, + initFieldCnt + numParentArgs, + initFieldCnt + autoFieldCnt, + parentType.firstField + parentType.numberOfFields, + function(args, name, k) { + return guard(args, name, + function(result) { + var parentArgs = result.slice(0, parentType.numberOfArgs); + var restArgs = result.slice(parentType.numberOfArgs); + return parentType.applyGuard(parentArgs, name, + function(parentRes) { return k( parentRes.concat(restArgs) ); }); + }); + }, + function() { + var args = helpers.map(function(x) { return x; }, arguments); + return newType.applyGuard(args, + Symbol.makeInstance(theName), + function(res) { return new aStruct(theName, res); }); + }, + function(x) { + return x instanceof aStruct; + }, + function(x, i) { return x._fields[i + this.firstField]; }, + function(x, i, v) { x._fields[i + this.firstField] = v; }); + return newType; +}; + + + + var Struct = function(constructorName, fields) { + this._constructorName = constructorName; + this._fields = []; + }; + + Struct.prototype.toWrittenString = function(cache) { + cache.put(this, true); + var buffer = []; + buffer.push("("); + buffer.push(this._constructorName); + for(var i = 0; i < this._fields.length; i++) { + buffer.push(" "); + buffer.push(toWrittenString(this._fields[i], cache)); + } + buffer.push(")"); + return buffer.join(""); + }; + + Struct.prototype.toDisplayedString = function(cache) { + return toWrittenString(this, cache); + }; + + Struct.prototype.toDomNode = function(cache) { + cache.put(this, true); + var node = document.createElement("div"); + node.appendChild(document.createTextNode("(")); + node.appendChild(document.createTextNode(this._constructorName)); + for(var i = 0; i < this._fields.length; i++) { + node.appendChild(document.createTextNode(" ")); + appendChild(node, toDomNode(this._fields[i], cache)); + } + node.appendChild(document.createTextNode(")")); + return node; + }; + + + Struct.prototype.isEqual = function(other, aUnionFind) { + if ( other.type == undefined || + this.type !== other.type || + !(other instanceof this.type) ) { + return false; + } + + for (var i = 0; i < this._fields.length; i++) { + if (! isEqual(this._fields[i], + other._fields[i], + aUnionFind)) { + return false; + } + } + return true; + } + + Struct.prototype.type = Struct; + + + + + + + + + + + + + + + + + + + + + + + + +////////////////////////////////////////////////////////////////////// + +// Regular expressions. + +var RegularExpression = function(pattern) { + this.pattern = pattern; +}; + + +var ByteRegularExpression = function(pattern) { + this.pattern = pattern; +}; + + + + +////////////////////////////////////////////////////////////////////// + +// Paths + +var Path = function(p) { + this.path = p; +}; + +Path.prototype.toString = function() { + return this.path; +}; + + + +////////////////////////////////////////////////////////////////////// + +// Bytes + +var Bytes = function(bts, mutable) { + // bytes: arrayof [0-255] + this.bytes = bts; + this.mutable = (mutable === undefined) ? false : mutable; +}; + +Bytes.prototype.get = function(i) { + return this.bytes[i]; +}; + +Bytes.prototype.set = function(i, b) { + if (this.mutable) { + this.bytes[i] = b; + } +}; + +Bytes.prototype.length = function() { + return this.bytes.length; +}; + +Bytes.prototype.copy = function(mutable) { + return new Bytes(this.bytes.slice(0), mutable); +}; + +Bytes.prototype.subbytes = function(start, end) { + if (end == null || end == undefined) { + end = this.bytes.length; + } + + return new Bytes( this.bytes.slice(start, end), true ); +}; + + +Bytes.prototype.isEqual = function(other) { + if (! (other instanceof Bytes)) { + return false; + } + if (this.bytes.length != other.bytes.length) { + return false; + } + var A = this.bytes; + var B = other.bytes; + var n = this.bytes.length; + for (var i = 0; i < n; i++) { + if (A[i] !== B[i]) + return false; + } + return true; +}; + + +Bytes.prototype.toString = function(cache) { + var ret = ''; + for (var i = 0; i < this.bytes.length; i++) { + ret += String.fromCharCode(this.bytes[i]); + } + + return ret; +}; + +Bytes.prototype.toDisplayedString = Bytes.prototype.toString; + +Bytes.prototype.toWrittenString = function() { + var ret = ['#"']; + for (var i = 0; i < this.bytes.length; i++) { + ret.push( escapeByte(this.bytes[i]) ); + } + ret.push('"'); + return ret.join(''); +}; + +var escapeByte = function(aByte) { + var ret = []; + var returnVal; + switch(aByte) { + case 7: returnVal = '\\a'; break; + case 8: returnVal = '\\b'; break; + case 9: returnVal = '\\t'; break; + case 10: returnVal = '\\n'; break; + case 11: returnVal = '\\v'; break; + case 12: returnVal = '\\f'; break; + case 13: returnVal = '\\r'; break; + case 34: returnVal = '\\"'; break; + case 92: returnVal = '\\\\'; break; + default: if (aByte >= 32 && aByte <= 126) { + returnVal = String.fromCharCode(aByte); + } + else { + ret.push( '\\' + aByte.toString(8) ); + } + break; + } + return returnVal; +}; + + + + +////////////////////////////////////////////////////////////////////// +// Boxes + +var Box = function(x, mutable) { + this.val = x; + this.mutable = mutable; +}; + +Box.prototype.unbox = function() { + return this.val; +}; + +Box.prototype.set = function(newVal) { + if (this.mutable) { + this.val = newVal; + } +}; + +Box.prototype.toString = function(cache) { + cache.put(this, true); + return "#&" + toWrittenString(this.val, cache); +}; + +Box.prototype.toWrittenString = function(cache) { + cache.put(this, true); + return "#&" + toWrittenString(this.val, cache); +}; + +Box.prototype.toDisplayedString = function(cache) { + cache.put(this, true); + return "#&" + toDisplayedString(this.val, cache); +}; + +Box.prototype.toDomNode = function(cache) { + cache.put(this, true); + var parent = document.createElement("span"); + parent.appendChild(document.createTextNode('#&')); + parent.appendChild(toDomNode(this.val, cache)); + return parent; +}; + +Box.prototype.isEqual = function(other, aUnionFind) { + return ((other instanceof Box) && + isEqual(this.val, other.val, aUnionFind)); +}; + +////////////////////////////////////////////////////////////////////// + +// Placeholders: same thing as boxes. Distinct type just to support make-reader-graph. + +var Placeholder = function(x, mutable) { + this.val = x; +}; + +Placeholder.prototype.ref = function() { + return this.val; +}; + +Placeholder.prototype.set = function(newVal) { + this.val = newVal; +}; + +Placeholder.prototype.toString = function(cache) { + return "#"; +}; + +Placeholder.prototype.toWrittenString = function(cache) { + return "#"; +}; + +Placeholder.prototype.toDisplayedString = function(cache) { + return "#"; +}; + +Placeholder.prototype.toDomNode = function(cache) { + var parent = document.createElement("span"); + parent.appendChild(document.createTextNode('#')); + return parent; +}; + +Placeholder.prototype.isEqual = function(other, aUnionFind) { + return ((other instanceof Placeholder) && + isEqual(this.val, other.val, aUnionFind)); +}; + + + + +////////////////////////////////////////////////////////////////////// + + + + + + + + +// We are reusing the built-in Javascript boolean class here. +Logic = { + TRUE : true, + FALSE : false +}; + + +var isBoolean = function(x) { + return (x === true || x === false); +} + + +// WARNING +// WARNING: we are extending the built-in Javascript boolean class here! +// WARNING +Boolean.prototype.toWrittenString = function(cache) { + if (this.valueOf()) { return "true"; } + return "false"; +}; +Boolean.prototype.toDisplayedString = Boolean.prototype.toWrittenString; + +Boolean.prototype.toString = function(cache) { return this.valueOf() ? "true" : "false"; }; + +Boolean.prototype.isEqual = function(other, aUnionFind){ + return this == other; +}; + + + + +// Chars +// Char: string -> Char +Char = function(val){ + this.val = val; +}; + // The characters less than 256 must be eq?, according to the + // documentation: + // http://docs.racket-lang.org/reference/characters.html +var _CharCache = {}; +for (var i = 0; i < 256; i++) { + _CharCache[String.fromCharCode(i)] = new Char(String.fromCharCode(i)); +} + +// makeInstance: 1-character string -> Char +Char.makeInstance = function(val){ + if (_CharCache[val]) { + return _CharCache[val]; + } + return new Char(val); +}; + +Char.prototype.toString = function(cache) { + var code = this.val.charCodeAt(0); + var returnVal; + switch (code) { + case 0: returnVal = '#\\nul'; break; + case 8: returnVal = '#\\backspace'; break; + case 9: returnVal = '#\\tab'; break; + case 10: returnVal = '#\\newline'; break; + case 11: returnVal = '#\\vtab'; break; + case 12: returnVal = '#\\page'; break; + case 13: returnVal = '#\\return'; break; + case 20: returnVal = '#\\space'; break; + case 127: returnVal = '#\\rubout'; break; + default: if (code >= 32 && code <= 126) { + returnVal = ("#\\" + this.val); + } + else { + var numStr = code.toString(16).toUpperCase(); + while (numStr.length < 4) { + numStr = '0' + numStr; + } + returnVal = ('#\\u' + numStr); + } + break; + } + return returnVal; +}; + +Char.prototype.toWrittenString = Char.prototype.toString; + +Char.prototype.toDisplayedString = function (cache) { + return this.val; +}; + +Char.prototype.getValue = function() { + return this.val; +}; + +Char.prototype.isEqual = function(other, aUnionFind){ + return other instanceof Char && this.val == other.val; +}; + +////////////////////////////////////////////////////////////////////// + +// Symbols + +////////////////////////////////////////////////////////////////////// +var Symbol = function(val) { + this.val = val; +}; + +var symbolCache = {}; + +// makeInstance: string -> Symbol. +Symbol.makeInstance = function(val) { + // To ensure that we can eq? symbols with equal values. + if (!(val in symbolCache)) { + symbolCache[val] = new Symbol(val); + } else { + } + return symbolCache[val]; +}; + +Symbol.prototype.isEqual = function(other, aUnionFind) { + return other instanceof Symbol && + this.val == other.val; +}; + + +Symbol.prototype.toString = function(cache) { + return this.val; +}; + +Symbol.prototype.toWrittenString = function(cache) { + return this.val; +}; + +Symbol.prototype.toDisplayedString = function(cache) { + return this.val; +}; + +////////////////////////////////////////////////////////////////////// + +// Keywords + +var Keyword = function(val) { + this.val = val; +}; + +var keywordCache = {}; + +// makeInstance: string -> Keyword. +Keyword.makeInstance = function(val) { + // To ensure that we can eq? symbols with equal values. + if (!(val in keywordCache)) { + keywordCache[val] = new Keyword(val); + } else { + } + return keywordCache[val]; +}; + +Keyword.prototype.isEqual = function(other, aUnionFind) { + return other instanceof Keyword && + this.val == other.val; +}; + + +Keyword.prototype.toString = function(cache) { + return this.val; +}; + +Keyword.prototype.toWrittenString = function(cache) { + return this.val; +}; + +Keyword.prototype.toDisplayedString = function(cache) { + return this.val; +}; + + +////////////////////////////////////////////////////////////////////// + + + + + +Empty = function() { +}; +Empty.EMPTY = new Empty(); + + +Empty.prototype.isEqual = function(other, aUnionFind) { + return other instanceof Empty; +}; + +Empty.prototype.reverse = function() { + return this; +}; + +Empty.prototype.first = function() { + helpers.raise(types.incompleteExn( + types.exnFailContract, + "first can't be applied on empty.", + [])); +}; +Empty.prototype.rest = function() { + helpers.raise(types.incompleteExn( + types.exnFailContract, + "rest can't be applied on empty.", + [])); +}; +Empty.prototype.isEmpty = function() { + return true; +}; +Empty.prototype.toWrittenString = function(cache) { return "empty"; }; +Empty.prototype.toDisplayedString = function(cache) { return "empty"; }; +Empty.prototype.toString = function(cache) { return "()"; }; + + + +// Empty.append: (listof X) -> (listof X) +Empty.prototype.append = function(b){ + return b; +}; + +Cons = function(f, r) { + this.f = f; + this.r = r; +}; + +Cons.prototype.reverse = function() { + var lst = this; + var ret = Empty.EMPTY; + while (!lst.isEmpty()){ + ret = Cons.makeInstance(lst.first(), ret); + lst = lst.rest(); + } + return ret; +}; + +Cons.makeInstance = function(f, r) { + return new Cons(f, r); +}; + + +// FIXME: can we reduce the recursion on this? +Cons.prototype.isEqual = function(other, aUnionFind) { + if (! (other instanceof Cons)) { + return Logic.FALSE; + } + return (isEqual(this.first(), other.first(), aUnionFind) && + isEqual(this.rest(), other.rest(), aUnionFind)); +}; + +Cons.prototype.first = function() { + return this.f; +}; + +Cons.prototype.rest = function() { + return this.r; +}; + +Cons.prototype.isEmpty = function() { + return false; +}; + +// Cons.append: (listof X) -> (listof X) +Cons.prototype.append = function(b){ + if (b === Empty.EMPTY) + return this; + var ret = b; + var lst = this.reverse(); + while ( !lst.isEmpty() ) { + ret = Cons.makeInstance(lst.first(), ret); + lst = lst.rest(); + } + + return ret; +}; + + +Cons.prototype.toWrittenString = function(cache) { + cache.put(this, true); + var texts = []; + var p = this; + while ( p instanceof Cons ) { + texts.push(toWrittenString(p.first(), cache)); + p = p.rest(); + if (typeof(p) === 'object' && cache.containsKey(p)) { + break; + } + } + if ( p !== Empty.EMPTY ) { + texts.push('.'); + texts.push(toWrittenString(p, cache)); + } +// while (true) { +// if ((!(p instanceof Cons)) && (!(p instanceof Empty))) { +// texts.push("."); +// texts.push(toWrittenString(p, cache)); +// break; +// } +// if (p.isEmpty()) +// break; +// texts.push(toWrittenString(p.first(), cache)); +// p = p.rest(); +// } + return "(" + texts.join(" ") + ")"; +}; + +Cons.prototype.toString = Cons.prototype.toWrittenString; + +Cons.prototype.toDisplayedString = function(cache) { + cache.put(this, true); + var texts = []; + var p = this; + while ( p instanceof Cons ) { + texts.push(toDisplayedString(p.first(), cache)); + p = p.rest(); + if (typeof(p) === 'object' && cache.containsKey(p)) { + break; + } + } + if ( p !== Empty.EMPTY ) { + texts.push('.'); + texts.push(toDisplayedString(p, cache)); + } +// while (true) { +// if ((!(p instanceof Cons)) && (!(p instanceof Empty))) { +// texts.push("."); +// texts.push(toDisplayedString(p, cache)); +// break; +// } +// if (p.isEmpty()) +// break; +// texts.push(toDisplayedString(p.first(), cache)); +// p = p.rest(); +// } + return "(" + texts.join(" ") + ")"; +}; + + + +Cons.prototype.toDomNode = function(cache) { + cache.put(this, true); + var node = document.createElement("span"); + node.appendChild(document.createTextNode("(")); + var p = this; + while ( p instanceof Cons ) { + appendChild(node, toDomNode(p.first(), cache)); + p = p.rest(); + if ( p !== Empty.EMPTY ) { + appendChild(node, document.createTextNode(" ")); + } + if (typeof(p) === 'object' && cache.containsKey(p)) { + break; + } + } + if ( p !== Empty.EMPTY ) { + appendChild(node, document.createTextNode(".")); + appendChild(node, document.createTextNode(" ")); + appendChild(node, toDomNode(p, cache)); + } +// while (true) { +// if ((!(p instanceof Cons)) && (!(p instanceof Empty))) { +// appendChild(node, document.createTextNode(" ")); +// appendChild(node, document.createTextNode(".")); +// appendChild(node, document.createTextNode(" ")); +// appendChild(node, toDomNode(p, cache)); +// break; +// } +// if (p.isEmpty()) +// break; +// appendChild(node, toDomNode(p.first(), cache)); +// p = p.rest(); +// if (! p.isEmpty()) { +// appendChild(node, document.createTextNode(" ")); +// } +// } + node.appendChild(document.createTextNode(")")); + return node; +}; + + + +////////////////////////////////////////////////////////////////////// + +Vector = function(n, initialElements) { + this.elts = new Array(n); + if (initialElements) { + for (var i = 0; i < n; i++) { + this.elts[i] = initialElements[i]; + } + } else { + for (var i = 0; i < n; i++) { + this.elts[i] = undefined; + } + } + this.mutable = true; +}; +Vector.makeInstance = function(n, elts) { + return new Vector(n, elts); +} + Vector.prototype.length = function() { + return this.elts.length; + }; +Vector.prototype.ref = function(k) { + return this.elts[k]; +}; +Vector.prototype.set = function(k, v) { + this.elts[k] = v; +}; + +Vector.prototype.isEqual = function(other, aUnionFind) { + if (other != null && other != undefined && other instanceof Vector) { + if (other.length() != this.length()) { + return false + } + for (var i = 0; i < this.length(); i++) { + if (! isEqual(this.elts[i], other.elts[i], aUnionFind)) { + return false; + } + } + return true; + } else { + return false; + } +}; + +Vector.prototype.toList = function() { + var ret = Empty.EMPTY; + for (var i = this.length() - 1; i >= 0; i--) { + ret = Cons.makeInstance(this.elts[i], ret); + } + return ret; +}; + +Vector.prototype.toWrittenString = function(cache) { + cache.put(this, true); + var texts = []; + for (var i = 0; i < this.length(); i++) { + texts.push(toWrittenString(this.ref(i), cache)); + } + return "#(" + texts.join(" ") + ")"; +}; + +Vector.prototype.toDisplayedString = function(cache) { + cache.put(this, true); + var texts = []; + for (var i = 0; i < this.length(); i++) { + texts.push(toDisplayedString(this.ref(i), cache)); + } + return "#(" + texts.join(" ") + ")"; +}; + +Vector.prototype.toDomNode = function(cache) { + cache.put(this, true); + var node = document.createElement("span"); + node.appendChild(document.createTextNode("#(")); + for (var i = 0; i < this.length(); i++) { + appendChild(node, + toDomNode(this.ref(i), cache)); + if (i !== this.length()-1) { + appendChild(node, document.createTextNode(" ")); + } + } + node.appendChild(document.createTextNode(")")); + return node; +}; + + +////////////////////////////////////////////////////////////////////// + + + + + + +// Now using mutable strings +var Str = function(chars) { + this.chars = chars; + this.length = chars.length; + this.mutable = true; +} + +Str.makeInstance = function(chars) { + return new Str(chars); +} + +Str.fromString = function(s) { + return Str.makeInstance(s.split("")); +} + +Str.prototype.toString = function() { + return this.chars.join(""); +} + +Str.prototype.toWrittenString = function(cache) { + return escapeString(this.toString()); +} + +Str.prototype.toDisplayedString = Str.prototype.toString; + +Str.prototype.copy = function() { + return Str.makeInstance(this.chars.slice(0)); +} + +Str.prototype.substring = function(start, end) { + if (end == null || end == undefined) { + end = this.length; + } + + return Str.makeInstance( this.chars.slice(start, end) ); +} + +Str.prototype.charAt = function(index) { + return this.chars[index]; +} + +Str.prototype.charCodeAt = function(index) { + return this.chars[index].charCodeAt(0); +} + +Str.prototype.replace = function(expr, newStr) { + return Str.fromString( this.toString().replace(expr, newStr) ); +} + + +Str.prototype.isEqual = function(other, aUnionFind) { + if ( !(other instanceof Str || typeof(other) == 'string') ) { + return false; + } + return this.toString() === other.toString(); +} + + +Str.prototype.set = function(i, c) { + this.chars[i] = c; +} + +Str.prototype.toUpperCase = function() { + return Str.fromString( this.chars.join("").toUpperCase() ); +} + +Str.prototype.toLowerCase = function() { + return Str.fromString( this.chars.join("").toLowerCase() ); +} + +Str.prototype.match = function(regexpr) { + return this.toString().match(regexpr); +} + + +//var _quoteReplacingRegexp = new RegExp("[\"\\\\]", "g"); +var escapeString = function(s) { + return '"' + replaceUnprintableStringChars(s) + '"'; +// return '"' + s.replace(_quoteReplacingRegexp, +// function(match, submatch, index) { +// return "\\" + match; +// }) + '"'; +}; + +var replaceUnprintableStringChars = function(s) { + var ret = []; + for (var i = 0; i < s.length; i++) { + var val = s.charCodeAt(i); + switch(val) { + case 7: ret.push('\\a'); break; + case 8: ret.push('\\b'); break; + case 9: ret.push('\\t'); break; + case 10: ret.push('\\n'); break; + case 11: ret.push('\\v'); break; + case 12: ret.push('\\f'); break; + case 13: ret.push('\\r'); break; + case 34: ret.push('\\"'); break; + case 92: ret.push('\\\\'); break; + default: if (val >= 32 && val <= 126) { + ret.push( s.charAt(i) ); + } + else { + var numStr = val.toString(16).toUpperCase(); + while (numStr.length < 4) { + numStr = '0' + numStr; + } + ret.push('\\u' + numStr); + } + break; + } + } + return ret.join(''); +}; + + +/* +// Strings +// For the moment, we just reuse Javascript strings. +String = String; +String.makeInstance = function(s) { + return s.valueOf(); +}; + + +// WARNING +// WARNING: we are extending the built-in Javascript string class here! +// WARNING +String.prototype.isEqual = function(other, aUnionFind){ + return this == other; +}; + +var _quoteReplacingRegexp = new RegExp("[\"\\\\]", "g"); +String.prototype.toWrittenString = function(cache) { + return '"' + this.replace(_quoteReplacingRegexp, + function(match, submatch, index) { + return "\\" + match; + }) + '"'; +}; + +String.prototype.toDisplayedString = function(cache) { + return this; +}; +*/ + + +////////////////////////////////////////////////////////////////////// + +// makeLowLevelEqHash: -> hashtable +// Constructs an eq hashtable that uses Moby's getEqHashCode function. +var makeLowLevelEqHash = helpers.makeLowLevelEqHash; + + + + + + + + +////////////////////////////////////////////////////////////////////// +// Hashtables +var EqHashTable = function(inputHash) { + this.hash = makeLowLevelEqHash(); + this.mutable = true; + +}; +EqHashTable = EqHashTable; + +EqHashTable.prototype.toWrittenString = function(cache) { + var keys = this.hash.keys(); + var ret = []; + for (var i = 0; i < keys.length; i++) { + var keyStr = toWrittenString(keys[i], cache); + var valStr = toWrittenString(this.hash.get(keys[i]), cache); + ret.push('(' + keyStr + ' . ' + valStr + ')'); + } + return ('#hasheq(' + ret.join(' ') + ')'); +}; + +EqHashTable.prototype.toDisplayedString = function(cache) { + var keys = this.hash.keys(); + var ret = []; + for (var i = 0; i < keys.length; i++) { + var keyStr = types.toDisplayedString(keys[i], cache); + var valStr = types.toDisplayedString(this.hash.get(keys[i]), cache); + ret.push('(' + keyStr + ' . ' + valStr + ')'); + } + return ('#hasheq(' + ret.join(' ') + ')'); +}; + +EqHashTable.prototype.isEqual = function(other, aUnionFind) { + if ( !(other instanceof EqHashTable) ) { + return false; + } + + if (this.hash.keys().length != other.hash.keys().length) { + return false; + } + + var keys = this.hash.keys(); + for (var i = 0; i < keys.length; i++){ + if ( !(other.hash.containsKey(keys[i]) && + isEqual(this.hash.get(keys[i]), + other.hash.get(keys[i]), + aUnionFind)) ) { + return false; + } + } + return true; +}; + + + +var EqualHashTable = function(inputHash) { + this.hash = new _Hashtable(function(x) { + return toWrittenString(x); + }, + function(x, y) { + return isEqual(x, y, new UnionFind()); + }); + this.mutable = true; +}; + +EqualHashTable = EqualHashTable; + +EqualHashTable.prototype.toWrittenString = function(cache) { + var keys = this.hash.keys(); + var ret = []; + for (var i = 0; i < keys.length; i++) { + var keyStr = toWrittenString(keys[i], cache); + var valStr = toWrittenString(this.hash.get(keys[i]), cache); + ret.push('(' + keyStr + ' . ' + valStr + ')'); + } + return ('#hash(' + ret.join(' ') + ')'); +}; +EqualHashTable.prototype.toDisplayedString = function(cache) { + var keys = this.hash.keys(); + var ret = []; + for (var i = 0; i < keys.length; i++) { + var keyStr = types.toDisplayedString(keys[i], cache); + var valStr = types.toDisplayedString(this.hash.get(keys[i]), cache); + ret.push('(' + keyStr + ' . ' + valStr + ')'); + } + return ('#hash(' + ret.join(' ') + ')'); +}; + +EqualHashTable.prototype.isEqual = function(other, aUnionFind) { + if ( !(other instanceof EqualHashTable) ) { + return false; + } + + if (this.hash.keys().length != other.hash.keys().length) { + return false; + } + + var keys = this.hash.keys(); + for (var i = 0; i < keys.length; i++){ + if (! (other.hash.containsKey(keys[i]) && + isEqual(this.hash.get(keys[i]), + other.hash.get(keys[i]), + aUnionFind))) { + return false; + } + } + return true; +}; + + +////////////////////////////////////////////////////////////////////// + +var JsValue = function(name, val) { + this.name = name; + this.val = val; +}; + +JsValue.prototype.toString = function() { + return '#'; +}; + +JsValue.prototype.toDomNode = function(cache) { + return toDomNode(this.val, cache); +}; + +JsValue.prototype.isEqual = function(other, aUnionFind) { + return (this.val === other.val); +}; + +// unbox: jsvalue -> any +// Unwraps the value out of the JsValue box. +JsValue.prototype.unbox = function() { + return this.val; +}; + + + +var WrappedSchemeValue = function(val) { + this.val = val; +}; + +WrappedSchemeValue.prototype.toString = function() { return toString(this.val); }; +WrappedSchemeValue.prototype.toWrittenString = function(cache) { return toWrittenString(this.val, cache); }; +WrappedSchemeValue.prototype.toDisplayedString = function(cache) { return toDisplayedString(this.val, cache); }; + + +// unbox: jsvalue -> any +// Unwraps the value out of the WrappedSchemeValue box. +WrappedSchemeValue.prototype.unbox = function() { + return this.val; +}; + + +////////////////////////////////////////////////////////////////////// + +var WorldConfig = function(startup, shutdown, startupArgs) { + this.startup = startup; + this.shutdown = shutdown; + this.startupArgs = startupArgs; +}; + +WorldConfig.prototype.toString = function() { + return '#'; +}; + +WorldConfig.prototype.isEqual = function(other, aUnionFind) { + return ( isEqual(this.startup, other.startup, aUnionFind) && + isEqual(this.shutdown, other.shutdown, aUnionFind) && + isEqual(this.shutdownArg, other.shutdownArg, aUnionFind) && + isEqual(this.restartArg, other.restartArg, aUnionFind) ); +}; + + +var Effect = makeStructureType('effect', false, 0, 0, false, false); +Effect.type.prototype.invokeEffect = function() { + helpers.raise(types.incompleteExn( + types.exnFail, + 'effect type created without using make-effect-type', + [])); +}; + + +var makeEffectType = function(name, superType, initFieldCnt, impl, guard) { + if ( !superType ) { + superType = Effect; + } + + var newType = makeStructureType(name, superType, initFieldCnt, 0, false, guard); + var lastFieldIndex = newType.firstField + newType.numberOfFields; + + newType.type.prototype.invokeEffect = function(aBigBang, k) { + var schemeChangeWorld = new PrimProc('update-world', 1, false, false, + function(worldUpdater) { + helpers.check(worldUpdater, helpers.procArityContains(1), + 'update-world', 'procedure (arity 1)', 1); + + return new INTERNAL_PAUSE( + function(caller, onSuccess, onFail) { + aBigBang.changeWorld(function(w, k2) { + caller(worldUpdater, + [w], k2, + function(e) { throw e; }, + 'change-world (effect)'); + }, + function() { onSuccess(VOID_VALUE, 'restarting (change-world (effect))'); }); + }); + }); + + var args = this._fields.slice(0, lastFieldIndex); + args.unshift(schemeChangeWorld); + return aBigBang.caller(impl, args, k, function(e) { throw e; }, 'invoking effect ' + name); + } + + return newType; +}; + + +var RenderEffect = makeStructureType('render-effect', false, 0, 0, false, false); +RenderEffect.type.prototype.callImplementation = function(caller, k) { + helpers.raise(types.incompleteExn( + types.exnFail, + 'render effect created without using make-render-effect-type', + [])); +}; + +var makeRenderEffectType = function(name, superType, initFieldCnt, impl, guard) { + if ( !superType ) { + superType = RenderEffect; + } + + var newType = makeStructureType(name, superType, initFieldCnt, 0, false, guard); + var lastFieldIndex = newType.firstField + newType.numberOfFields; + + newType.type.prototype.callImplementation = function(caller, k) { + var args = this._fields.slice(0, lastFieldIndex); + caller(impl, args, k); + } + + return newType; +}; + +////////////////////////////////////////////////////////////////////// + + + + + + + + + +////////////////////////////////////////////////////////////////////// + + + + + + + +var toWrittenString = function(x, cache) { + if (! cache) { + cache = makeLowLevelEqHash(); + } + + if (typeof(x) === 'object') { + if (cache.containsKey(x)) { + return "..."; + } + } + + if (x == undefined || x == null) { + return "#"; + } + if (typeof(x) == 'string') { + return escapeString(x.toString()); + } + if (typeof(x) != 'object' && typeof(x) != 'function') { + return x.toString(); + } + + var returnVal; + if (typeof(x.toWrittenString) !== 'undefined') { + returnVal = x.toWrittenString(cache); + } else if (typeof(x.toDisplayedString) !== 'undefined') { + returnVal = x.toDisplayedString(cache); + } else { + returnVal = x.toString(); + } + cache.remove(x); + return returnVal; +}; + + + +var toDisplayedString = function(x, cache) { + if (! cache) { + cache = makeLowLevelEqHash(); + } + if (typeof(x) === 'object') { + if (cache.containsKey(x)) { + return "..."; + } + } + + if (x == undefined || x == null) { + return "#"; + } + if (typeof(x) == 'string') { + return x; + } + if (typeof(x) != 'object' && typeof(x) != 'function') { + return x.toString(); + } + + var returnVal; + if (typeof(x.toDisplayedString) !== 'undefined') { + returnVal = x.toDisplayedString(cache); + } else if (typeof(x.toWrittenString) !== 'undefined') { + returnVal = x.toWrittenString(cache); + } else { + returnVal = x.toString(); + } + cache.remove(x); + return returnVal; +}; + + + +// toDomNode: scheme-value -> dom-node +var toDomNode = function(x, cache) { + if (! cache) { + cache = makeLowLevelEqHash(); + } + if (isNumber(x)) { + return numberToDomNode(x); + } + + if (typeof(x) == 'object') { + if (cache.containsKey(x)) { + var node = document.createElement("span"); + node.appendChild(document.createTextNode("...")); + return node; + } + } + + if (x == undefined || x == null) { + var node = document.createElement("span"); + node.appendChild(document.createTextNode("#")); + return node; + } + if (typeof(x) == 'string') { + var wrapper = document.createElement("span"); + wrapper.style["white-space"] = "pre"; + var node = document.createTextNode(toWrittenString(x)); + wrapper.appendChild(node); + return wrapper; + } + if (typeof(x) != 'object' && typeof(x) != 'function') { + var node = document.createElement("span"); + node.appendChild(document.createTextNode(x.toString())); + return node; + } + + var returnVal; + if (x.nodeType) { + returnVal = x; + } else if (typeof(x.toDomNode) !== 'undefined') { + returnVal = x.toDomNode(cache); + } else if (typeof(x.toWrittenString) !== 'undefined') { + + var node = document.createElement("span"); + node.appendChild(document.createTextNode(x.toWrittenString(cache))); + returnVal = node; + } else if (typeof(x.toDisplayedString) !== 'undefined') { + var node = document.createElement("span"); + node.appendChild(document.createTextNode(x.toDisplayedString(cache))); + returnVal = node; + } else { + var node = document.createElement("span"); + node.appendChild(document.createTextNode(x.toString())); + returnVal = node; + } + cache.remove(x); + return returnVal; +}; + + +// numberToDomNode: jsnum -> dom +// Given a jsnum, produces a dom-node representation. +var numberToDomNode = function(n) { + var node; + if (jsnums.isExact(n)) { + if (jsnums.isInteger(n)) { + node = document.createElement("span"); + node.appendChild(document.createTextNode(n.toString())); + return node; + } else if (jsnums.isRational(n)) { + return rationalToDomNode(n); + } else if (jsnums.isComplex(n)) { + node = document.createElement("span"); + node.appendChild(document.createTextNode(n.toString())); + return node; + } else { + node = document.createElement("span"); + node.appendChild(document.createTextNode(n.toString())); + return node; + } + } else { + node = document.createElement("span"); + node.appendChild(document.createTextNode(n.toString())); + return node; + } +}; + +// rationalToDomNode: rational -> dom-node +var rationalToDomNode = function(n) { + var node = document.createElement("span"); + var chunks = jsnums.toRepeatingDecimal(jsnums.numerator(n), + jsnums.denominator(n)); + node.appendChild(document.createTextNode(chunks[0] + '.')) + node.appendChild(document.createTextNode(chunks[1])); + var overlineSpan = document.createElement("span"); + overlineSpan.style.textDecoration = 'overline'; + overlineSpan.appendChild(document.createTextNode(chunks[2])); + node.appendChild(overlineSpan); + return node; +} + + + + +var isNumber = jsnums.isSchemeNumber; + +var isReal = jsnums.isReal; +var isRational = jsnums.isRational; +var isComplex = isNumber; +var isInteger = jsnums.isInteger; + +var isNatural = function(x) { + return (jsnums.isExact(x) && isInteger(x) + && jsnums.greaterThanOrEqual(x, 0)); +}; +var isNonNegativeReal = function(x) { + return isReal(x) && jsnums.greaterThanOrEqual(x, 0); +}; + + + + + + +var isString = function(s) { + return (typeof s === 'string' || s instanceof Str); +} + + +// isEqual: X Y -> boolean +// Returns true if the objects are equivalent; otherwise, returns false. +var isEqual = function(x, y, aUnionFind) { + if (x === y) { return true; } + + if (isNumber(x) && isNumber(y)) { + return jsnums.eqv(x, y); + } + + if (isString(x) && isString(y)) { + return x.toString() === y.toString(); + } + + if (x == undefined || x == null) { + return (y == undefined || y == null); + } + + if ( typeof(x) == 'object' && + typeof(y) == 'object' && + x.isEqual && + y.isEqual) { + + if (typeof (aUnionFind) === 'undefined') { + aUnionFind = new UnionFind(); + } + + if (aUnionFind.find(x) === aUnionFind.find(y)) { + return true; + } + else { + aUnionFind.merge(x, y); + return x.isEqual(y, aUnionFind); + } + } + return false; +}; + + + + + +// liftToplevelToFunctionValue: primitive-function string fixnum scheme-value -> scheme-value +// Lifts a primitive toplevel or module-bound value to a scheme value. +var liftToplevelToFunctionValue = function(primitiveF, + name, + minArity, + procedureArityDescription) { + if (! primitiveF._mobyLiftedFunction) { + var lifted = function(args) { + return primitiveF.apply(null, args.slice(0, minArity).concat([args.slice(minArity)])); + }; + lifted.isEqual = function(other, cache) { + return this === other; + } + lifted.toWrittenString = function(cache) { + return "#"; + }; + lifted.toDisplayedString = lifted.toWrittenString; + lifted.procedureArity = procedureArityDescription; + primitiveF._mobyLiftedFunction = lifted; + + } + return primitiveF._mobyLiftedFunction; +}; + + + +////////////////////////////////////////////////////////////////////// +var ThreadCell = function(v, isPreserved) { + this.v = v; + this.isPreserved = isPreserved || false; +}; + + + +////////////////////////////////////////////////////////////////////// + + +// Wrapper around functions that return multiple values. +var ValuesWrapper = function(elts) { + this.elts = elts; +}; + +ValuesWrapper.prototype.toDomNode = function(cache) { + var parent = document.createElement("span"); + parent.style["white-space"] = "pre"; + if ( this.elts.length > 0 ) { + parent.appendChild( toDomNode(this.elts[0], cache) ); + for (var i = 1; i < this.elts.length; i++) { + parent.appendChild( document.createTextNode('\n') ); + parent.appendChild( toDomNode(this.elts[i], cache) ); + } + } + return parent; +}; + +ValuesWrapper.prototype.isEqual = function(other, aUnionFind) { + if (! other instanceof ValuesWrapper) { + return false; + } + if (this.elts.length !== other.elts.length) { + return false; + } + for (var i = 0; i < this.elts.length; i++) { + if (! isEqual(this.elts[i], other.elts[i], aUnionFind)) { + return false; + } + } + return true; +}; + + + +var UndefinedValue = function() { +}; +UndefinedValue.prototype.toString = function() { + return "#"; +}; +var UNDEFINED_VALUE = new UndefinedValue(); + +var VoidValue = function() {}; +VoidValue.prototype.toString = function() { + return "#"; +}; + +var VOID_VALUE = new VoidValue(); + + +var EofValue = function() {}; +EofValue.prototype.toString = function() { + return "#"; +} + +var EOF_VALUE = new EofValue(); + + +var ClosureValue = function(name, numParams, paramTypes, isRest, closureVals, body) { + this.name = name; + this.numParams = numParams; + this.paramTypes = paramTypes; + this.isRest = isRest; + this.closureVals = closureVals; + this.body = body; +}; + + + + +ClosureValue.prototype.toString = function() { + if (this.name !== undefined && this.name !== Empty.EMPTY) { + return helpers.format("#", [this.name]); + } else { + return "#"; + } +}; + + +var CaseLambdaValue = function(name, closures) { + this.name = name; + this.closures = closures; +}; + +CaseLambdaValue.prototype.toString = function() { + if (this.name !== undefined && this.name !== Empty.EMPTY) { + return helpers.format("#", [this.name]); + } else { + return "#"; + } +}; + + + +var ContinuationClosureValue = function(vstack, cstack) { + this.name = types.EMPTY; + this.vstack = vstack.slice(0); + this.cstack = cstack.slice(0); +}; + +ContinuationClosureValue.prototype.toString = function() { + if (this.name !== Empty.EMPTY) { + return helpers.format("#", [this.name]); + } else { + return "#"; + } +}; + + + +////////////////////////////////////////////////////////////////////// + + + +var PrefixValue = function() { + this.slots = []; + this.definedMask = []; +}; + +PrefixValue.prototype.addSlot = function(v) { + if (v === undefined) { + this.slots.push(types.UNDEFINED); + this.definedMask.push(false); + } else { + this.slots.push(v); + if (v instanceof GlobalBucket) { + if (v.value === types.UNDEFINED) { + this.definedMask.push(false); + } else { + this.definedMask.push(true); + } + } else if (v instanceof NamedSlot) { + if (v.value === types.UNDEFINED) { + this.definedMask.push(false); + } else { + this.definedMask.push(true); + } + } else { + this.definedMask.push(true); + } + } +}; + +PrefixValue.prototype.ref = function(n) { + if (this.slots[n] instanceof GlobalBucket) { + if (this.definedMask[n]) { + return this.slots[n].value; + } else { + helpers.raise(types.incompleteExn( + types.exnFailContractVariable, + "reference to an identifier before its definition: " + this.slots[n].name, + [this.slots[n].name])); + } + } else if (this.slots[n] instanceof NamedSlot) { + if (this.definedMask[n]) { + return this.slots[n].value; + } else { + helpers.raise(types.incompleteExn( + types.exnFailContractVariable, + "reference to an identifier before its definition: " + this.slots[n].name, + [this.slots[n].name])); + } + } else { + if (this.definedMask[n]) { + return this.slots[n]; + } else { + helpers.raise(types.incompleteExn( + types.exnFailContractVariable, + "variable has not been defined", + [false])); + } + } +}; + +PrefixValue.prototype.lookup = function(name) { + for (var i = 0; i < this.slots.length; i++) { + if (this.slots[i] instanceof NamedSlot) { + if (this.slots[i].name === name) { + return this.slots[i].value; + } + } else if (this.slots[i] instanceof GlobalBucket) { + if (this.slots[i].name === name) { + return this.slots[i].value; + } + } + }; + return types.UNDEFINED; +}; + +PrefixValue.prototype.set = function(n, v) { + if (this.slots[n] instanceof GlobalBucket) { + this.slots[n].value = v; + this.definedMask[n] = true; + } else if (this.slots[n] instanceof NamedSlot) { + this.slots[n].value = v; + this.definedMask[n] = true; + } else { + this.slots[n] = v; + this.definedMask[n] = true; + } +}; + + +PrefixValue.prototype.length = function() { + return this.slots.length; +}; + + +var GlobalBucket = function(name, value) { + this.name = name; + this.value = value; +}; + +var NamedSlot = function(name, value) { + this.name = name; + this.value = value; +}; + +var ModuleVariableRecord = function(resolvedModuleName, + variableName) { + this.resolvedModuleName = resolvedModuleName; + this.variableName = variableName; +}; + + +////////////////////////////////////////////////////////////////////// + + +var Namespace = function() { + this.prefixes = []; + this.bindings = {}; +}; + + +Namespace.prototype.addPrefix = function(prefixValue) { + this.prefixes.push(prefixValue); +}; + + +Namespace.prototype.getVariableValue = function(name) { + // FIXME: fill me in. + // first, look in bindings. + // if not there, then look into each of the prefixes. +}; + + +Namespace.prototype.setVariableValue = function(name, value) { + // FIXME: fill me in. + this.bindings[name] = value; +}; + + + + +////////////////////////////////////////////////////////////////////// + + +var VariableReference = function(prefix, pos) { + this.prefix = prefix; + this.pos = pos; +}; + +VariableReference.prototype.ref = function() { + return this.prefix.ref(this.pos); +}; + +VariableReference.prototype.set = function(v) { + this.prefix.set(this.pos, v); +} + +////////////////////////////////////////////////////////////////////// + +// Continuation Marks + +var ContMarkRecordControl = function(dict) { + this.dict = dict || makeLowLevelEqHash(); +}; + +ContMarkRecordControl.prototype.invoke = function(state) { + // No-op: the record will simply pop off the control stack. +}; + +ContMarkRecordControl.prototype.update = function(key, val) { + var newDict = makeLowLevelEqHash(); + // FIXME: what's the javascript idiom for hash key copy? + // Maybe we should use a rbtree instead? + var oldKeys = this.dict.keys(); + for (var i = 0; i < oldKeys.length; i++) { + newDict.put( oldKeys[i], this.dict.get(oldKeys[i]) ); + } + newDict.put(key, val); + return new ContMarkRecordControl(newDict); +}; + + + +var ContinuationMarkSet = function(dict) { + this.dict = dict; +} + +ContinuationMarkSet.prototype.toDomNode = function(cache) { + var dom = document.createElement("span"); + dom.appendChild(document.createTextNode('#')); + return dom; +}; + +ContinuationMarkSet.prototype.toWrittenString = function(cache) { + return '#'; +}; + +ContinuationMarkSet.prototype.toDisplayedString = function(cache) { + return '#'; +}; + +ContinuationMarkSet.prototype.ref = function(key) { + if ( this.dict.containsKey(key) ) { + return this.dict.get(key); + } + return []; +}; + + +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////// + +var PrimProc = function(name, numParams, isRest, usesState, impl) { + this.name = name; + this.numParams = numParams; + this.isRest = isRest; + this.usesState = usesState; + this.impl = impl; +}; + +PrimProc.prototype.toString = function() { + return ("#"); +}; + +PrimProc.prototype.toWrittenString = function(cache) { + return ("#"); +}; + +PrimProc.prototype.toDisplayedString = function(cache) { + return ("#"); +}; + + +PrimProc.prototype.toDomNode = function(cache) { + var div = document.createElement("span"); + div.appendChild(document.createTextNode("#")); + return div; +}; + + +var CasePrimitive = function(name, cases) { + this.name = name; + this.cases = cases; +}; + + +CasePrimitive.prototype.toDomNode = function(cache) { + var div = document.createElement("span"); + div.appendChild(document.createTextNode("#")); + return div; +}; + +CasePrimitive.prototype.toWrittenString = function(cache) { + return ("#"); +}; + +CasePrimitive.prototype.toDisplayedString = function(cache) { + return ("#"); +}; +////////////////////////////////////////////////////////////////////// + +var makeOptionPrimitive = function(name, + numArgs, + defaultVals, + usesState, + bodyF) { + var makeNthPrimitive = function(n) { + return new PrimProc(name, + numArgs + n, + false, + usesState, + function() { + var expectedNumArgs = numArgs + n + (usesState ? 1 : 0); + assert.equal(arguments.length, + expectedNumArgs); + var args = [arguments]; + for (var i = 0; i < arguments.length; i++) { + args.push(arguments[i]); + } + var startDefaults = i - numArgs - (usesState ? 1 : 0); + return bodyF.apply( + bodyF, + args.concat(defaultVals.slice(startDefaults))); + }); + }; + + var cases = []; + for (var i = 0; i <= defaultVals.length; i++) { + cases.push(makeNthPrimitive(i)); + } + return new CasePrimitive(name, cases); +}; + + + + + + + +// Struct Procedure types +var StructProc = function(type, name, numParams, isRest, usesState, impl) { + PrimProc.call(this, name, numParams, isRest, usesState, impl); + this.type = type; +}; +StructProc.prototype = helpers.heir(PrimProc.prototype); + +var StructConstructorProc = function() { + StructProc.apply(this, arguments); +}; +StructConstructorProc.prototype = helpers.heir(StructProc.prototype); + +var StructPredicateProc = function() { + StructProc.apply(this, arguments); +}; +StructPredicateProc.prototype = helpers.heir(StructProc.prototype); + +var StructAccessorProc = function() { + StructProc.apply(this, arguments); +}; +StructAccessorProc.prototype = helpers.heir(StructProc.prototype); + +var StructMutatorProc = function() { + StructProc.apply(this, arguments); +}; +StructMutatorProc.prototype = helpers.heir(StructProc.prototype); + + + + + + + + + +////////////////////////////////////////////////////////////////////// + + +// INTERNAL_CALL +// used for interaction between the Primitives and the interpreter (callPrimitiveProcedure). +// Don't confuse this with CallControl. +var INTERNAL_CALL = function(operator, operands, k) { + this.operator = operator; + this.operands = operands; + this.k = k; +}; + +// INTERNAL_PAUSE +// used for interaction between the Primitive functions and the +// interpreter. +// Halts the interpreter, but passing onPause the functions necessary +// to restart computation. +var INTERNAL_PAUSE = function(onPause) { + this.onPause = onPause; +}; + + + +////////////////////////////////////////////////////////////////////// + + +// ContinuationPromptTag: symbol | false -> ContinuationPromptTag +var ContinuationPromptTag = function(sym) { + this.sym = sym; +}; + +var defaultContinuationPromptTag = new ContinuationPromptTag(); + +var defaultContinuationPromptTagHandler = new PrimProc( + 'default-continuation-prompt-tag-handler', + 1, + false, + true, + function(aState, thunk) { + aState.pushControl( + new control.ApplicationControl( + new control.ConstantControl(thunk), + [])); + }); + + +////////////////////////////////////////////////////////////////////// + + + + + +var makeList = function(args) { + var result = Empty.EMPTY; + for(var i = args.length-1; i >= 0; i--) { + result = Cons.makeInstance(args[i], result); + } + return result; +}; + + +var makeVector = function(args) { + return Vector.makeInstance(args.length, args); +}; + + +var makeVectorImmutable = function(args) { + var v = Vector.makeInstance(args.length, args); + v.mutable = false; + return v; +}; + + +var makeString = function(s) { + if (s instanceof Str) { + return s; + } + else if (s instanceof Array) { +// for (var i = 0; i < s.length; i++) { +// if ( typeof s[i] !== 'string' || s[i].length != 1 ) { +// return undefined; +// } +// } + return Str.makeInstance(s); + } + else if (typeof s === 'string') { + return Str.fromString(s); + } + else { + throw types.internalError('makeString expects and array of 1-character strings or a string;' + + ' given ' + s.toString(), + false); + } +} + + +var makeHashEq = function(lst) { + var newHash = new EqHashTable(); + while ( !lst.isEmpty() ) { + newHash.hash.put(lst.first().first(), lst.first().rest()); + lst = lst.rest(); + } + return newHash; +} + + +var makeHashEqual = function(lst) { + var newHash = new EqualHashTable(); + while ( !lst.isEmpty() ) { + newHash.hash.put(lst.first().first(), lst.first().rest()); + lst = lst.rest(); + } + return newHash; +} + + +var Color = makeStructureType('color', false, 3, 0, false, false); +var ArityAtLeast = makeStructureType('arity-at-least', false, 1, 0, false, + function(args, name, k) { + helpers.check(args[0], function(x) { return ( jsnums.isExact(x) && + jsnums.isInteger(x) && + jsnums.greaterThanOrEqual(x, 0) ); }, + name, 'exact non-negative integer', 1); + return k(args); + }); + + + + +////////////////////////////////////////////////////////////////////// + + + +var readerGraph = function(x, objectHash, n) { + if (typeof(x) === 'object' && objectHash.containsKey(x)) { + return objectHash.get(x); + } + + if (types.isPair(x)) { + var consPair = types.cons(x.first(), x.rest()); + objectHash.put(x, consPair); + consPair.f = readerGraph(x.first(), objectHash, n+1); + consPair.r = readerGraph(x.rest(), objectHash, n+1); + return consPair; + } + + if (types.isVector(x)) { + var len = x.length(); + var aVector = types.vector(len, x.elts); + objectHash.put(x, aVector); + for (var i = 0; i < len; i++) { + aVector.elts[i] = readerGraph(aVector.elts[i], objectHash, n+1); + } + return aVector; + } + + if (types.isBox(x)) { + var aBox = types.box(x.unbox()); + objectHash.put(x, aBox); + aBox.val = readerGraph(x.unbox(), objectHash, n+1); + return aBox; + } + + if (types.isHash(x)) { + throw new Error("make-reader-graph of hash not implemented yet"); + } + + if (types.isStruct(x)) { + var aStruct = clone(x); + objectHash.put(x, aStruct); + for(var i = 0 ;i < x._fields.length; i++) { + x._fields[i] = readerGraph(x._fields[i], objectHash, n+1); + } + return aStruct; + } + + if (types.isPlaceholder(x)) { + return readerGraph(x.ref(), objectHash, n+1); + } + + return x; +}; + + + +// clone: object -> object +// Copies an object. The new object should respond like the old +// object, including to things like instanceof +var clone = function(obj) { + var C = function() {} + C.prototype = obj; + var c = new C(); + for (property in obj) { + if (obj.hasOwnProperty(property)) { + c[property] = obj[property]; + } + } + return c; +}; + + + + +////////////////////////////////////////////////////////////////////// + + + + + + +types.exceptionHandlerKey = new Symbol("exnh"); + +types.symbol = Symbol.makeInstance; +types.rational = jsnums.makeRational; +types.float = jsnums.makeFloat; +types.complex = jsnums.makeComplex; +types.bignum = jsnums.makeBignum; +types.list = makeList; +types.vector = makeVector; +types.vectorImmutable = makeVectorImmutable; +types.regexp = function(p) { return new RegularExpression(p) ; } +types.byteRegexp = function(p) { return new ByteRegularExpression(p) ; } +types['char'] = Char.makeInstance; +types['string'] = makeString; +types.box = function(x) { return new Box(x, true); }; +types.placeholder = function(x) { return new Placeholder(x); }; +types.boxImmutable = function(x) { return new Box(x, false); }; +types.path = function(x) { return new Path(x); }; +types.bytes = function(x, mutable) { return new Bytes(x, mutable); }; +types.bytesImmutable = function(x) { return new Bytes(x, false); }; +types.keyword = function(k) { return new Keyword(k); }; +types.pair = function(x, y) { return Cons.makeInstance(x, y); }; +types.hash = makeHashEqual; +types.hashEq = makeHashEq; +types.jsValue = function(name, val) { return new JsValue(name, val); }; +types.wrappedSchemeValue = function(val) { return new WrappedSchemeValue(val); }; + +types.toWrittenString = toWrittenString; +types.toDisplayedString = toDisplayedString; +types.toDomNode = toDomNode; + + +types.color = Color.constructor; +types.colorRed = function(x) { return Color.accessor(x, 0); }; +types.colorGreen = function(x) { return Color.accessor(x, 1); }; +types.colorBlue = function(x) { return Color.accessor(x, 2); }; + +types.arityAtLeast = ArityAtLeast.constructor; +types.arityAtLeastValue = function(arity) { return ArityAtLeast.accessor(arity, 0); }; + + +types.FALSE = Logic.FALSE; +types.TRUE = Logic.TRUE; +types.EMPTY = Empty.EMPTY; + +types.isEqual = isEqual; +types.isNumber = isNumber; + +types.isReal = jsnums.isReal; +types.isBoolean = isBoolean; +types.isRational = jsnums.isRational; +types.isComplex = isNumber; +types.isInteger = jsnums.isInteger; +types.isNatural = isNatural; +types.isNonNegativeReal = isNonNegativeReal; + + +types.isSymbol = function(x) { return x instanceof Symbol; }; +types.isChar = function(x) { return x instanceof Char; }; +types.isString = isString; +types.isPair = function(x) { return x instanceof Cons; }; +types.isEmpty = function(x) { return x === Empty.EMPTY; }; +types.isVector = function(x) { return x instanceof Vector; }; +types.isBox = function(x) { return x instanceof Box; }; +types.isPlaceholder = function(x) { return x instanceof Placeholder; }; +types.isHash = function(x) { return (x instanceof EqHashTable || + x instanceof EqualHashTable); }; +types.isByteString = function(x) { return x instanceof Bytes; }; +types.isStruct = function(x) { return x instanceof Struct; }; +types.isArityAtLeast = ArityAtLeast.predicate; +types.isColor = Color.predicate; +types.isFunction = function(x) { + return (x instanceof PrimProc || + x instanceof CasePrimitive || + x instanceof ClosureValue || + x instanceof CaseLambdaValue || + x instanceof ContinuationClosureValue); +}; +types.isJsValue = function(x) { return x instanceof JsValue; }; +types.isWrappedSchemeValue = function(x) { return x instanceof WrappedSchemeValue; }; + +types.UnionFind = UnionFind; +types.cons = Cons.makeInstance; + +types.UNDEFINED = UNDEFINED_VALUE; +types.VOID = VOID_VALUE; +types.EOF = EOF_VALUE; + +types.ValuesWrapper = ValuesWrapper; +types.ClosureValue = ClosureValue; +types.ContinuationPromptTag = ContinuationPromptTag; +types.defaultContinuationPromptTag = defaultContinuationPromptTag; +types.defaultContinuationPromptTagHandler = defaultContinuationPromptTagHandler; +types.ContinuationClosureValue = ContinuationClosureValue; +types.CaseLambdaValue = CaseLambdaValue; +types.PrimProc = PrimProc; +types.CasePrimitive = CasePrimitive; +types.makeOptionPrimitive = makeOptionPrimitive; + +types.internalCall = function(op, args, k) { return new INTERNAL_CALL(op, args, k); }; +types.isInternalCall = function(x) { return (x instanceof INTERNAL_CALL); }; +types.internalPause = function(onPause) { return new INTERNAL_PAUSE(onPause) }; +types.isInternalPause = function(x) { return (x instanceof INTERNAL_PAUSE); }; + +types.contMarkRecordControl = function(dict) { return new ContMarkRecordControl(dict); }; +types.isContMarkRecordControl = function(x) { return x instanceof ContMarkRecordControl; }; +types.continuationMarkSet = function(dict) { return new ContinuationMarkSet(dict); }; +types.isContinuationMarkSet = function(x) { return x instanceof ContinuationMarkSet; }; +types.isContinuationPromptTag = function(x) { return x instanceof ContinuationPromptTag; }; + + +types.PrefixValue = PrefixValue; +types.GlobalBucket = GlobalBucket; +types.NamedSlot = NamedSlot; +types.ModuleVariableRecord = ModuleVariableRecord; +types.VariableReference = VariableReference; + +types.Box = Box; +types.Placeholder = Placeholder; +types.ThreadCell = ThreadCell; + + + + +types.makeStructureType = makeStructureType; +types.isStructType = function(x) { return x instanceof StructType; }; + +types.StructProc = StructProc; +types.StructConstructorProc = StructConstructorProc; +types.StructPredicateProc = StructPredicateProc; +types.StructAccessorProc = StructAccessorProc; +types.StructMutatorProc = StructMutatorProc; + + +types.makeLowLevelEqHash = makeLowLevelEqHash; + + +// Error type exports +var InternalError = function(val, contMarks) { + this.val = val; + this.contMarks = (contMarks ? contMarks : false); +} +types.internalError = function(v, contMarks) { return new InternalError(v, contMarks); }; +types.isInternalError = function(x) { return x instanceof InternalError; }; + +var SchemeError = function(val) { + this.val = val; +} +types.schemeError = function(v) { return new SchemeError(v); }; +types.isSchemeError = function(v) { return v instanceof SchemeError; }; + + +var IncompleteExn = function(constructor, msg, otherArgs) { + this.constructor = constructor; + this.msg = msg; + this.otherArgs = otherArgs; +}; +types.incompleteExn = function(constructor, msg, args) { return new IncompleteExn(constructor, msg, args); }; +types.isIncompleteExn = function(x) { return x instanceof IncompleteExn; }; + +var Exn = makeStructureType('exn', false, 2, 0, false, + function(args, name, k) { + helpers.check(args[0], isString, name, 'string', 1); + helpers.check(args[1], types.isContinuationMarkSet, name, 'continuation mark set', 2); + return k(args); + }); +types.exn = Exn.constructor; +types.isExn = Exn.predicate; +types.exnMessage = function(exn) { return Exn.accessor(exn, 0); }; +types.exnContMarks = function(exn) { return Exn.accessor(exn, 1); }; +types.exnSetContMarks = function(exn, v) { Exn.mutator(exn, 1, v); }; + +// (define-struct (exn:break exn) (continuation)) +var ExnBreak = makeStructureType('exn:break', Exn, 1, 0, false, + function(args, name, k) { + helpers.check(args[2], function(x) { return x instanceof ContinuationClosureValue; }, + name, 'continuation', 3); + return k(args); + }); +types.exnBreak = ExnBreak.constructor; +types.isExnBreak = ExnBreak.predicate; +types.exnBreakContinuation = function(exn) { return ExnBreak.accessor(exn, 0); }; + +var ExnFail = makeStructureType('exn:fail', Exn, 0, 0, false, false); +types.exnFail = ExnFail.constructor; +types.isExnFail = ExnFail.predicate; + +var ExnFailContract = makeStructureType('exn:fail:contract', ExnFail, 0, 0, false, false); +types.exnFailContract = ExnFailContract.constructor; +types.isExnFailContract = ExnFailContract.predicate; + +var ExnFailContractArity = makeStructureType('exn:fail:contract:arity', ExnFailContract, 0, 0, false, false); +types.exnFailContractArity = ExnFailContractArity.constructor; +types.isExnFailContractArity = ExnFailContractArity.predicate; + +var ExnFailContractVariable = makeStructureType('exn:fail:contract:variable', ExnFailContract, 1, 0, false, false); +types.exnFailContractVariable = ExnFailContractVariable.constructor; +types.isExnFailContractVariable = ExnFailContractVariable.predicate; +types.exnFailContractVariableId = function(exn) { return ExnFailContractVariable.accessor(exn, 0); }; + +var ExnFailContractDivisionByZero = makeStructureType('exn:fail:contract:divide-by-zero', ExnFailContract, 0, 0, false, false); +types.exnFailContractDivisionByZero = ExnFailContractDivisionByZero.constructor; +types.isExnFailContractDivisionByZero = ExnFailContractDivisionByZero.predicate; + + +/////////////////////////////////////// +// World-specific exports + +// big bang info to be passed into a make-world-config startup argument +var BigBangInfo = makeStructureType('bb-info', false, 2, 0, false, + function(args, name, k) { + helpers.check(args[0], helpers.procArityContains(1), name, 'procedure (arity 1)', 1); + helpers.check(args[1], types.isJsValue, name, 'js-object', 2); + return k(args); + }); +types.BigBangInfo = BigBangInfo; +types.makeBigBangInfo = BigBangInfo.constructor; +types.isBigBangInfo = BigBangInfo.predicate; +types.bbInfoChangeWorld = function(info) { return BigBangInfo.accessor(info, 0); }; +types.bbInfoToplevelNode = function(info) { return BigBangInfo.accessor(info, 1); }; + + + +// World config information for user-defined configurations +types.worldConfig = function(startup, shutdown, pause, restart) { return new WorldConfig(startup, shutdown, pause, restart); }; +types.isWorldConfig = function(x) { return x instanceof WorldConfig; }; + + +// exporting information to create effect types +types.makeEffectType = makeEffectType; +types.isEffectType = function(x) { + return ((x instanceof StructType)&& x.type.prototype.invokeEffect) ? true : false; +}; + +types.isEffect = Effect.predicate; + + +// exporting functions to create render effect types +types.makeRenderEffectType = makeRenderEffectType; +types.isRenderEffectType = function(x) { + return (x instanceof StructType && x.type.prototype.callImplementation) ? true : false; +}; + +types.isRenderEffect = RenderEffect.predicate; + + + +types.readerGraph = readerGraph; + +})(); + diff --git a/runtime.compressed.js b/runtime.compressed.js deleted file mode 100644 index d7b1954..0000000 --- a/runtime.compressed.js +++ /dev/null @@ -1,16 +0,0 @@ -this.plt===void 0&&(this.plt={}); -(function(){this.plt.runtime={};var g=this.plt.runtime,m=function(a){var b=function(){};b.prototype=a;return new b},r=function(a){return function(b){return b instanceof a}},h=function(a){return typeof a==="number"},k=function(a){return typeof a=="object"&&a.length===2},o=function(a){return typeof a=="object"&&a.length!==void 0},p=function(){},t=function(a,b){this.label=a;this.proc=b};t.prototype=m(p.prototype);var u=function(a,b){this.label=a;this.tag=b};u.prototype=m(p.prototype);var n=function(){}; -p=r(n);var s=function(){};s.prototype=m(n.prototype);s.prototype.write=function(a,b){a.params.currentDisplayer(b)};var i=function(){this.buf=[]};i.prototype=m(n.prototype);i.prototype.write=function(a,b){this.buf.push(String(b))};i.prototype.getOutputString=function(){return this.buf.join("")};n=r(i);i=function(a){this.name=a};var w=new i("default-continuation-prompt-tag"),l=[],j=function(a){throw a;},f=function(a,b,c,d,e){if(b(c))return!0;else j(Error(e+": expected "+a+" as argument #"+d+" but received "+ -c+" instead"))},q=function(a,b,c,d){(bd)&&j(Error(a+": expected at least "+c+" arguments but received "+observer))},e={};e.display=function(a,b){q("display",b,1,2);var c=a.env[a.env.length-1],d=a.params.currentOutputPort;b==2&&(d=a.env[a.env.length-2]);d.write(a,c)};e.newline=function(a,b){q("newline",b,0,1);var c=a.params.currentOutputPort;b==1&&(c=a.env[a.env.length-1]);c.write(a,"\n")};e.displayln=function(a,b){q("displayln",b,1,2);var c=a.env[a.env.length-1],d=a.params.currentOutputPort; -b==2&&(d=a.env[a.env.length-2]);d.write(a,c);d.write(a,"\n")};e.pi=Math.PI;e.e=Math.E;e["="]=function(a){var b=a.env[a.env.length-1];a=a.env[a.env.length-2];f("number",h,b,0,"=");f("number",h,a,1,"=");return b===a};e["<"]=function(a){var b=a.env[a.env.length-1];a=a.env[a.env.length-2];f("number",h,b,0,"<");f("number",h,a,1,"<");return b"]=function(a){var b=a.env[a.env.length-1];a=a.env[a.env.length-2];f("number",h,b,0,">");f("number",h,a,1,">");return b>a};e["<="]=function(a){var b=a.env[a.env.length- -1];a=a.env[a.env.length-2];f("number",h,b,0,"<=");f("number",h,a,1,"<=");return b<=a};e[">="]=function(a){var b=a.env[a.env.length-1];a=a.env[a.env.length-2];f("number",h,b,0,">=");f("number",h,a,1,">=");return b>=a};e["+"]=function(a,b){var c=0,d=0;for(d=0;dlist"]=function(a){f("vector", -o,a.env[a.env.length-1],0,"vector->list");a=a.env[a.env.length-1];var b,c=l;for(b=0;bvector"]=function(a){a=a.env[a.env.length-1];for(var b=[];a!==l;)b.push(a[0]),a=a[1];return b};e["vector-ref"]=function(a){f("vector",o,a.env[a.env.length-1],0,"vector-ref");return a.env[a.env.length-1][a.env[a.env.length-2]]};e["vector-set!"]=function(a){f("vector",o,a.env[a.env.length-1],0,"vector-set!");a.env[a.env.length-1][a.env[a.env.length-2]]=a.env[a.env.length- -3];return null};e["symbol?"]=function(a){return typeof a.env[a.env.length-1]==="string"};e["symbol->string"]=function(a){return a.env[a.env.length-1]};e["string-append"]=function(a,b){var c=[],d;for(d=0;d=0;d--)if(a.control[d].tag===c)return a.control.slice(d+1,a.control.length-b);j(Error("captureControl: unable to find tag "+c))};g.restoreControl=function(a,b){var c;for(c=a.control.length-1;c>=0;c--)if(a.control[c].tag===b){a.control=a.control.slice(0,c+1).concat(a.env[a.env.length-1]);return}j(Error("restoreControl: unable to find tag "+b))}; -g.isNumber=h;g.isPair=k;g.isVector=o;g.isOutputPort=p;g.isOutputStringPort=n;g.heir=m;g.makeClassPredicate=r;g.trampoline=v}).call(this);