Fork of daniellemaywood.uk/gleam — Wasm codegen work
5.5 kB
165 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 (globalThis.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 (globalThis.Deno) {
12 // If we're in Deno, use `stderr`
13 const string = `${grey}${file_line}${reset_color}\n${string_value}\n`;
14 globalThis.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 globalThis.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 +=
37 "\\u{" +
38 char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") +
39 "}";
40 } else {
41 new_str += char;
42 }
43 }
44 new_str += '"';
45 return new_str;
46}
47
48function echo$inspectDict(map) {
49 let body = "dict.from_list([";
50 let first = true;
51
52 let key_value_pairs = [];
53 map.forEach((value, key) => {
54 key_value_pairs.push([key, value]);
55 });
56 key_value_pairs.sort();
57 key_value_pairs.forEach(([key, value]) => {
58 if (!first) body = body + ", ";
59 body = body + "#(" + echo$inspect(key) + ", " + echo$inspect(value) + ")";
60 first = false;
61 });
62 return body + "])";
63}
64
65function echo$inspectCustomType(record) {
66 const props = globalThis.Object.keys(record)
67 .map((label) => {
68 const value = echo$inspect(record[label]);
69 return isNaN(parseInt(label)) ? `${label}: ${value}` : value;
70 })
71 .join(", ");
72 return props
73 ? `${record.constructor.name}(${props})`
74 : record.constructor.name;
75}
76
77function echo$inspectObject(v) {
78 const name = Object.getPrototypeOf(v)?.constructor?.name || "Object";
79 const props = [];
80 for (const k of Object.keys(v)) {
81 props.push(`${echo$inspect(k)}: ${echo$inspect(v[k])}`);
82 }
83 const body = props.length ? " " + props.join(", ") + " " : "";
84 const head = name === "Object" ? "" : name + " ";
85 return `//js(${head}{${body}})`;
86}
87
88function echo$inspect(v) {
89 const t = typeof v;
90 if (v === true) return "True";
91 if (v === false) return "False";
92 if (v === null) return "//js(null)";
93 if (v === undefined) return "Nil";
94 if (t === "string") return echo$inspectString(v);
95 if (t === "bigint" || t === "number") return v.toString();
96 if (globalThis.Array.isArray(v))
97 return `#(${v.map(echo$inspect).join(", ")})`;
98 if (v instanceof $List)
99 return `[${v.toArray().map(echo$inspect).join(", ")}]`;
100 if (v instanceof $UtfCodepoint)
101 return `//utfcodepoint(${String.fromCodePoint(v.value)})`;
102 if (v instanceof $BitArray) return echo$inspectBitArray(v);
103 if (v instanceof $CustomType) return echo$inspectCustomType(v);
104 if (echo$isDict(v)) return echo$inspectDict(v);
105 if (v instanceof Set)
106 return `//js(Set(${[...v].map(echo$inspect).join(", ")}))`;
107 if (v instanceof RegExp) return `//js(${v})`;
108 if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`;
109 if (v instanceof Function) {
110 const args = [];
111 for (const i of Array(v.length).keys())
112 args.push(String.fromCharCode(i + 97));
113 return `//fn(${args.join(", ")}) { ... }`;
114 }
115 return echo$inspectObject(v);
116}
117
118function echo$inspectBitArray(bitArray) {
119 // We take all the aligned bytes of the bit array starting from `bitOffset`
120 // up to the end of the section containing all the aligned bytes.
121 let endOfAlignedBytes =
122 bitArray.bitOffset + 8 * Math.trunc(bitArray.bitSize / 8);
123 let alignedBytes = bitArraySlice(
124 bitArray,
125 bitArray.bitOffset,
126 endOfAlignedBytes,
127 );
128
129 // Now we need to get the remaining unaligned bits at the end of the bit array.
130 // They will start after `endOfAlignedBytes` and end at `bitArray.bitSize`
131 let remainingUnalignedBits = bitArray.bitSize % 8;
132 if (remainingUnalignedBits > 0) {
133 let remainingBits = bitArraySliceToInt(
134 bitArray,
135 endOfAlignedBytes,
136 bitArray.bitSize,
137 false,
138 false,
139 );
140 let alignedBytesArray = Array.from(alignedBytes.rawBuffer);
141 let suffix = `${remainingBits}:size(${remainingUnalignedBits})`;
142 if (alignedBytesArray.length === 0) {
143 return `<<${suffix}>>`;
144 } else {
145 return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}, ${suffix}>>`;
146 }
147 } else {
148 return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}>>`;
149 }
150}
151
152function echo$isDict(value) {
153 try {
154 // We can only check if an object is a stdlib Dict if it is one of the
155 // project's dependencies.
156 // The `Dict` class is the default export of `stdlib/dict.mjs`
157 // that we import as `$stdlib$dict`.
158 return value instanceof $stdlib$dict.default;
159 } catch {
160 // If stdlib is not one of the project's dependencies then `$stdlib$dict`
161 // will not have been imported and the check will throw an exception meaning
162 // we can't check if something is actually a `Dict`.
163 return false;
164 }
165}