UNPKG

fable-compiler

Version:
477 lines 16.4 kB
import { toString as dateToString } from "./Date"; import Long, { fromBytes as longFromBytes, toBytes as longToBytes, toString as longToString } from "./Long"; import { escape } from "./RegExp"; const fsFormatRegExp = /(^|[^%])%([0+ ]*)(-?\d+)?(?:\.(\d+))?(\w)/; const formatRegExp = /\{(\d+)(,-?\d+)?(?:\:([a-zA-Z])(\d{0,2})|\:(.+?))?\}/g; // RFC 4122 compliant. From https://stackoverflow.com/a/13653180/3922220 // const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; // Relax GUID parsing, see #1637 const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/; function cmp(x, y, ic) { function isIgnoreCase(i) { return i === true || i === 1 /* CurrentCultureIgnoreCase */ || i === 3 /* InvariantCultureIgnoreCase */ || i === 5 /* OrdinalIgnoreCase */; } function isOrdinal(i) { return i === 4 /* Ordinal */ || i === 5 /* OrdinalIgnoreCase */; } if (x == null) { return y == null ? 0 : -1; } if (y == null) { return 1; } // everything is bigger than null if (isOrdinal(ic)) { if (isIgnoreCase(ic)) { x = x.toLowerCase(); y = y.toLowerCase(); } return (x === y) ? 0 : (x < y ? -1 : 1); } else { if (isIgnoreCase(ic)) { x = x.toLocaleLowerCase(); y = y.toLocaleLowerCase(); } return x.localeCompare(y); } } export function compare(...args) { switch (args.length) { case 2: return cmp(args[0], args[1], false); case 3: return cmp(args[0], args[1], args[2]); case 4: return cmp(args[0], args[1], args[2] === true); case 5: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), false); case 6: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), args[5]); case 7: return cmp(args[0].substr(args[1], args[4]), args[2].substr(args[3], args[4]), args[5] === true); default: throw new Error("String.compare: Unsupported number of parameters"); } } export function compareOrdinal(x, y) { return cmp(x, y, 4 /* Ordinal */); } export function compareTo(x, y) { return cmp(x, y, 0 /* CurrentCulture */); } export function startsWith(str, pattern, ic) { if (str.length >= pattern.length) { return cmp(str.substr(0, pattern.length), pattern, ic) === 0; } return false; } export function indexOfAny(str, anyOf, ...args) { if (str == null || str === "") { return -1; } const startIndex = (args.length > 0) ? args[0] : 0; if (startIndex < 0) { throw new Error("Start index cannot be negative"); } const length = (args.length > 1) ? args[1] : str.length - startIndex; if (length < 0) { throw new Error("Length cannot be negative"); } if (length > str.length - startIndex) { throw new Error("Invalid startIndex and length"); } str = str.substr(startIndex, length); for (const c of anyOf) { const index = str.indexOf(c); if (index > -1) { return index + startIndex; } } return -1; } function toHex(x) { if (x instanceof Long) { return longToString(x.unsigned ? x : longFromBytes(longToBytes(x), true), 16); } else { return (Number(x) >>> 0).toString(16); } } export function printf(input) { return { input, cont: fsFormat(input), }; } export function toConsole(arg) { // Don't remove the lambda here, see #1357 return arg.cont((x) => { console.log(x); }); } export function toConsoleError(arg) { return arg.cont((x) => { console.error(x); }); } export function toText(arg) { return arg.cont((x) => x); } export function toFail(arg) { return arg.cont((x) => { throw new Error(x); }); } function formatOnce(str2, rep) { return str2.replace(fsFormatRegExp, (_, prefix, flags, pad, precision, format) => { switch (format) { case "f": case "F": rep = Number(rep).toFixed(precision || 6); break; case "g": case "G": rep = Number(rep).toPrecision(precision); break; case "e": case "E": rep = Number(rep).toExponential(precision); break; case "O": case "A": rep = String(rep); break; case "x": rep = toHex(rep); break; case "X": rep = toHex(rep).toUpperCase(); break; } const plusPrefix = flags.indexOf("+") >= 0 && parseInt(rep, 10) >= 0; pad = parseInt(pad, 10); if (!isNaN(pad)) { const ch = pad >= 0 && flags.indexOf("0") >= 0 ? "0" : " "; rep = padLeft(String(rep), Math.abs(pad) - (plusPrefix ? 1 : 0), ch, pad < 0); } const once = prefix + (plusPrefix ? "+" + rep : rep); return once.replace(/%/g, "%%"); }); } function createPrinter(str, cont) { return (...args) => { // Make a copy as the function may be used several times let strCopy = str; for (const arg of args) { strCopy = formatOnce(strCopy, arg); } return fsFormatRegExp.test(strCopy) ? createPrinter(strCopy, cont) : cont(strCopy.replace(/%%/g, "%")); }; } export function fsFormat(str) { return (cont) => { return fsFormatRegExp.test(str) ? createPrinter(str, cont) : cont(str); }; } export function format(str, ...args) { if (typeof str === "object" && args.length > 0) { // Called with culture info str = args[0]; args.shift(); } return str.replace(formatRegExp, (match, idx, pad, format, precision, pattern) => { let rep = args[idx]; let padSymbol = " "; const isNumericType = (typeof rep === "number") || (rep instanceof Long); // TODO: || (rep instanceof Decimal) || (rep instanceof BigInt); if (isNumericType) { switch (format) { case "f": case "F": rep = precision ? rep.toFixed(precision) : rep.toFixed(2); break; case "g": case "G": rep = precision ? rep.toPrecision(precision) : rep.toPrecision(); break; case "e": case "E": rep = precision ? rep.toExponential(precision) : rep.toExponential(); break; case "p": case "P": rep = (precision ? (rep * 100).toFixed(precision) : (rep * 100).toFixed(2)) + " %"; break; case "d": case "D": rep = precision ? padLeft(rep.toString(), precision, "0") : rep.toString(); break; case "x": case "X": rep = precision ? padLeft(toHex(rep), precision, "0") : toHex(rep); if (format === "X") { rep = rep.toUpperCase(); } break; default: const m = /^(0+)(\.0+)?$/.exec(pattern); if (m != null) { let decs = 0; if (m[2] != null) { rep = rep.toFixed(decs = m[2].length - 1); } pad = "," + (m[1].length + (decs ? decs + 1 : 0)).toString(); padSymbol = "0"; } else if (pattern) { rep = pattern; } } } else if (rep instanceof Date) { rep = dateToString(rep, pattern || format); } pad = parseInt((pad || "").substring(1), 10); if (!isNaN(pad)) { rep = padLeft(String(rep), Math.abs(pad), padSymbol, pad < 0); } return rep; }); } export function endsWith(str, search) { const idx = str.lastIndexOf(search); return idx >= 0 && idx === str.length - search.length; } export function initialize(n, f) { if (n < 0) { throw new Error("String length must be non-negative"); } const xs = new Array(n); for (let i = 0; i < n; i++) { xs[i] = f(i); } return xs.join(""); } export function insert(str, startIndex, value) { if (startIndex < 0 || startIndex > str.length) { throw new Error("startIndex is negative or greater than the length of this instance."); } return str.substring(0, startIndex) + value + str.substring(startIndex); } export function isNullOrEmpty(str) { return typeof str !== "string" || str.length === 0; } export function isNullOrWhiteSpace(str) { return typeof str !== "string" || /^\s*$/.test(str); } export function join(delimiter, ...xs) { return xs.map((x) => String(x)).join(delimiter); } export function joinWithIndices(delimiter, xs, startIndex, count) { const endIndexPlusOne = startIndex + count; if (endIndexPlusOne > xs.length) { throw new Error("Index and count must refer to a location within the buffer."); } return join(delimiter, ...xs.slice(startIndex, endIndexPlusOne)); } /** Validates UUID as specified in RFC4122 (versions 1-5). Trims braces. */ export function validateGuid(str, doNotThrow) { const trimmedAndLowered = trim(str, "{", "}").toLowerCase(); if (guidRegex.test(trimmedAndLowered)) { return doNotThrow ? [true, trimmedAndLowered] : trimmedAndLowered; } else if (doNotThrow) { return [false, "00000000-0000-0000-0000-000000000000"]; } throw new Error("Guid should contain 32 digits with 4 dashes: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"); } // From https://gist.github.com/LeverOne/1308368 export function newGuid() { let b = ""; for (let a = 0; a++ < 36;) { b += a * 51 & 52 ? (a ^ 15 ? 8 ^ Math.random() * (a ^ 20 ? 16 : 4) : 4).toString(16) : "-"; } return b; } // Maps for number <-> hex string conversion let _convertMapsInitialized = false; let _byteToHex; let _hexToByte; function initConvertMaps() { _byteToHex = new Array(256); _hexToByte = {}; for (let i = 0; i < 256; i++) { _byteToHex[i] = (i + 0x100).toString(16).substr(1); _hexToByte[_byteToHex[i]] = i; } _convertMapsInitialized = true; } /** Parse a UUID into it's component bytes */ // Adapted from https://github.com/zefferus/uuid-parse export function guidToArray(s) { if (!_convertMapsInitialized) { initConvertMaps(); } let i = 0; const buf = new Uint8Array(16); s.toLowerCase().replace(/[0-9a-f]{2}/g, ((oct) => { switch (i) { // .NET saves first three byte groups with different endianness // See https://stackoverflow.com/a/16722909/3922220 case 0: case 1: case 2: case 3: buf[3 - i++] = _hexToByte[oct]; break; case 4: case 5: buf[9 - i++] = _hexToByte[oct]; break; case 6: case 7: buf[13 - i++] = _hexToByte[oct]; break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: buf[i++] = _hexToByte[oct]; break; } })); // Zero out remaining bytes if string was short while (i < 16) { buf[i++] = 0; } return buf; } /** Convert UUID byte array into a string */ export function arrayToGuid(buf) { if (buf.length !== 16) { throw new Error("Byte array for GUID must be exactly 16 bytes long"); } if (!_convertMapsInitialized) { initConvertMaps(); } return _byteToHex[buf[3]] + _byteToHex[buf[2]] + _byteToHex[buf[1]] + _byteToHex[buf[0]] + "-" + _byteToHex[buf[5]] + _byteToHex[buf[4]] + "-" + _byteToHex[buf[7]] + _byteToHex[buf[6]] + "-" + _byteToHex[buf[8]] + _byteToHex[buf[9]] + "-" + _byteToHex[buf[10]] + _byteToHex[buf[11]] + _byteToHex[buf[12]] + _byteToHex[buf[13]] + _byteToHex[buf[14]] + _byteToHex[buf[15]]; } function notSupported(name) { throw new Error("The environment doesn't support '" + name + "', please use a polyfill."); } export function toBase64String(inArray) { let str = ""; for (let i = 0; i < inArray.length; i++) { str += String.fromCharCode(inArray[i]); } return typeof btoa === "function" ? btoa(str) : notSupported("btoa"); } export function fromBase64String(b64Encoded) { const binary = typeof atob === "function" ? atob(b64Encoded) : notSupported("atob"); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; } export function padLeft(str, len, ch, isRight) { ch = ch || " "; len = len - str.length; for (let i = 0; i < len; i++) { str = isRight ? str + ch : ch + str; } return str; } export function padRight(str, len, ch) { return padLeft(str, len, ch, true); } export function remove(str, startIndex, count) { if (startIndex >= str.length) { throw new Error("startIndex must be less than length of string"); } if (typeof count === "number" && (startIndex + count) > str.length) { throw new Error("Index and count must refer to a location within the string."); } return str.slice(0, startIndex) + (typeof count === "number" ? str.substr(startIndex + count) : ""); } export function replace(str, search, replace) { return str.replace(new RegExp(escape(search), "g"), replace); } export function replicate(n, x) { return initialize(n, () => x); } export function getCharAtIndex(input, index) { if (index < 0 || index >= input.length) { throw new Error("Index was outside the bounds of the array."); } return input[index]; } export function split(str, splitters, count, removeEmpty) { count = typeof count === "number" ? count : null; removeEmpty = typeof removeEmpty === "number" ? removeEmpty : null; if (count < 0) { throw new Error("Count cannot be less than zero"); } if (count === 0) { return []; } if (!Array.isArray(splitters)) { if (removeEmpty === 0) { return str.split(splitters, count); } const len = arguments.length; splitters = Array(len - 1); for (let key = 1; key < len; key++) { splitters[key - 1] = arguments[key]; } } splitters = splitters.map((x) => escape(x)); splitters = splitters.length > 0 ? splitters : [" "]; let i = 0; const splits = []; const reg = new RegExp(splitters.join("|"), "g"); while (count == null || count > 1) { const m = reg.exec(str); if (m === null) { break; } if (!removeEmpty || (m.index - i) > 0) { count = count != null ? count - 1 : count; splits.push(str.substring(i, m.index)); } i = reg.lastIndex; } if (!removeEmpty || (str.length - i) > 0) { splits.push(str.substring(i)); } return splits; } export function trim(str, ...chars) { if (chars.length === 0) { return str.trim(); } const pattern = "[" + escape(chars.join("")) + "]+"; return str.replace(new RegExp("^" + pattern), "").replace(new RegExp(pattern + "$"), ""); } export function trimStart(str, ...chars) { return chars.length === 0 ? str.trimStart() : str.replace(new RegExp("^[" + escape(chars.join("")) + "]+"), ""); } export function trimEnd(str, ...chars) { return chars.length === 0 ? str.trimEnd() : str.replace(new RegExp("[" + escape(chars.join("")) + "]+$"), ""); } export function filter(pred, x) { return x.split("").filter((c) => pred(c)).join(""); } //# sourceMappingURL=String.js.map