aboutsummaryrefslogtreecommitdiff
path: root/src/gleam_stdlib.mjs
diff options
context:
space:
mode:
authorLouis Pilfold <louis@lpil.uk>2021-12-19 13:57:26 +0000
committerLouis Pilfold <louis@lpil.uk>2021-12-19 13:57:26 +0000
commitb5535d14f17fb5bd5f70598bc92e238182477ae4 (patch)
tree734f0182edba0c02945dc47b0457b0553f28213b /src/gleam_stdlib.mjs
parent353bc1c6b8165739ca8c04d4e1b0e9e06b49a5bc (diff)
downloadgleam_stdlib-b5535d14f17fb5bd5f70598bc92e238182477ae4.tar.gz
gleam_stdlib-b5535d14f17fb5bd5f70598bc92e238182477ae4.zip
Flip/flop
Diffstat (limited to 'src/gleam_stdlib.mjs')
-rw-r--r--src/gleam_stdlib.mjs569
1 files changed, 569 insertions, 0 deletions
diff --git a/src/gleam_stdlib.mjs b/src/gleam_stdlib.mjs
new file mode 100644
index 0000000..96fa4f1
--- /dev/null
+++ b/src/gleam_stdlib.mjs
@@ -0,0 +1,569 @@
+import {
+ BitString,
+ Error,
+ List,
+ Ok,
+ Result,
+ UtfCodepoint,
+ inspect,
+ stringBits,
+ toBitString,
+} from "./gleam.js";
+import {
+ CompileError as RegexCompileError,
+ Match as RegexMatch,
+} from "./gleam/regex.js";
+import { DecodeError } from "./gleam/dynamic.js";
+import { Some, None } from "./gleam/option.js";
+
+const HASHCODE_CACHE = new WeakMap();
+
+const Nil = undefined;
+
+export function identity(x) {
+ return x;
+}
+
+export function parse_int(value) {
+ if (/^[-+]?(\d+)$/.test(value)) {
+ return new Ok(parseInt(value));
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function parse_float(value) {
+ if (/^[-+]?(\d+)\.(\d+)$/.test(value)) {
+ return new Ok(parseFloat(value));
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function to_string(term) {
+ return term.toString();
+}
+
+export function float_to_string(float) {
+ let string = float.toString();
+ if (string.indexOf(".") >= 0) {
+ return string;
+ } else {
+ return string + ".0";
+ }
+}
+
+export function int_to_base_string(int, base) {
+ return int.toString(base).toUpperCase();
+}
+
+export function string_replace(string, target, substitute) {
+ return string.replaceAll(target, substitute);
+}
+
+export function string_reverse(string) {
+ return [...string].reverse().join("");
+}
+
+export function string_length(string) {
+ let iterator = graphemes_iterator(string);
+ if (iterator) {
+ let i = 0;
+ for (let _ of iterator) {
+ i++;
+ }
+ return i;
+ } else {
+ return string.match(/./gu).length;
+ }
+}
+
+function graphemes_iterator(string) {
+ if (Intl && Intl.Segmenter) {
+ return new Intl.Segmenter("en-gb").segment(string)[Symbol.iterator]();
+ }
+}
+
+export function pop_grapheme(string) {
+ let first;
+ let iterator = graphemes_iterator(string);
+ if (iterator) {
+ first = iterator.next().value?.segment;
+ } else {
+ first = string.match(/./u)?.[0];
+ }
+ if (first) {
+ return new Ok([first, string.slice(first.length)]);
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function lowercase(string) {
+ return string.toLowerCase();
+}
+
+export function uppercase(string) {
+ return string.toUpperCase();
+}
+
+export function less_than(a, b) {
+ return a < b;
+}
+
+export function add(a, b) {
+ return a + b;
+}
+
+export function equal(a, b) {
+ return a === b;
+}
+
+export function split(xs, pattern) {
+ return List.fromArray(xs.split(pattern));
+}
+
+export function join(xs) {
+ return xs.toArray().join("");
+}
+
+export function length(data) {
+ return data.length;
+}
+
+export function slice_string(string, from, length) {
+ return string.slice(from, from + length);
+}
+
+export function crop_string(string, substring) {
+ return string.substring(string.indexOf(substring));
+}
+
+export function index_of(haystack, needle) {
+ return haystack.indexOf(needle) | 0;
+}
+
+export function starts_with(haystack, needle) {
+ return haystack.startsWith(needle);
+}
+
+export function ends_with(haystack, needle) {
+ return haystack.endsWith(needle);
+}
+
+export function split_once(haystack, needle) {
+ let index = haystack.indexOf(needle);
+ if (index >= 0) {
+ let before = haystack.slice(0, index);
+ let after = haystack.slice(index + needle.length);
+ return new Ok([before, after]);
+ } else {
+ return new Error(Nil);
+ }
+}
+
+export function trim(string) {
+ return string.trim();
+}
+
+export function trim_left(string) {
+ return string.trimLeft();
+}
+
+export function trim_right(string) {
+ return string.trimRight();
+}
+
+export function bit_string_from_string(string) {
+ return new toBitString([stringBits(string)]);
+}
+
+export function bit_string_concat(bit_strings) {
+ return toBitString(bit_strings.toArray().map((b) => b.buffer));
+}
+
+export function log(term) {
+ console.log(term);
+}
+
+export function debug(term) {
+ console.log(inspect(term));
+}
+
+export function crash(message) {
+ throw new globalThis.Error(message);
+}
+
+export function bit_string_to_string(bit_string) {
+ try {
+ let decoder = new TextDecoder("utf-8", { fatal: true });
+ return new Ok(decoder.decode(bit_string.buffer));
+ } catch (_error) {
+ return new Error(undefined);
+ }
+}
+
+export function print(string) {
+ if (typeof process === "object") {
+ process.stdout.write(string); // We can write without a trailing newline
+ } else {
+ console.log(string); // We're in a browser. Newlines are mandated
+ }
+}
+
+export function ceiling(float) {
+ return Math.ceil(float);
+}
+
+export function floor(float) {
+ return Math.floor(float);
+}
+
+export function round(float) {
+ return Math.round(float);
+}
+
+export function truncate(float) {
+ return Math.trunc(float);
+}
+
+export function power(base, exponent) {
+ return Math.pow(base, exponent);
+}
+
+export function bit_string_slice(bits, position, length) {
+ let start = Math.min(position, position + length);
+ let end = Math.max(position, position + length);
+ if (start < 0 || end > bits.length) return new Error(Nil);
+ let buffer = new Uint8Array(bits.buffer.buffer, start, Math.abs(length));
+ return new Ok(new BitString(buffer));
+}
+
+export function codepoint(int) {
+ return new UtfCodepoint(int);
+}
+
+export function regex_check(regex, string) {
+ return regex.test(string);
+}
+
+export function compile_regex(pattern, options) {
+ try {
+ let flags = "gu";
+ if (options.case_insensitive) flags += "i";
+ if (options.multi_line) flags += "m";
+ return new Ok(new RegExp(pattern, flags));
+ } catch (error) {
+ let number = (error.columnNumber || 0) | 0;
+ return new Error(new RegexCompileError(error.message, number));
+ }
+}
+
+export function regex_scan(regex, string) {
+ let matches = Array.from(string.matchAll(regex)).map((match) => {
+ let content = match.shift();
+ let submatches = match.map((x) => (x ? new Some(x) : new None()));
+ return new RegexMatch(content, List.fromArray(submatches));
+ });
+ return List.fromArray(matches);
+}
+
+class Map {
+ static #hashcode_cache = new WeakMap();
+
+ static hash(value) {
+ let existing = this.#hashcode_cache.get(value);
+ if (existing) {
+ return existing;
+ } else if (value instanceof Object) {
+ let hashcode = JSON.stringify(value);
+ HASHCODE_CACHE.set(value, hashcode);
+ return hashcode;
+ } else {
+ return value.toString();
+ }
+ }
+
+ constructor() {
+ this.entries = new globalThis.Map();
+ }
+
+ get size() {
+ return this.entries.size;
+ }
+
+ inspect() {
+ let entries = [...this.entries.values()]
+ .map((pair) => inspect(pair))
+ .join(", ");
+ return `map.from_list([${entries}])`;
+ }
+
+ copy() {
+ let map = new Map();
+ map.entries = new globalThis.Map(this.entries);
+ return map;
+ }
+
+ toList() {
+ return List.fromArray([...this.entries.values()]);
+ }
+
+ insert(k, v) {
+ let map = this.copy();
+ map.entries.set(Map.hash(k), [k, v]);
+ return map;
+ }
+
+ delete(k) {
+ let map = this.copy();
+ map.entries.delete(Map.hash(k));
+ return map;
+ }
+
+ get(key) {
+ let code = Map.hash(key);
+ if (this.entries.has(code)) {
+ return new Ok(this.entries.get(code)[1]);
+ } else {
+ return new Error(Nil);
+ }
+ }
+}
+
+export function new_map() {
+ return new Map();
+}
+
+export function map_size(map) {
+ return map.size;
+}
+
+export function map_to_list(map) {
+ return map.toList();
+}
+
+export function map_remove(k, map) {
+ return map.delete(k);
+}
+
+export function map_get(map, key) {
+ return map.get(key);
+}
+
+export function map_insert(key, value, map) {
+ return map.insert(key, value);
+}
+
+function unsafe_percent_decode(string) {
+ return decodeURIComponent((string || "").replace("+", " "));
+}
+
+export function percent_decode(string) {
+ try {
+ return new Ok(unsafe_percent_decode(string));
+ } catch (error) {
+ return new Error(Nil);
+ }
+}
+
+export function percent_encode(string) {
+ return encodeURIComponent(string);
+}
+
+export function parse_query(query) {
+ try {
+ let pairs = [];
+ for (let section of query.split("&")) {
+ let [key, value] = section.split("=");
+ if (!key) continue;
+ pairs.push([unsafe_percent_decode(key), unsafe_percent_decode(value)]);
+ }
+ return new Ok(List.fromArray(pairs));
+ } catch (error) {
+ return new Error(Nil);
+ }
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+export function encode64(bit_string) {
+ let aBytes = bit_string.buffer;
+ let nMod3 = 2,
+ sB64Enc = "";
+
+ for (let nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
+ nMod3 = nIdx % 3;
+ if (nIdx > 0 && ((nIdx * 4) / 3) % 76 === 0) {
+ sB64Enc += "\r\n";
+ }
+ nUint24 |= aBytes[nIdx] << ((16 >>> nMod3) & 24);
+ if (nMod3 === 2 || aBytes.length - nIdx === 1) {
+ sB64Enc += String.fromCharCode(
+ uint6ToB64((nUint24 >>> 18) & 63),
+ uint6ToB64((nUint24 >>> 12) & 63),
+ uint6ToB64((nUint24 >>> 6) & 63),
+ uint6ToB64(nUint24 & 63)
+ );
+ nUint24 = 0;
+ }
+ }
+
+ return (
+ sB64Enc.substr(0, sB64Enc.length - 2 + nMod3) +
+ (nMod3 === 2 ? "" : nMod3 === 1 ? "=" : "==")
+ );
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+function uint6ToB64(nUint6) {
+ return nUint6 < 26
+ ? nUint6 + 65
+ : nUint6 < 52
+ ? nUint6 + 71
+ : nUint6 < 62
+ ? nUint6 - 4
+ : nUint6 === 62
+ ? 43
+ : nUint6 === 63
+ ? 47
+ : 65;
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+function b64ToUint6(nChr) {
+ return nChr > 64 && nChr < 91
+ ? nChr - 65
+ : nChr > 96 && nChr < 123
+ ? nChr - 71
+ : nChr > 47 && nChr < 58
+ ? nChr + 4
+ : nChr === 43
+ ? 62
+ : nChr === 47
+ ? 63
+ : 0;
+}
+
+// From https://developer.mozilla.org/en-US/docs/Glossary/Base64#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8
+export function decode64(sBase64) {
+ if (sBase64.match(/[^A-Za-z0-9\+\/=]/g)) return new Error(Nil);
+ let sB64Enc = sBase64.replace(/=/g, "");
+ let nInLen = sB64Enc.length;
+ let nOutLen = (nInLen * 3 + 1) >> 2;
+ let taBytes = new Uint8Array(nOutLen);
+
+ for (
+ let nMod3, nMod4, nUint24 = 0, nOutIdx = 0, nInIdx = 0;
+ nInIdx < nInLen;
+ nInIdx++
+ ) {
+ nMod4 = nInIdx & 3;
+ nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (6 * (3 - nMod4));
+ if (nMod4 === 3 || nInLen - nInIdx === 1) {
+ for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3++, nOutIdx++) {
+ taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
+ }
+ nUint24 = 0;
+ }
+ }
+
+ return new Ok(new BitString(taBytes));
+}
+
+export function classify_dynamic(data) {
+ if (typeof data === "string") {
+ return "String";
+ } else if (Result.isResult(data)) {
+ return "Result";
+ } else if (List.isList(data)) {
+ return "List";
+ } else if (Number.isInteger(data)) {
+ return "Int";
+ } else if (Array.isArray(data)) {
+ return `Tuple of ${data.length} elements`;
+ } else if (BitString.isBitString(data)) {
+ return "BitString";
+ } else if (data instanceof Map) {
+ return "Map";
+ } else if (typeof data === "number") {
+ return "Float";
+ } else {
+ let type = typeof data;
+ return type.charAt(0).toUpperCase() + type.slice(1);
+ }
+}
+
+function decoder_error(expected, got) {
+ return new Error(new DecodeError(expected, classify_dynamic(got)));
+}
+
+export function decode_string(data) {
+ return typeof data === "string"
+ ? new Ok(data)
+ : decoder_error("String", data);
+}
+
+export function decode_int(data) {
+ return Number.isInteger(data) ? new Ok(data) : decoder_error("Int", data);
+}
+
+export function decode_float(data) {
+ return typeof data === "number" ? new Ok(data) : decoder_error("Float", data);
+}
+
+export function decode_bool(data) {
+ return typeof data === "boolean" ? new Ok(data) : decoder_error("Bool", data);
+}
+
+export function decode_bit_string(data) {
+ return BitString.isBitString(data)
+ ? new Ok(data)
+ : decoder_error("BitString", data);
+}
+
+export function decode_tuple(data) {
+ return Array.isArray(data) ? new Ok(data) : decoder_error("Tuple", data);
+}
+
+export function tuple_get(data, index) {
+ return index >= 0 && data.length > index
+ ? new Ok(data[index])
+ : new Error(Nil);
+}
+
+export function decode_list(data) {
+ return List.isList(data) ? new Ok(data) : decoder_error("List", data);
+}
+
+export function decode_result(data) {
+ return Result.isResult(data) ? new Ok(data) : decoder_error("Result", data);
+}
+
+export function decode_map(data) {
+ return data instanceof Map ? new Ok(data) : decoder_error("Map", data);
+}
+
+export function decode_option(data, decoder) {
+ if (data === null || data === undefined || data instanceof None)
+ return new Ok(new None());
+ if (data instanceof Some) data = data[0];
+ let result = decoder(data);
+ if (result.isOk()) {
+ return new Ok(new Some(result[0]));
+ } else {
+ return result;
+ }
+}
+
+export function decode_field(value, name) {
+ let error = () => decoder_error(`Value with field ${inspect(name)}`, value);
+ if (value instanceof Map) {
+ let entry = value.get(name);
+ return entry.isOk() ? entry : error();
+ }
+ try {
+ return name in value ? new Ok(value[name]) : error();
+ } catch {
+ return error();
+ }
+}