minigrace
Version:
A compiler for the Grace programming language
1,277 lines (1,224 loc) • 148 kB
JavaScript
/* 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