UNPKG

minigrace

Version:

A compiler for the Grace programming language

1,277 lines (1,224 loc) 148 kB
/* global minigrace, unicode */ var inBrowser = (typeof global === "undefined"); if (!inBrowser) { // if we are in Node.js global.lineNumber = 0; } else { var lineNumber = 0; } function setLineNumber(n) { lineNumber = n; } function getLineNumber() { return lineNumber; } function setModuleName() { return; } function getModuleName() { return arguments.callee.caller.definitionModule || "native code" ; } function jsTrue() { return true; } function jsFalse() { return false; } Array.prototype.sum = function () { return this.reduce(function(a,b) {return a+b;}, 0); }; if (!Array.prototype.includes) { Array.prototype.includes = function (needle) { for (let i = 0, len = this.length ; i < len ; i++) { if (this[i] === needle) return true; } return false; }; } function hashAndCombine(aHash, objB) { // aHash is a JS number; objB is a Grace object // returns a JS Number var bHash = request(objB, 'hash', [0])._value; return (aHash * 2) ^ bHash; // ^ is bitwise XOR } function GraceObject() { // constructor function // gets its methods from the prototype. Don't add to them! this.data = {}; this.className = "graceObject"; this.mutable = false; this.definitionModule = "built-in library"; this.definitionLine = 0; this.closureKeys = []; } function object_notEquals (argcv, o) { var b = callmethod(this, "==(1)", [1], o); return callmethod(b, "not", [0]); } function object_isMe (argcv, other) { return Object.is(this, other) ? GraceTrue : GraceFalse; } object_isMe.confidential = true; object_isMe.methodName = "isMe(_)"; object_isMe.paramCounts = [1]; object_isMe.paramNames = ["other"]; object_isMe.definitionLine = 0; object_isMe.definitionModule = "built-in library"; var identityHashSeed = 1001; function object_identityHash(argcv) { if (! this.identityHash) { this.identityHash = new GraceNum(identityHashSeed ^ 0xdeadbeef); // ^ means XOR identityHashSeed = identityHashSeed + 1; } return this.identityHash; } object_identityHash.confidential = true; object_identityHash.methodName = "identityHash"; object_identityHash.paramCounts = [0]; object_identityHash.definitionLine = 0; object_identityHash.definitionModule = "built-in library"; function object_basicAsString (argcv) { var s = "object {"; var firstTime = true; for (var i in this.data) { if (firstTime) firstTime = false; else s += ", "; try { s += "" + i + " = " + callmethod(this.data[i], "asString", [0])._value; } catch (ex) { s += "" + i + " = ?"; } } return new GraceString(s + "}"); } object_basicAsString.confidential = true; function articleFor(str) { var noun = str.toLowerCase(); if (noun.startsWith("one")) return "a"; if (noun.startsWith("un")) return "an"; if ("aeio".indexOf(noun[0]) >= 0) return "an"; return "a"; } function object_asString (argcv) { if (!this.className || this.className.length === 0) return new GraceString("an object"); var clNm = this.className; return new GraceString(articleFor(clNm) + " " + clNm); } function object_asDebugString (argcv) { return callmethod(this, "asString", [0]); } function object_debugIterator (argcv) { return new GraceIterator(this); } function object_colonColon (argcv, other) { return callmethod(GraceBindingClass(), "key(1)value(1)", [1, 1], this, other); } GraceObject.prototype = { methods: { "isMe(1)": object_isMe, "myIdentityHash": object_identityHash, "basicAsString": object_basicAsString, "asString": object_asString, "asDebugString": object_asDebugString, "debug$Iterator": object_debugIterator }, classUid: "graceObject-built-in" // data: {} The prototype should NOT have a data object — data should go in the // child (non-shared) object. }; function GraceTrait() { // constructor function // gets its methods from the prototype. Don't add to them! this.closureKeys = []; this.data = {}; this.className = "graceTrait"; this.mutable = false; this.definitionModule = "built-in library"; this.definitionLine = 0; } function trait_asString (argcv) { if (!this.className || this.className.length === 0) return new GraceString("a trait"); var firstChar = this.className[0].toLowerCase(); var article = ("aeio".indexOf(firstChar) >= 0)? "an " : "a "; return new GraceString(article + this.className + " trait"); } function confidentialVersion(fun, optionalName) { if (fun.wrappedFunction) fun = fun.wrappedFunction; // avoid multiple wrappers const newFun = function confidentialV (...args) { return fun.apply(this, args); }; newFun.confidential = true; newFun.wrappedFunction = fun; if (fun.paramCounts) newFun.paramCounts = fun.paramCounts; if (fun.paramNames) newFun.paramNames = fun.paramNames; if (fun.typeParamNames) newFun.typeParamNames = fun.typeParamNames; if (fun.paramTypes) newFun.paramTypes = fun.paramTypes; if (fun.returnType) newFun.returnType = fun.returnType; if (optionalName) newFun.methodName = optionalName; return newFun; } function publicVersion(fun, optionalName) { if (fun.wrappedFunction) fun = fun.wrappedFunction; // avoid multiple wrappers const newFun = function publicV (...args) { return fun.apply(this, args); }; newFun.wrappedFunction = fun; if (fun.paramCounts) newFun.paramCounts = fun.paramCounts; if (fun.paramNames) newFun.paramNames = fun.paramNames; if (fun.typeParamNames) newFun.typeParamNames = fun.typeParamNames; if (fun.paramTypes) newFun.paramTypes = fun.paramTypes; if (fun.returnType) newFun.returnType = fun.returnType; if (optionalName) newFun.methodName = optionalName; return newFun; } function memoized(methodFun, memoCellName) { return function() { if (! this[memoCellName]) { this[memoCellName] = methodFun.call(this); } return this[memoCellName]; }; } GraceTrait.prototype = { methods: { "isMe(1)": object_isMe, "myIdentityHash": object_identityHash, "basicAsString": object_basicAsString, "asString": trait_asString, "asDebugString": object_asDebugString, "debug$Iterator": object_debugIterator }, classUid: "graceTrait-built-in" }; function isEmptyObject(obj) { for (var name in obj) { return false; } return true; } function Grace_allocObject(superConstructor, givenName) { // The difference between this function and "new GraceObject" is that the // object returned here has its OWN methods object, // whereas the one returned from new GraceObject shares its prototype's methods. // Changing the 'methods' object has different effects in the two cases! var newMethods = {}; if (superConstructor) { var supMethods = (new superConstructor()).methods; for (var nm in supMethods) { if (supMethods.hasOwnProperty(nm)) newMethods[nm] = supMethods[nm]; } } givenName = givenName || "object"; givenName = givenName.toString(); var resultObj = { methods: newMethods, data: {}, className: givenName, mutable: false, closureKeys: [], definitionModule: "built-in library", definitionLine: 0, classUid: givenName + "-built-in" }; return resultObj; } function emptyGraceObject(givenName, modname, line) { return { methods: { "isMe(1)": object_isMe, "myIdentityHash": object_identityHash, "basicAsString": object_basicAsString, "asString": object_asString, "asDebugString": object_asDebugString, "debug$Iterator": object_debugIterator }, data: {}, className: givenName, mutable: false, closureKeys: [], definitionModule: modname, definitionLine: line, classUid: givenName + "-" + modname + "-" + line }; } function capitalize(str) { return str.replace(/^.|\s\S/g, function(a) { return a.toUpperCase(); }); } function GraceString(s) { this._value = s; } function string_greaterThanOrEqual (argcv, that) { var self = this._value; if (that.className !== "string") { this.failArgCheck('', "string", "≥(_)"); } var other = that._value; if (self >= other) return GraceTrue; return GraceFalse; } function string_lessThanOrEqual (argcv, that) { var self = this._value; if (that.className !== "string") { this.failArgCheck('', "string", "≤(_)"); } var other = that._value; if (self <= other) return GraceTrue; return GraceFalse; } function string_at(argcv, where) { var idx = where._value; var s = this._value; var jrv = s[idx-1]; // don't use charAt(): [] gives undefined for non-integers if (jrv) return new GraceString(jrv); // now deal with the error conditions: var msgstr = (s.length <= 20) ? s : (s.substr(0, 17) + "…"); if (where.className === "number") { if (idx > s.length) { throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + '".at(' + idx + ') but string.size = ' + s.length)); } if (idx < 1) { throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + '".at(' + idx + ') but strings are indexed from 1')); } } throw new GraceExceptionPacket(TypeErrorObject, new GraceString("argument to 'at(_)' on string \"" + msgstr + "\" is not an Integer")); } function string_curriedAt(idx) { var s = this._value; if (idx > s.length) { var msgstr = (s.length <= 20) ? s : (s.substr(0, 17) + "…"); throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + "\".at(" + idx + ") but string.size = " + s.length)); } return new GraceString(s.charAt(idx-1)); } function string_hash(argcv) { if (typeof this._hash === 'undefined') { var hc = 0; for (var i=0; i<this._value.length; i++) { hc *= 23; hc += this._value.charCodeAt(i); hc = hc & hc; } this._hash = new GraceNum(Math.abs(hc)); } return this._hash; } function curriedEquality(argcv) { return new GracePredicatePattern(arg => selfRequest(this, "==(1)", [1], arg)); } function curriedInequality(argcv) { return new GracePredicatePattern(arg => selfRequest(this, "≠(1)", [1], arg)); } function string_identityHash(argcv) { return string_hash(argcv); } string_identityHash.confidential = true; function string_indices(argcv) { var size = this._value.length; return callmethod(GraceRangeClass(), "uncheckedFrom(1)to(1)", [1, 1], new GraceNum(1), new GraceNum(size)); } function escapeRegExp(str) { return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); // prefixes special chars with \ } function escapeReplacement(str) { return str.replace(/\$/g, '$$$$'); // replaces a single $ by $$ } function failStringMethodArgCheck(desc, className, methodName) { const idStart = /^[a-zA-Z_]/; const sep = methodName.match(idStart) ? "." : " "; raiseClassError(desc + 'argument to "' + escapestring(this._value) + '"' + sep + methodName + ' is not a ' + className); } GraceString.prototype = { failArgCheck: failStringMethodArgCheck, methods: { "isMe(1)": value_isMe, "myIdentityHash": string_identityHash, "≠(1)": object_notEquals, "basicAsString": object_basicAsString, "debug$Iterator": object_debugIterator, "::(1)": object_colonColon, "++(1)": function(argcv, other) { var o = other.className == "string" ? other : request(other, "asString", [0]); if (this._value.length === 0) return o; if (o._value.length === 0) return this; return new GraceString(this._value + o._value); }, ">>(1)": function(argcv, target) { return callmethod(target, "<<(1)", [1], this); }, "<<(1)": function(argcv, source) { return callmethod(this, "++(1)", [1], source); }, "at(1)": string_at, "size": function(argcv) { return new GraceNum(this._value.length); }, "sizeIfUnknown(1)": function(argcv) { return new GraceNum(this._value.length); }, "isEmpty": function(argcv) { return (this._value.length === 0) ? GraceTrue : GraceFalse; }, "first": function() { return string_curriedAt.call(this, 1); }, "second": function() { return string_curriedAt.call(this, 2); }, "third": function() { return string_curriedAt.call(this, 3); }, "fourth": function() { return string_curriedAt.call(this, 4); }, "fifth": function() { return string_curriedAt.call(this, 5); }, "last": function() { return string_curriedAt.call(this, this._value.length); }, "quoted": function(argcv) { var s = this._value; var tmp = s.replace(/\\/g, '\\\\'); // replaces backslash tmp = tmp.replace(/"/g, '\\"'); // quotes double-quote tmp = tmp.replace(/\n/g, '\\n'); // quotes newline tmp = tmp.replace(/\t/g, '\\t'); // quotes tab return new GraceString(tmp); }, "replace(1)with(1)": function(argcv, what, wth) { var s = this._value; var os = ""; if (what.className !== "string") { this.failArgCheck('first ', "string", "replace(_)with(_)"); } if (wth.className !== "string") { this.failArgCheck('second ', "string", "replace(_)with(_)"); } let pattern = new RegExp(escapeRegExp(what._value), 'gm'); let replacement = escapeReplacement(wth._value); // a string, not a RegExp, but must escape any $ characters os = s.replace(pattern, replacement); return new GraceString(os); }, "indexOf(1)": function string_indexOf (argcv, needle) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('', "string", "indexOf(_)"); } var result = self.indexOf(needle._value); return new GraceNum(result + 1); }, "indexOf(1)ifAbsent(1)": function string_indexOf_ifAbsent (argcv, needle, block0) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('first ', "string", "indexOf(_)ifAbsent(_)"); } var result = self.indexOf(needle._value); if (result === -1) { return callmethod(block0, "apply", [0]); } return new GraceNum(result + 1); }, "indexOf(1)startingAt(1)": function string_indexOf_startingAt (argcv, needle, startPos) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('first ', "string", "indexOf(_)startingAt(_)"); } if (startPos.className !== "number") { this.failArgCheck('second ', "number", "indexOf(_)startingAt(_)"); } var start = startPos._value - 1; var result = self.indexOf(needle._value, start); if (result === -1) { return new GraceNum(0); } return new GraceNum(result + 1); }, "indexOf(1)startingAt(1)ifAbsent(1)": function string_indexOf_startingAt_ifAbsent (argcv, needle, startPos, block0) { if (needle.className !== "string") { this.failArgCheck('first ', "string", "indexOf(_)startingAt(_)ifAbsent(_)"); } if (startPos.className !== "number") { this.failArgCheck('second ', "number", "indexOf(_)startingAt(_)ifAbsent(_)"); } const self = this._value; const start = startPos._value - 1; const result = self.indexOf(needle._value, start); if (result === -1) { return callmethod(block0, "apply", [0]); } return new GraceNum(result + 1); }, "lastIndexOf(1)": function string_lastIndexOf (argcv, needle) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('', "string", "indexOf(_)"); } var result = self.lastIndexOf(needle._value); return new GraceNum(result + 1); }, "lastIndexOf(1)startingAt(1)": function string_lastIndexOf_startingAt (argcv, needle, startPos) { if (needle.className !== "string") { this.failArgCheck('first ', "string", "indexOf(_)startingAt(_)"); } if (startPos.className !== "number") { this.failArgCheck('second ', "number", "indexOf(_)startingAt(_)"); } var self = this._value; var start = startPos._value - 1; var result = self.lastIndexOf(needle._value, start); return new GraceNum(result + 1); }, "lastIndexOf(1)startingAt(1)ifAbsent(1)": function string_lastIndexOf_startingAt_ifAbsent (argcv, needle, startPos, block0) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('first ', "string", "lastIndexOf(_)startingAt(_)ifAbsent(_)"); } if (startPos.className !== "number") { this.failArgCheck('second ', "number", "lastIndexOf(_)startingAt(_)ifAbsent(_)"); } var start = startPos._value - 1; var result = self.lastIndexOf(needle._value, start); if (result === -1) { return callmethod(block0, "apply", [0]); } return new GraceNum(result + 1); }, "lastIndexOf(1)ifAbsent(1)": function string_lastIndexOf (argcv, needle, block0) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('first ', "string", "lastIndexOf(_)ifAbsent(_)"); } var result = self.lastIndexOf(needle._value); if (result === -1) { return callmethod(block0, "apply", [0]); } return new GraceNum(result + 1); }, "contains(1)": function string_contains(argcv, needle) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('', "string", "contains(_)"); } var result = self.indexOf(needle._value); if (result === -1) { return GraceFalse; } return GraceTrue; }, "anySatisfy(1)": function anySatisfy(argcv, predicate) { var self = this._value; for (let i=0; i<self.length; i++) { const candidate = new GraceString(self[i]); if (Grace_isTrue(request(predicate, "apply(1)", [1], candidate))) { return GraceTrue; } } return GraceFalse; }, "allSatisfy(1)": function allSatisfy(argcv, predicate) { var self = this._value; for (let i=0; i<self.length; i++) { const candidate = new GraceString(self[i]); if (! Grace_isTrue(request(predicate, "apply(1)", [1], candidate))) { return GraceFalse; } } return GraceTrue; }, "split(1)": function string_split (argcv, spliter) { const self = this._value; if (spliter.className !== "string") { this.failArgCheck('', "string", "split(_)"); } const ary = self.split(spliter._value); const len = ary.length; for (var i = 0 ; i < len ; i++) { ary[i] = new GraceString(ary[i]); } return new GraceList(ary); }, "trim": function string_trim (argcv) { var self = this._value; return new GraceString(self.trim()); }, "substringFrom(1)to(1)": function string_substringFrom_to(argcv, from, to) { var s = this._value; if (from.className !== "number") { this.failArgCheck('first ', "number", "substringFrom(_)to(_)"); } if (to.className !== "number") { this.failArgCheck('second ', "number", "substringFrom(_)to(_)"); } var start = from._value; var stop = to._value; // we deliberately allow "abc".substringFrom 4 to (stop) // and for stop to be beyond the end of s if ((start < 1) || (start > s.length + 1)) { var msgstr = s.length <= 20 ? s : (s.substr(0, 17) + "…"); throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + "\".substringFrom(" + start + ")to(" + stop + ") but string.size = " + s.length)); } return new GraceString(s.substring(start - 1, stop)); }, "substringFrom(1)size(1)": function string_substringFrom_size(argcv, from, size) { var s = this._value; if (from.className !== "number") { this.failArgCheck('first ', "number", "substringFrom(_)to(_)"); } if (size.className !== "number") { this.failArgCheck('second ', "number", "substringFrom(_)to(_)"); } var start = from._value; var n = size._value; // we deliberatly allow "abc".substringFrom 4 size (size) // and for start + n to extend beyond the end of s. if ((start < 1) || (start > s.length + 1)) { var msgstr = s.length <= 20 ? s : (s.substr(0, 17) + "…"); throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + "\".substringFrom(" + start + ")size(" + n + ") but string.size = " + s.length)); } return new GraceString(s.substr(start - 1, n)); }, "substringFrom(1)": function string_substringFrom_size(argcv, from) { var s = this._value; if (from.className !== "number") { this.failArgCheck('', "number", "substringFrom(_)to(_)"); } var start = from._value; var n = s.length; // we deliberatly allow "abc".substringFrom 4 if ((start < 1) || (start > s.length + 1)) { var msgstr = s.length <= 20 ? s : (s.substr(0, 17) + "…"); throw new GraceExceptionPacket(BoundsErrorObject, new GraceString('"' + msgstr + "\".substringFrom(" + start + ") but string.size = " + s.length)); } return new GraceString(s.substr(start - 1, n)); }, "startsWith(1)": function string_startsWith(argcv, needle) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('', "string", "startsWith(_)"); } var n = needle._value; if (self.lastIndexOf(n, 0) === 0) return GraceTrue; return GraceFalse; }, "endsWith(1)": function string_endsWith(argcv, needle) { var self = this._value; if (needle.className !== "string") { this.failArgCheck('', "string", "endsWith(_)"); } var n = needle._value; var startPosition = self.length - n.length; if (startPosition < 1) return GraceFalse; var lastIndex = self.lastIndexOf(n, startPosition); if (lastIndex === startPosition) return GraceTrue; return GraceFalse; }, "capitalized": function string_capitalized(argcv) { var self = this._value; return new GraceString(capitalize(self)); }, "asLower": function string_asLower(argcv) { var self = this._value; return new GraceString(self.toLowerCase()); }, "asUpper": function string_asUpper(argcv) { var self = this._value; return new GraceString(self.toUpperCase()); }, "asString": function string_asString(argcv) { return this ; }, "asDebugString": function string_asDebugString (argcv) { var quote = new GraceString("\""); var self = callmethod(this, "quoted", [0]); var qSelf = callmethod(quote, "++(1)", [1], self); var qSelfq = callmethod(qSelf, "++(1)", [1], quote); return qSelfq; }, "compare(1)": function string_compare (argcv, that) { var self = this._value; if (that.className !== "string") { this.failArgCheck('', "string", "compare(_)"); } var other = that._value; if (self === other) return new GraceNum(0); if (self > other) return new GraceNum(+1); return new GraceNum(-1); }, ">(1)": function string_greaterThan (argcv, that) { var self = this._value; if (that.className !== "string") { this.failArgCheck('', "string", ">(_)"); } var other = that._value; if (self > other) return GraceTrue; return GraceFalse; }, "<(1)": function string_lessThan (argcv, that) { var self = this._value; if (that.className !== "string") { this.failArgCheck('', "string", "<(_)"); } var other = that._value; if (self < other) return GraceTrue; return GraceFalse; }, "≤(1)": string_lessThanOrEqual, "≥(1)": string_greaterThanOrEqual, "==(1)": function string_equal(argcv, other) { if (this === other) return GraceTrue; if ("string" === other.className && this._value === other._value) return GraceTrue; return GraceFalse; }, "iterator": function string_iterator(argcv) { return new GraceStringIterator(this); }, "do(1)": function string_do(argcv, action1) { var self = this._value; var size = self.length; for (var ix = 0; ix < size; ix ++) { callmethod(action1, "apply(1)", [1], new GraceString(self[ix])); } return GraceDone; }, "do(1)separatedBy(1)": function string_do_sepBy(argcv, action1, separatorAction) { var self = this._value; var size = self.length; var firstTime = true; for (var ix = 0; ix < size; ix ++) { if (! firstTime) callmethod(separatorAction, "apply", [0]); else firstTime = false; callmethod(action1, "apply(1)", [1], new GraceString(self[ix])); } return GraceDone; }, "keysAndValuesDo(1)": function string_do(argcv, action2) { var self = this._value; var size = self.length; for (var ix = 0; ix < size; ix ++) { callmethod(action2, "apply(2)", [2], new GraceNum(ix+1), new GraceString(self[ix])); } return GraceDone; }, "ord": function string_ord (argcv) { return new GraceNum(this._value.charCodeAt(0)); }, "hash": string_hash, "indices": string_indices, "keys": string_indices, "asNumber": function string_asNumber(argcv) { return new GraceNum(+this._value); }, "startsWithSpace": function string_startsWithSpace (argcv) { var s = this._value.charCodeAt(0); return ( (unicode.inCategory(s, "Zs") ) ? GraceTrue : GraceFalse); }, "startsWithPeriod": function string_startsWithPeriod (argcv) { if (this._value.charCodeAt(0) === 46) return GraceTrue; else return GraceFalse; }, "startsWithDigit": function string_startsWithDigit (argcv) { var s = this._value.charCodeAt(0); return ( ( unicode.inCategory(s, "Nd") || unicode.inCategory(s, "No") || unicode.inCategory(s, "Nl") ) ? GraceTrue : GraceFalse); }, "startsWithLetter": function string_startsWithLetter (argcv) { var c = this._value.charCodeAt(0); return ( ( unicode.inCategory(c, "Ll") || unicode.inCategory(c, "Lu") || unicode.inCategory(c, "Lo") || unicode.inCategory(c, "Lm") ) ? GraceTrue : GraceFalse); }, "*(1)": function string_times (argcv, num) { // We could adapt the Russian Peasant algorithm for multiplication by addition, // but this simpler algorithm will usually use fewer string operations. var n; if ((num.className !== 'number') || (! Number.isInteger(n = num._value))) { raiseClassError("argument to string *(_) must be an integer"); } var output = this._value; var requiredLength = output.length * n; while (output.length < requiredLength) { output = output.concat(output); } output = output.substr(0, requiredLength); return new GraceString(output); }, "reverseTimesNumber(1)": function (argcv, num) { return callmethod(this, "*(1)", [1], num); }, "map(1)": function string_map(argcv, function1) { const collections = loadDynamicModule("collections"); return selfRequest(collections, "lazySequenceOver(1)mappedBy(1)", [1, 1], this, function1); }, "filter(1)": function string_filter(argcv, predicate1) { var self = this._value; var size = self.length; var result = ""; for (var ix = 0; ix < size; ix ++) { var ch = self[ix]; if (GraceTrue === callmethod(predicate1, "apply(1)", [1], new GraceString(ch))) result = result + ch; } return new GraceString(result); }, "fold(1)startingWith(1)": function string_fold(argcv, block2, initialValue) { var self = this._value; var size = self.length; var accum = initialValue; for (var ix = 0; ix < size; ix ++) { accum = callmethod(block2, "apply(2)", [2], accum, new GraceString(self[ix])); } return accum; }, "prefix<": function(argcv) { return new GracePredicatePattern(arg => selfRequest(this, ">(1)", [1], arg)); }, "prefix>": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "string") ? selfRequest(this, "<(1)", [1], arg) : GraceFalse); }, "prefix≤": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "string") ? selfRequest(this, "≥(1)", [1], arg) : GraceFalse); }, "prefix≥": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "string") ? selfRequest(this, "≤(1)", [1], arg) : GraceFalse); }, "prefix==": curriedEquality, "prefix≠": curriedInequality }, className: "string", definitionModule: "built-in library", definitionLine: 0, classUid: "string-built-in" }; const GraceEmptyString = new GraceString(""); function failNumMethodArgCheck(desc, className, methodName) { const idStart = /^[a-zA-Z_]/; const sep = methodName.match(idStart) ? "." : " "; raiseClassError(desc + 'argument to ' + this._value + sep + methodName + ' is not a ' + className); } function pointObject() { if (! pointObject.cache) { const pointBundle = loadDynamicModule("pointBundle"); const openBundle = request(pointBundle, "open"); pointObject.cache = openBundle; } return pointObject.cache; } function value_equals(argcv, other) { // Do NOT test for identity, because then NaN == NaN ! if (this.className === other.className && this._value === other._value) return GraceTrue; return GraceFalse; } value_equals.methodName = "==(_)"; value_equals.paramCounts = [1]; value_equals.paramNames = ["other"]; value_equals.definitionLine = 0; value_equals.definitionModule = "built-in library"; function value_isMe(argcv, other) { // identity test for value objects, which should be considered identical if // they have equal values. Can't re-use value_equals, because isMe is confidential if (this.className === other.className && this._value === other._value) return GraceTrue; return GraceFalse; } value_isMe.confidential = true; // returns the first character of self, as a String of size 1; self must not be empty value_isMe.methodName = "isMe(_)"; value_isMe.paramCounts = [1]; value_isMe.paramNames = ["other"]; value_isMe.definitionLine = 0; value_isMe.definitionModule = "built-in library"; function num_hash (argcv) { var self = this._value; if (isNaN(self)) { throw new GraceExceptionPacket(RequestErrorObject, new GraceString("attempting to hash NaN")); } var raw = self * 7; // was 13, but hash tables can be of size 13! return new GraceNum(Math.abs(raw & raw)); // & converts to 32-bit int } num_hash.methodName = "hash"; num_hash.definitionLine = 0; num_hash.definitionModule = "built-in library"; function num_identityHash (argcv) { return num_hash (argcv); } num_identityHash.confidential = true; function GraceNum(n) { this._value = n; } GraceNum.prototype = { failArgCheck: failNumMethodArgCheck, methods: { "isMe(1)": value_isMe, "myIdentityHash": num_identityHash, "≠(1)": object_notEquals, "basicAsString": object_basicAsString, "debug$Iterator": object_debugIterator, "::(1)": object_colonColon, "+(1)": function(argcv, other) { if (other.className === "number") { var s = this._value + other._value; return new GraceNum(s); } return callmethod(other, "reversePlusNumber(1)", [1], this); }, "*(1)": function(argcv, other) { if (other.className === "number") { var s = this._value * other._value; return new GraceNum(s); } return callmethod(other, "reverseTimesNumber(1)", [1], this); }, "/(1)": function(argcv, other) { if (other.className === "number") { var s = this._value / other._value; return new GraceNum(s); } return callmethod(other, "reverseDivideNumber(1)", [1], this); }, "-(1)": function(argcv, other) { if (other.className === "number") { var s = this._value - other._value; return new GraceNum(s); } return callmethod(other, "reverseMinusNumber(1)", [1], this); }, "^(1)": function(argcv, other) { if (other.className === "number") { var s = Math.pow(this._value, other._value); return new GraceNum(s); } return callmethod(other, "reversePowerNumber(1)", [1], this); }, "%(1)": function(argcv, other) { if (other.className === "number") { var s = this._value % other._value; if (s < 0) s = s + Math.abs(other._value); return new GraceNum(s); } return callmethod(other, "reverseRemainderNumber(1)", [1], this); }, "÷(1)": function(argcv, other) { if (other.className === "number") { var quo = this._value / other._value; var q = Math.trunc(quo); if (this._value >= 0) return new GraceNum(q); if (q === quo) return new GraceNum(q); if (other._value < 0) return new GraceNum(q + 1); return new GraceNum(q - 1); } return callmethod(other, "reverseQuotientNumber(1)", [1], this); }, "@(1)": function(argcv, other) { return callmethod(pointObject(), "point2Dx(1)y(1)", [1, 1], this, other); }, "..(1)": function(argcv, other) { if (! Number.isInteger(this._value)) { raiseException(RequestErrorObject, "lower bound of range (" + this._value + ") not an integer."); } else if (other.className !== "number") { raiseException(TypeErrorObject, "upper bound of range not a Number"); } else if (! Number.isInteger(other._value)) { raiseException(RequestErrorObject, "upper bound of range (" + other._value + ") not an integer"); } return callmethod(GraceRangeClass(), "uncheckedFrom(1)to(1)", [1, 1], this, other); }, "downTo(1)": function(argcv, other) { if (! Number.isInteger(this._value)) { raiseException(RequestErrorObject, "first bound of downward range (" + this._value + ") not an integer."); } else if (other.className !== "number") { raiseException(TypeErrorObject, "second bound of downward range not a Number"); } else if (! Number.isInteger(other._value)) { raiseException(RequestErrorObject, "second bound of downward range (" + other._value + ") not an integer"); } return callmethod(GraceRangeClass(), "from(1)downTo(1)", [1, 1], this, other); }, "compare(1)": function(argcv, that) { var self = this._value; var other = that._value; if (self === other) return new GraceNum(0); if (that.className !== "number") { this.failArgCheck(dbgp(other), "number", "compare(_)"); } if (self > other) return new GraceNum(+1); return new GraceNum(-1); }, "<(1)": function(argcv, other) { if (other.className === "number") { return (this._value < other._value) ? GraceTrue : GraceFalse; } return request(other, "≥(1)", [1], this); }, ">(1)": function(argcv, other) { if (other.className === "number") { return (this._value > other._value) ? GraceTrue : GraceFalse; } return request(other, "≤(1)", [1], this); }, "≤(1)": function(argcv, other) { if (other.className === "number") { return (this._value <= other._value) ? GraceTrue : GraceFalse; } return request(other, ">(1)", [1], this); }, "≥(1)": function(argcv, other) { if (other.className === "number") { return (this._value >= other._value) ? GraceTrue : GraceFalse; } return request(other, "<(1)", [1], this); }, "isInteger": function(argcv) { if (Number.isInteger(this._value)) return GraceTrue; else return GraceFalse; }, "isEven": function(argcv) { if (this._value % 2 === 0) return GraceTrue; else return GraceFalse; }, "isOdd": function(argcv) { // we can't just check that remainder is 1, because negative // arguments give a remaider of -1. This is much faster than // checking for +1 or -1, or taking abs! var val = this._value; if (Number.isInteger(val) && (val % 2 !== 0)) { return GraceTrue; } else { return GraceFalse; } }, "prefix-": function(argcv) { return new GraceNum(-this._value); }, "prefix<": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, ">(1)", [1], arg) : GraceFalse); }, "prefix>": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, "<(1)", [1], arg) : GraceFalse); }, "prefix≤": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, "≥(1)", [1], arg) : GraceFalse); }, "prefix≥": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, "≤(1)", [1], arg) : GraceFalse); }, "prefix==": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, "==(1)", [1], arg) : GraceFalse); }, "prefix≠": function(argcv) { return new GracePredicatePattern(arg => (arg.className == "number") ? selfRequest(this, "≠(1)", [1], arg) : GraceTrue); }, "asString": function(argcv) { var num = this._value; if (num === Infinity) return new GraceString("infinity"); if (num === -Infinity) return new GraceString("-infinity"); // otherwise, print to 6 decimal places return new GraceString((Math.round(num * 1000000) / 1000000).toString()); }, "asDebugString": function(argcv) { return new GraceString("" + this._value); }, "asStringDecimals(1)": function (argcv, other) { var num = this._value; if (num === Infinity) return new GraceString("infinity"); if (num === -Infinity) return new GraceString("-infinity"); var d = other._value; if ((d < 0) || (d > 20)) throw new GraceExceptionPacket(RequestErrorObject, new GraceString("argument to asStringDecimals(_) must be between 0 and 20")); return new GraceString(num.toFixed(d)); }, "==(1)": value_equals, "≠(1)": function(argcv, other) { var t = callmethod(this, "==(1)", [1], other); return callmethod(t, "not", [0]); }, "hash": num_hash, "inBase(1)": function(argcv, other) { var mine = this._value; var base = other._value; var symbols = "0123456789abcdefghijklmnopqrstuvwxyz"; var str = ""; var before = ""; if (mine < 0) { before = '-'; mine = -mine; } while (mine !== 0) { var r = mine % base; str = symbols[r] + str; mine = (mine - r) / base; } if (before) str = before + str; return new GraceString(str); }, "isNaN": function Num_isNaN(argcv) { return (isNaN(this._value)) ? GraceTrue : GraceFalse; }, "sin": function(argcv) { return new GraceNum(Math.sin(this._value)); }, "cos": function(argcv) { return new GraceNum(Math.cos(this._value)); }, "tan": function(argcv) { return new GraceNum(Math.tan(this._value)); }, "asin": function(argcv) { return new GraceNum(Math.asin(this._value)); }, "acos": function(argcv) { return new GraceNum(Math.acos(this._value)); }, "atan": function(argcv) { return new GraceNum(Math.atan(this._value)); }, lg: function(argcv) { return new GraceNum(Math.log(this._value) / Math.LN2); }, ln: function(argcv) { return new GraceNum(Math.log(this._value)); }, exp: function(argcv) { return new GraceNum(Math.exp(this._value)); }, log10: function(argcv) { return new GraceNum(Math.log(this._value) / Math.LN10); }, "truncated": function(argcv) { return new GraceNum(Math.trunc(this._value)); }, "floor": function(argcv) { return new GraceNum(Math.floor(this._value)); }, "ceiling": function(argcv) { return new GraceNum(Math.ceil(this._value)); }, "rounded": function(argcv) { return new GraceNum(Math.round(this._value)); }, "abs": function(argcv) { return new GraceNum(Math.abs(this._value)); }, "sgn": function(argcv) { var n = this._value; if (n != n) throw new GraceExceptionPacket(ProgrammingErrorObject, new GraceString("NaN.sgn is undefined")); if (n === 0) return new GraceNum(0); return new GraceNum(n > 0 ? 1 : -1); }, "sqrt": function(argcv) { return new GraceNum(Math.sqrt(this._value)); } }, className: "number", definitionModule: "built-in library", definitionLine: 0, classUid: "number-built-in" }; function GracePredicatePattern(pred) { // pred is a js function that takes a Grace object as argument, // and answers GraceTrue or GraceFalse this._value = pred; } GracePredicatePattern.prototype = { methods: { "isMe(1)": object_isMe, "myIdentityHash": object_identityHash, "≠(1)": object_notEquals, "basicAsString": object_basicAsString, "debug$Iterator": object_debugIterator, "::(1)": object_colonColon, "asString": function(argcv) { return new GraceString("a predicate pattern"); }, "asDebugString": function(argcv) { return new GraceString("a predicate pattern"); }, "matches(1)": function predicate_match (argcv, o) { return (this._value(o)); }, "|(1)": function predicate_orPattern(argcv, o) { return graceOrPattern(this, o); }, "&(1)": function predicate_andPattern (argcv, o) { return graceAndPattern(this, o); }, "prefix¬": function predicate_notPattern (argcv) { return graceNotPattern(this); }, "isType": function type_isType (argcv) { return GraceFalse; } }, className: "predicatePattern", definitionModule: "built-in library", definitionLine: 1171, classUid: "predicatePattern-built-in" } var GraceTrue; var GraceFalse; (function () { function Boolean(b) { this._value = b; } Boolean.prototype = { methods: { "isMe(1)": object_isMe, "myIdentityHash": object_identityHash, "≠(1)": object_notEquals, "basicAsString": object_basicAsString, "debug$Iterator": object_debugIterator, "::(1)": object_colonColon, "ifTrue(1)": function(argcv, action) { return (this._value) ? request(action, "apply") : GraceDone; }, "ifFalse(1)": function(argcv, action) { return (this._value) ? GraceDone : request(action, "apply"); }, "ifTrue(1)ifFalse(1)": function(argcv, trueAction, falseAction) { return ((this._value) ? request(trueAction, "apply") : request(falseAction, "apply")); }, "ifFalse(1)ifTrue(1)": function(argcv, falseAction, trueAction) { return ((this._value) ? request(trueAction, "apply") : request(falseAction, "apply")); }, "not": function(argcv) { return this.negated; }, "prefix!": function(argcv) { return this.negated; }, "prefix==": curriedEquality, "prefix≠": curriedInequality, "&&(1)": function(argcv, other) { if (!this._value) return this; if ((this.className === other.className) || other.methods['ifTrue(1)ifFalse(1)']) return other; return request(other, "apply", [0]); }, "||(1)": function(argcv, other) { if (this._value) return this; if ((this.className === other.className) || other.methods['ifTrue(1)ifFalse(1)']) return other; return request(other, "apply", [0]); }, "asSt