UNPKG

fable-compiler

Version:
686 lines 19.8 kB
// tslint:disable:ban-types export function bindThis(this$, source) { for (const key of Object.keys(source)) { if (typeof source[key] === "function") { source[key] = source[key].bind(this$); } } return source; } // Object.assign flattens getters and setters // See https://stackoverflow.com/questions/37054596/js-es5-how-to-assign-objects-with-setters-and-getters export function extend(target, ...sources) { for (const source of sources) { for (const key of Object.keys(source)) { const descr = Object.getOwnPropertyDescriptor(source, key); if (descr) { Object.defineProperty(target, key, descr); } } } return target; } export function isIterable(x) { return x != null && typeof x === "object" && Symbol.iterator in x; } export function isArrayLike(x) { return x != null && (Array.isArray(x) || ArrayBuffer.isView(x)); } export function isComparer(x) { return x != null && typeof x.Compare === "function"; } export function isComparable(x) { return x != null && typeof x.CompareTo === "function"; } export function isEquatable(x) { return x != null && typeof x.Equals === "function"; } export function isHashable(x) { return x != null && typeof x.GetHashCode === "function"; } export function isDisposable(x) { return x != null && typeof x.Dispose === "function"; } export class Comparer { constructor(f) { this.Compare = f || compare; } } export function comparerFromEqualityComparer(comparer) { // Sometimes IEqualityComparer also implements IComparer if (isComparer(comparer)) { return new Comparer(comparer.Compare); } else { return new Comparer((x, y) => { const xhash = comparer.GetHashCode(x); const yhash = comparer.GetHashCode(y); if (xhash === yhash) { return comparer.Equals(x, y) ? 0 : -1; } else { return xhash < yhash ? -1 : 1; } }); } } // TODO: Move these three methods to Map and Set modules export function containsValue(v, map) { for (const kv of map) { if (equals(v, kv[1])) { return true; } } return false; } export function tryGetValue(map, key, defaultValue) { return map.has(key) ? [true, map.get(key)] : [false, defaultValue]; } export function addToSet(v, set) { if (set.has(v)) { return false; } set.add(v); return true; } export function assertEqual(actual, expected, msg) { if (!equals(actual, expected)) { throw Object.assign(new Error(msg || `Expected: ${expected} - Actual: ${actual}`), { actual, expected, }); } } export function assertNotEqual(actual, expected, msg) { if (equals(actual, expected)) { throw Object.assign(new Error(msg || `Expected: ${expected} - Actual: ${actual}`), { actual, expected, }); } } export class Lazy { constructor(factory) { this.factory = factory; this.isValueCreated = false; } get Value() { if (!this.isValueCreated) { this.createdValue = this.factory(); this.isValueCreated = true; } return this.createdValue; } get IsValueCreated() { return this.isValueCreated; } } export function lazyFromValue(v) { return new Lazy(() => v); } export function padWithZeros(i, length) { let str = i.toString(10); while (str.length < length) { str = "0" + str; } return str; } export function padLeftAndRightWithZeros(i, lengthLeft, lengthRight) { let str = i.toString(10); while (str.length < lengthLeft) { str = "0" + str; } while (str.length < lengthRight) { str = str + "0"; } return str; } export function dateOffset(date) { const date1 = date; return typeof date1.offset === "number" ? date1.offset : (date.kind === 1 /* UTC */ ? 0 : date.getTimezoneOffset() * -60000); } export function int16ToString(i, radix) { i = i < 0 && radix != null && radix !== 10 ? 0xFFFF + i + 1 : i; return i.toString(radix); } export function int32ToString(i, radix) { i = i < 0 && radix != null && radix !== 10 ? 0xFFFFFFFF + i + 1 : i; return i.toString(radix); } let ObjectRef = /** @class */ (() => { class ObjectRef { static id(o) { if (!ObjectRef.idMap.has(o)) { ObjectRef.idMap.set(o, ++ObjectRef.count); } return ObjectRef.idMap.get(o); } } ObjectRef.idMap = new WeakMap(); ObjectRef.count = 0; return ObjectRef; })(); export { ObjectRef }; export function stringHash(s) { let i = 0; let h = 5381; const len = s.length; while (i < len) { h = (h * 33) ^ s.charCodeAt(i++); } return h; } export function numberHash(x) { return x * 2654435761 | 0; } // From https://stackoverflow.com/a/37449594 export function combineHashCodes(hashes) { if (hashes.length === 0) { return 0; } return hashes.reduce((h1, h2) => { return ((h1 << 5) + h1) ^ h2; }); } export function identityHash(x) { if (x == null) { return 0; } switch (typeof x) { case "boolean": return x ? 1 : 0; case "number": return numberHash(x); case "string": return stringHash(x); default: return numberHash(ObjectRef.id(x)); } } export function structuralHash(x) { if (x == null) { return 0; } switch (typeof x) { case "boolean": return x ? 1 : 0; case "number": return numberHash(x); case "string": return stringHash(x); default: { if (isHashable(x)) { return x.GetHashCode(); } else if (isArrayLike(x)) { const len = x.length; const hashes = new Array(len); for (let i = 0; i < len; i++) { hashes[i] = structuralHash(x[i]); } return combineHashCodes(hashes); } else { return stringHash(String(x)); } } } } export function equalArraysWith(x, y, eq) { if (x == null) { return y == null; } if (y == null) { return false; } if (x.length !== y.length) { return false; } for (let i = 0; i < x.length; i++) { if (!eq(x[i], y[i])) { return false; } } return true; } export function equalArrays(x, y) { return equalArraysWith(x, y, equals); } // export function equalObjects(x: { [k: string]: any }, y: { [k: string]: any }): boolean { // if (x == null) { return y == null; } // if (y == null) { return false; } // const xKeys = Object.keys(x); // const yKeys = Object.keys(y); // if (xKeys.length !== yKeys.length) { // return false; // } // xKeys.sort(); // yKeys.sort(); // for (let i = 0; i < xKeys.length; i++) { // if (xKeys[i] !== yKeys[i] || !equals(x[xKeys[i]], y[yKeys[i]])) { // return false; // } // } // return true; // } export function equals(x, y) { if (x === y) { return true; } else if (x == null) { return y == null; } else if (y == null) { return false; } else if (typeof x !== "object") { return false; } else if (isEquatable(x)) { return x.Equals(y); } else if (isArrayLike(x)) { return isArrayLike(y) && equalArrays(x, y); } else if (x instanceof Date) { return (y instanceof Date) && compareDates(x, y) === 0; } else { return false; } } export function compareDates(x, y) { let xtime; let ytime; // DateTimeOffset and DateTime deals with equality differently. if ("offset" in x && "offset" in y) { xtime = x.getTime(); ytime = y.getTime(); } else { xtime = x.getTime() + dateOffset(x); ytime = y.getTime() + dateOffset(y); } return xtime === ytime ? 0 : (xtime < ytime ? -1 : 1); } export function comparePrimitives(x, y) { return x === y ? 0 : (x < y ? -1 : 1); } export function compareArraysWith(x, y, comp) { if (x == null) { return y == null ? 0 : 1; } if (y == null) { return -1; } if (x.length !== y.length) { return x.length < y.length ? -1 : 1; } for (let i = 0, j = 0; i < x.length; i++) { j = comp(x[i], y[i]); if (j !== 0) { return j; } } return 0; } export function compareArrays(x, y) { return compareArraysWith(x, y, compare); } export function compareObjects(x, y) { if (x == null) { return y == null ? 0 : 1; } if (y == null) { return -1; } const xKeys = Object.keys(x); const yKeys = Object.keys(y); if (xKeys.length !== yKeys.length) { return xKeys.length < yKeys.length ? -1 : 1; } xKeys.sort(); yKeys.sort(); for (let i = 0, j = 0; i < xKeys.length; i++) { const key = xKeys[i]; if (key !== yKeys[i]) { return key < yKeys[i] ? -1 : 1; } else { j = compare(x[key], y[key]); if (j !== 0) { return j; } } } return 0; } export function compare(x, y) { if (x === y) { return 0; } else if (x == null) { return y == null ? 0 : -1; } else if (y == null) { return 1; } else if (typeof x !== "object") { return x < y ? -1 : 1; } else if (isComparable(x)) { return x.CompareTo(y); } else if (isArrayLike(x) && isArrayLike(y)) { return compareArrays(x, y); } else if (x instanceof Date && y instanceof Date) { return compareDates(x, y); } else { return 1; } } export function min(comparer, x, y) { return comparer(x, y) < 0 ? x : y; } export function max(comparer, x, y) { return comparer(x, y) > 0 ? x : y; } export function createAtom(value) { let atom = value; return (value, isSetter) => { if (!isSetter) { return atom; } else { atom = value; return void 0; } }; } const CaseRules = { None: 0, LowerFirst: 1, SnakeCase: 2, SnakeCaseAllCaps: 3, KebabCase: 4, }; function dashify(str, separator) { return str.replace(/[a-z]?[A-Z]/g, (m) => m.length === 1 ? m.toLowerCase() : m.charAt(0) + separator + m.charAt(1).toLowerCase()); } function changeCase(str, caseRule) { switch (caseRule) { case CaseRules.LowerFirst: return str.charAt(0).toLowerCase() + str.slice(1); case CaseRules.SnakeCase: return dashify(str, "_"); case CaseRules.SnakeCaseAllCaps: return dashify(str, "_").toUpperCase(); case CaseRules.KebabCase: return dashify(str, "-"); case CaseRules.None: default: return str; } } export function createObj(fields, caseRule = CaseRules.None) { function fail(kvPair) { throw new Error("Cannot infer key and value of " + String(kvPair)); } const o = {}; const definedCaseRule = caseRule; for (let kvPair of fields) { let caseRule = CaseRules.None; if (kvPair == null) { fail(kvPair); } // Deflate unions and use the defined case rule if (typeof kvPair.toJSON === "function") { kvPair = kvPair.toJSON(); caseRule = definedCaseRule; } if (Array.isArray(kvPair)) { switch (kvPair.length) { case 0: fail(kvPair); break; case 1: o[changeCase(kvPair[0], caseRule)] = true; break; case 2: const value = kvPair[1]; o[changeCase(kvPair[0], caseRule)] = value; break; default: o[changeCase(kvPair[0], caseRule)] = kvPair.slice(1); } } else if (typeof kvPair === "string") { o[changeCase(kvPair, caseRule)] = true; } else { fail(kvPair); } } return o; } export function jsOptions(mutator) { const opts = {}; mutator(opts); return opts; } export function round(value, digits = 0) { const m = Math.pow(10, digits); const n = +(digits ? value * m : value).toFixed(8); const i = Math.floor(n); const f = n - i; const e = 1e-8; const r = (f > 0.5 - e && f < 0.5 + e) ? ((i % 2 === 0) ? i : i + 1) : Math.round(n); return digits ? r / m : r; } export function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; } export function randomNext(min, max) { return Math.floor(Math.random() * (max - min)) + min; } export function randomBytes(buffer) { if (buffer == null) { throw new Error("Buffer cannot be null"); } for (let i = 0; i < buffer.length; i += 6) { // Pick random 48-bit number. Fill buffer in 2 24-bit chunks to avoid bitwise truncation. let r = Math.floor(Math.random() * 281474976710656); // Low 24 bits = chunk 1. const rhi = Math.floor(r / 16777216); // High 24 bits shifted via division = chunk 2. for (let j = 0; j < 6 && i + j < buffer.length; j++) { if (j === 3) { r = rhi; } buffer[i + j] = r & 255; r >>>= 8; } } } export function unescapeDataString(s) { // https://stackoverflow.com/a/4458580/524236 return decodeURIComponent((s).replace(/\+/g, "%20")); } export function escapeDataString(s) { return encodeURIComponent(s).replace(/!/g, "%21") .replace(/'/g, "%27") .replace(/\(/g, "%28") .replace(/\)/g, "%29") .replace(/\*/g, "%2A"); } export function escapeUriString(s) { return encodeURI(s); } // ICollection.Clear and Count members can be called on Arrays // or Dictionaries so we need a runtime check (see #1120) export function count(col) { if (isArrayLike(col)) { return col.length; } else { let count = 0; for (const _ of col) { count++; } return count; } } export function clear(col) { if (isArrayLike(col)) { col.splice(0); } else { col.clear(); } } const CURRIED_KEY = "__CURRIED__"; export function uncurry(arity, f) { // f may be a function option with None value if (f == null) { return undefined; } // The function is already uncurried if (f.length > 1) { // if (CURRIED_KEY in f) { // This doesn't always work return f; } let uncurriedFn; switch (arity) { case 2: uncurriedFn = (a1, a2) => f(a1)(a2); break; case 3: uncurriedFn = (a1, a2, a3) => f(a1)(a2)(a3); break; case 4: uncurriedFn = (a1, a2, a3, a4) => f(a1)(a2)(a3)(a4); break; case 5: uncurriedFn = (a1, a2, a3, a4, a5) => f(a1)(a2)(a3)(a4)(a5); break; case 6: uncurriedFn = (a1, a2, a3, a4, a5, a6) => f(a1)(a2)(a3)(a4)(a5)(a6); break; case 7: uncurriedFn = (a1, a2, a3, a4, a5, a6, a7) => f(a1)(a2)(a3)(a4)(a5)(a6)(a7); break; case 8: uncurriedFn = (a1, a2, a3, a4, a5, a6, a7, a8) => f(a1)(a2)(a3)(a4)(a5)(a6)(a7)(a8); break; default: throw new Error("Uncurrying to more than 8-arity is not supported: " + arity); } uncurriedFn[CURRIED_KEY] = f; return uncurriedFn; } export function curry(arity, f) { if (f == null) { return undefined; } if (CURRIED_KEY in f) { return f[CURRIED_KEY]; } switch (arity) { case 2: return (a1) => (a2) => f(a1, a2); case 3: return (a1) => (a2) => (a3) => f(a1, a2, a3); case 4: return (a1) => (a2) => (a3) => (a4) => f(a1, a2, a3, a4); case 5: return (a1) => (a2) => (a3) => (a4) => (a5) => f(a1, a2, a3, a4, a5); case 6: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => f(a1, a2, a3, a4, a5, a6); case 7: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => (a7) => f(a1, a2, a3, a4, a5, a6, a7); case 8: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => (a7) => (a8) => f(a1, a2, a3, a4, a5, a6, a7, a8); default: throw new Error("Currying to more than 8-arity is not supported: " + arity); } } export function partialApply(arity, f, args) { if (f == null) { return undefined; } else if (CURRIED_KEY in f) { f = f[CURRIED_KEY]; for (let i = 0; i < args.length; i++) { f = f(args[i]); } return f; } else { switch (arity) { case 1: // Wrap arguments to make sure .concat doesn't destruct arrays. Example // [1,2].concat([3,4],5) --> [1,2,3,4,5] // fails // [1,2].concat([[3,4],5]) --> [1,2,[3,4],5] // ok return (a1) => f.apply(undefined, args.concat([a1])); case 2: return (a1) => (a2) => f.apply(undefined, args.concat([a1, a2])); case 3: return (a1) => (a2) => (a3) => f.apply(undefined, args.concat([a1, a2, a3])); case 4: return (a1) => (a2) => (a3) => (a4) => f.apply(undefined, args.concat([a1, a2, a3, a4])); case 5: return (a1) => (a2) => (a3) => (a4) => (a5) => f.apply(undefined, args.concat([a1, a2, a3, a4, a5])); case 6: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => f.apply(undefined, args.concat([a1, a2, a3, a4, a5, a6])); case 7: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => (a7) => f.apply(undefined, args.concat([a1, a2, a3, a4, a5, a6, a7])); case 8: return (a1) => (a2) => (a3) => (a4) => (a5) => (a6) => (a7) => (a8) => f.apply(undefined, args.concat([a1, a2, a3, a4, a5, a6, a7, a8])); default: throw new Error("Partially applying to more than 8-arity is not supported: " + arity); } } } export function mapCurriedArgs(fn, mappings) { function mapArg(fn, arg, mappings, idx) { const mapping = mappings[idx]; if (mapping !== 0) { const expectedArity = mapping[0]; const actualArity = mapping[1]; if (expectedArity > 1) { arg = curry(expectedArity, arg); } if (actualArity > 1) { arg = uncurry(actualArity, arg); } } const res = fn(arg); if (idx + 1 === mappings.length) { return res; } else { return (arg) => mapArg(res, arg, mappings, idx + 1); } } return (arg) => mapArg(fn, arg, mappings, 0); } export function addToDict(dict, k, v) { if (dict.has(k)) { throw new Error("An item with the same key has already been added. Key: " + k); } dict.set(k, v); } export function getItemFromDict(map, key) { if (map.has(key)) { return map.get(key); } else { throw new Error(`The given key '${key}' was not present in the dictionary.`); } } //# sourceMappingURL=Util.js.map