UNPKG

functionalscript

Version:

FunctionalScript is a purely functional subset of JavaScript

99 lines (98 loc) 3.26 kB
import { codePointListToString, stringToCodePointList } from "../text/utf16/module.f.js"; import { isArray2 } from "../types/array/module.f.js"; import { map, toArray, repeat as listRepeat } from "../types/list/module.f.js"; // Internals: const { fromEntries, values } = Object; const { fromCodePoint } = String; /** * Two 24 bit numbers can be fit into one JS number (53 bit). */ const offset = 24; const mask = (1 << offset) - 1; const isValid = (r) => r >= 0 && r <= mask; export const max = codePointListToString([0x10FFFF]); export const rangeEncode = (a, b) => { if (!isValid(a) || !isValid(b) || a > b) { throw `Invalid range ${a} ${b}.`; } return (a << offset) | b; }; export const oneEncode = (a) => rangeEncode(a, a); export const rangeDecode = (r) => [r >> offset, r & mask]; const mapOneEncode = map(oneEncode); export const toSequence = (s) => toArray(mapOneEncode(stringToCodePointList(s))); export const str = (s) => { const x = toSequence(s); return x.length === 1 ? x[0] : x; }; const mapEntry = map((v) => [fromCodePoint(v), oneEncode(v)]); export const set = (s) => fromEntries(toArray(mapEntry(stringToCodePointList(s)))); export const range = (ab) => { const a = toArray(stringToCodePointList(ab)); if (!isArray2(a)) { throw `Invalid range ${ab}.`; } return rangeEncode(...a); }; export const rangeToId = (r) => { const ab = rangeDecode(r); const [a, b] = ab; const cp = a === b ? [a] : ab; return fromCodePoint(...cp); }; const rangeToEntry = (r) => [rangeToId(r), r]; const toVariantRangeSet = (r) => fromEntries(r.map(rangeToEntry)); const removeOne = (list, ab) => { const [a, b] = rangeDecode(ab); let result = []; for (const ab0 of list) { const [a0, b0] = rangeDecode(ab0); if (a0 < a) { // [a0 // ]a result = [...result, rangeEncode(a0, Math.min(b0, a - 1))]; } if (b < b0) { // b0] // b[ result = [...result, rangeEncode(Math.max(b + 1, a0), b0)]; } } return result; }; export const remove = (range, removeSet) => { let result = [range]; for (const r of values(removeSet)) { result = removeOne(result, r); } return toVariantRangeSet(result); }; export const none = []; export const option = (some) => ({ none, some, }); /** * Repeat zero or more times. * * https://english.stackexchange.com/questions/506480/single-word-quantifiers-for-zero-or-more-like-cardinalities * - zero or more - any, 0Plus * - one or more - several, 1Plus * * Also see: https://arbs.nzcer.org.nz/types-numbers */ export const repeat0Plus = (some) => { const r = () => option([some, r]); return r; }; /** * Repeat one or more times. */ export const repeat1Plus = (some) => [some, repeat0Plus(some)]; export const join1Plus = (some, separator) => [some, repeat0Plus([separator, some])]; export const join0Plus = (some, separator) => option(join1Plus(some, separator)); export const repeat = (n) => (some) => toArray(listRepeat(some)(n)); export const isEmpty = (rule) => { const d = typeof rule === 'function' ? rule() : rule; return d === '' || (d instanceof Array && d.length === 0); };