Packages
gleam_stdlib
0.22.2
1.0.3
1.0.2
1.0.1
1.0.0
0.71.0
0.70.0
0.69.0
0.68.1
0.68.0
0.67.1
0.67.0
0.65.0
0.64.0
0.63.2
0.63.1
0.63.0
0.62.1
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.1
0.35.0
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.0
0.30.2
0.30.1
0.30.0
0.29.2
0.29.1
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.0
0.22.3
0.22.2
0.22.1
0.22.0
0.21.0
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.18.0-rc1
0.17.1
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.1
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
0.4.0
0.4.0-rc1
0.3.1
0.3.0
0.2.0
retired
A standard library for the Gleam programming language
Current section
Files
Jump to
Current section
Files
src/gleam_stdlib.mjs
import {
BitString,
Error,
List,
Ok,
Result,
UtfCodepoint,
inspect,
stringBits,
toBitString,
} from "./gleam.mjs";
import {
CompileError as RegexCompileError,
Match as RegexMatch,
} from "./gleam/regex.mjs";
import { DecodeError } from "./gleam/dynamic.mjs";
import { Some, None } from "./gleam/option.mjs";
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) {
if (typeof string.replaceAll !== "undefined") {
return string.replaceAll(target, substitute);
}
// Fallback for older Node.js versions:
// 1. <https://stackoverflow.com/a/1144788>
// 2. <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping>
// TODO: This fallback could be remove once Node.js 14 is EOL
// aka <https://nodejs.org/en/about/releases/> on or after 2024-04-30
return string.replace(
// $& means the whole matched string
new RegExp(target.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
substitute
);
}
export function string_reverse(string) {
return [...string].reverse().join("");
}
export function string_length(string) {
if (string === "") {
return 0;
}
let iterator = graphemes_iterator(string);
if (iterator) {
let i = 0;
for (let _ of iterator) {
i++;
}
return i;
} else {
return string.match(/./gu).length;
}
}
export function graphemes(string) {
return List.fromArray(
Array.from(graphemes_iterator(string)).map(((item) => item.segment ))
);
}
function graphemes_iterator(string) {
if (Intl && Intl.Segmenter) {
return new Intl.Segmenter().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 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(Nil);
}
}
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) {
// It is checked in Gleam that:
// - The base is non-negative and that the exponent is not fractional.
// - The base is non-zero and the exponent is non-negative (otherwise
// the result will essentially be division by zero).
// It can thus be assumed that valid input is passed to the Math.pow
// function and a NaN or Infinity value will not be produced.
return Math.pow(base, exponent)
}
export function random_uniform() {
let random_uniform_result = Math.random();
// With round-to-nearest-even behavior, the ranges claimed for the functions below
// (excluding the one for Math.random() itself) aren't exact.
// If extremely large bounds are chosen (2^53 or higher),
// it's possible in extremely rare cases to calculate the usually-excluded upper bound.
// Note that as numbers in JavaScript are IEEE 754 floating point numbers
// See: <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random>
// Because of this, we just loop 'until' we get a valid result where 0.0 <= x < 1.0:
if (random_uniform_result === 1.0) {
return random_uniform();
}
return random_uniform_result;
}
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 = inspect(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 decoder_error_no_classify(expected, classify_dynamic(got));
}
function decoder_error_no_classify(expected, got) {
return new Error(
List.fromArray([new DecodeError(expected, got, List.fromArray([]))])
);
}
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) {
if (Array.isArray(data)) {
return new Ok(List.fromArray(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_no_classify("field", "nothing");
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();
}
}