Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.4 kB
144 lines
1function echo(value, file, line) {
2 const grey = "\u001b[90m";
3 const reset_color = "\u001b[39m";
4 const file_line = `${file}:${line}`;
5 const string_value = echo$inspect(value);
6
7 if (typeof process === "object" && process.stderr?.write) {
8 // If we're in Node.js, use `stderr`
9 const string = `${grey}${file_line}${reset_color}\n${string_value}\n`;
10 process.stderr.write(string);
11 } else if (typeof Deno === "object") {
12 // If we're in Deno, use `stderr`
13 const string = `${grey}${file_line}${reset_color}\n${string_value}\n`;
14 Deno.stderr.writeSync(new TextEncoder().encode(string));
15 } else {
16 // Otherwise, use `console.log`
17 // The browser's console.log doesn't support ansi escape codes
18 const string = `${file_line}\n${string_value}`;
19 console.log(string);
20 }
21
22 return value;
23}
24
25function echo$inspectString(str) {
26 let new_str = '"';
27 for (let i = 0; i < str.length; i++) {
28 let char = str[i];
29 if (char == "\n") new_str += "\\n";
30 else if (char == "\r") new_str += "\\r";
31 else if (char == "\t") new_str += "\\t";
32 else if (char == "\f") new_str += "\\f";
33 else if (char == "\\") new_str += "\\\\";
34 else if (char == '"') new_str += '\\"';
35 else if (char < " " || (char > "~" && char < "\u{00A0}")) {
36 new_str += "\\u{" + char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + "}";
37 } else {
38 new_str += char;
39 }
40 }
41 new_str += '"';
42 return new_str;
43}
44
45function echo$inspectDict(map) {
46 let body = "dict.from_list([";
47 let first = true;
48
49 let key_value_pairs = [];
50 map.forEach((value, key) => {
51 key_value_pairs.push([key, value]);
52 });
53 key_value_pairs.sort();
54 key_value_pairs.forEach(([key, value]) => {
55 if (!first) body = body + ", ";
56 body = body + "#(" + echo$inspect(key) + ", " + echo$inspect(value) + ")";
57 first = false;
58 });
59 return body + "])";
60}
61
62function echo$inspectCustomType(record) {
63 const props = Object.keys(record)
64 .map((label) => {
65 const value = echo$inspect(record[label]);
66 return isNaN(parseInt(label)) ? `${label}: ${value}` : value;
67 })
68 .join(", ");
69 return props ? `${record.constructor.name}(${props})` : record.constructor.name;
70}
71
72function echo$inspectObject(v) {
73 const name = Object.getPrototypeOf(v)?.constructor?.name || "Object";
74 const props = [];
75 for (const k of Object.keys(v)) {
76 props.push(`${echo$inspect(k)}: ${echo$inspect(v[k])}`);
77 }
78 const body = props.length ? " " + props.join(", ") + " " : "";
79 const head = name === "Object" ? "" : name + " ";
80 return `//js(${head}{${body}})`;
81}
82
83function echo$inspect(v) {
84 const t = typeof v;
85 if (v === true) return "True";
86 if (v === false) return "False";
87 if (v === null) return "//js(null)";
88 if (v === undefined) return "Nil";
89 if (t === "string") return echo$inspectString(v);
90 if (t === "bigint" || t === "number") return v.toString();
91 if (Array.isArray(v)) return `#(${v.map(echo$inspect).join(", ")})`;
92 if (v instanceof $List) return `[${v.toArray().map(echo$inspect).join(", ")}]`;
93 if (v instanceof $UtfCodepoint) return `//utfcodepoint(${String.fromCodePoint(v.value)})`;
94 if (v instanceof $BitArray) return echo$inspectBitArray(v);
95 if (v instanceof $CustomType) return echo$inspectCustomType(v);
96 if (echo$isDict(v)) return echo$inspectDict(v);
97 if (v instanceof Set) return `//js(Set(${[...v].map(echo$inspect).join(", ")}))`;
98 if (v instanceof RegExp) return `//js(${v})`;
99 if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`;
100 if (v instanceof Function) {
101 const args = [];
102 for (const i of Array(v.length).keys()) args.push(String.fromCharCode(i + 97));
103 return `//fn(${args.join(", ")}) { ... }`;
104 }
105 return echo$inspectObject(v);
106}
107
108function echo$inspectBitArray(bitArray) {
109 // We take all the aligned bytes of the bit array starting from `bitOffset`
110 // up to the end of the section containing all the aligned bytes.
111 let endOfAlignedBytes = bitArray.bitOffset + 8 * Math.trunc(bitArray.bitSize / 8);
112 let alignedBytes = bitArraySlice(bitArray, bitArray.bitOffset, endOfAlignedBytes);
113
114 // Now we need to get the remaining unaligned bits at the end of the bit array.
115 // They will start after `endOfAlignedBytes` and end at `bitArray.bitSize`
116 let remainingUnalignedBits = bitArray.bitSize % 8;
117 if (remainingUnalignedBits > 0) {
118 let remainingBits = bitArraySliceToInt(bitArray, endOfAlignedBytes, bitArray.bitSize, false, false);
119 let alignedBytesArray = Array.from(alignedBytes.rawBuffer);
120 let suffix = `${remainingBits}:size(${remainingUnalignedBits})`;
121 if (alignedBytesArray.length === 0) {
122 return `<<${suffix}>>`;
123 } else {
124 return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}, ${suffix}>>`;
125 }
126 } else {
127 return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}>>`;
128 }
129}
130
131function echo$isDict(value) {
132 try {
133 // We can only check if an object is a stdlib Dict if it is one of the
134 // project's dependencies.
135 // The `Dict` class is the default export of `stdlib/dict.mjs`
136 // that we import as `$stdlib$dict`.
137 return value instanceof $stdlib$dict.default;
138 } catch {
139 // If stdlib is not one of the project's dependencies then `$stdlib$dict`
140 // will not have been imported and the check will throw an exception meaning
141 // we can't check if something is actually a `Dict`.
142 return false;
143 }
144}