Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / templates / echo.mjs
7.2 kB 250 lines
1function echo(value, message, file, line) { 2 const grey = "\u001b[90m"; 3 const reset_color = "\u001b[39m"; 4 const file_line = `${file}:${line}`; 5 const inspector = new Echo$Inspector(); 6 const string_value = inspector.inspect(value); 7 const string_message = message === undefined ? "" : " " + message; 8 9 if (globalThis.process?.stderr?.write) { 10 // If we're in Node.js, use `stderr` 11 const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`; 12 globalThis.process.stderr.write(string); 13 } else if (globalThis.Deno) { 14 // If we're in Deno, use `stderr` 15 const string = `${grey}${file_line}${reset_color}${string_message}\n${string_value}\n`; 16 globalThis.Deno.stderr.writeSync(new TextEncoder().encode(string)); 17 } else { 18 // Otherwise, use `console.log` 19 // The browser's console.log doesn't support ansi escape codes 20 const string = `${file_line}${string_message}\n${string_value}`; 21 globalThis.console.log(string); 22 } 23 24 return value; 25} 26 27class Echo$Inspector { 28 #references = new globalThis.Set(); 29 30 #isDict(value) { 31 try { 32 // We can only check if an object is a stdlib Dict if it is one of the 33 // project's dependencies. 34 // We import the public gleam/dict module, so to check if something is a 35 // dict we compare the `constructor` field on the object with that of a 36 // new dict. 37 const empty_dict = $stdlib$dict.new$(); 38 const dict_class = empty_dict.constructor; 39 return value instanceof dict_class; 40 } catch { 41 // If stdlib is not one of the project's dependencies then `$stdlib$dict` 42 // will not have been imported and the check will throw an exception meaning 43 // we can't check if something is actually a `Dict`. 44 return false; 45 } 46 } 47 48 #float(float) { 49 const string = float.toString().replace("+", ""); 50 if (string.indexOf(".") >= 0) { 51 return string; 52 } else { 53 const index = string.indexOf("e"); 54 if (index >= 0) { 55 return string.slice(0, index) + ".0" + string.slice(index); 56 } else { 57 return string + ".0"; 58 } 59 } 60 } 61 62 inspect(v) { 63 const t = typeof v; 64 if (v === true) return "True"; 65 if (v === false) return "False"; 66 if (v === null) return "//js(null)"; 67 if (v === undefined) return "Nil"; 68 if (t === "string") return this.#string(v); 69 if (t === "bigint" || globalThis.Number.isInteger(v)) return v.toString(); 70 if (t === "number") return this.#float(v); 71 if (v instanceof $UtfCodepoint) return this.#utfCodepoint(v); 72 if (v instanceof $BitArray) return this.#bit_array(v); 73 if (v instanceof globalThis.RegExp) return `//js(${v})`; 74 if (v instanceof globalThis.Date) return `//js(Date("${v.toISOString()}"))`; 75 if (v instanceof globalThis.Error) return `//js(${v.toString()})`; 76 if (v instanceof globalThis.Function) { 77 const args = []; 78 for (const i of globalThis.Array(v.length).keys()) 79 args.push(globalThis.String.fromCharCode(i + 97)); 80 return `//fn(${args.join(", ")}) { ... }`; 81 } 82 83 if (this.#references.size === this.#references.add(v).size) { 84 return "//js(circular reference)"; 85 } 86 87 let printed; 88 if (globalThis.Array.isArray(v)) { 89 printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`; 90 } else if (v instanceof $List) { 91 printed = this.#list(v); 92 } else if (v instanceof $CustomType) { 93 printed = this.#customType(v); 94 } else if (this.#isDict(v)) { 95 printed = this.#dict(v); 96 } else if (v instanceof Set) { 97 return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`; 98 } else { 99 printed = this.#object(v); 100 } 101 this.#references.delete(v); 102 return printed; 103 } 104 105 #object(v) { 106 const name = 107 globalThis.Object.getPrototypeOf(v)?.constructor?.name || "Object"; 108 const props = []; 109 for (const k of globalThis.Object.keys(v)) { 110 props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`); 111 } 112 const body = props.length ? " " + props.join(", ") + " " : ""; 113 const head = name === "Object" ? "" : name + " "; 114 return `//js(${head}{${body}})`; 115 } 116 117 #dict(map) { 118 let body = "dict.from_list(["; 119 let first = true; 120 121 let key_value_pairs = $stdlib$dict.fold(map, [], (pairs, key, value) => { 122 pairs.push([key, value]); 123 return pairs; 124 }); 125 126 key_value_pairs.sort(); 127 key_value_pairs.forEach(([key, value]) => { 128 if (!first) body = body + ", "; 129 body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")"; 130 first = false; 131 }); 132 return body + "])"; 133 } 134 135 #customType(record) { 136 const props = globalThis.Object.keys(record) 137 .map((label) => { 138 const value = this.inspect(record[label]); 139 return isNaN(parseInt(label)) ? `${label}: ${value}` : value; 140 }) 141 .join(", "); 142 return props 143 ? `${record.constructor.name}(${props})` 144 : record.constructor.name; 145 } 146 147 #list(list) { 148 if (list instanceof $Empty) { 149 return "[]"; 150 } 151 152 let char_out = 'charlist.from_string("'; 153 let list_out = "["; 154 155 let current = list; 156 while (current instanceof $NonEmpty) { 157 let element = current.head; 158 current = current.tail; 159 160 if (list_out !== "[") { 161 list_out += ", "; 162 } 163 list_out += this.inspect(element); 164 165 if (char_out) { 166 if ( 167 globalThis.Number.isInteger(element) && 168 element >= 32 && 169 element <= 126 170 ) { 171 char_out += globalThis.String.fromCharCode(element); 172 } else { 173 char_out = null; 174 } 175 } 176 } 177 178 if (char_out) { 179 return char_out + '")'; 180 } else { 181 return list_out + "]"; 182 } 183 } 184 185 #string(str) { 186 let new_str = '"'; 187 for (let i = 0; i < str.length; i++) { 188 const char = str[i]; 189 switch (char) { 190 case "\n": 191 new_str += "\\n"; 192 break; 193 case "\r": 194 new_str += "\\r"; 195 break; 196 case "\t": 197 new_str += "\\t"; 198 break; 199 case "\f": 200 new_str += "\\f"; 201 break; 202 case "\\": 203 new_str += "\\\\"; 204 break; 205 case '"': 206 new_str += '\\"'; 207 break; 208 default: 209 if (char < " " || (char > "~" && char < "\u{00A0}")) { 210 new_str += 211 "\\u{" + 212 char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + 213 "}"; 214 } else { 215 new_str += char; 216 } 217 } 218 } 219 new_str += '"'; 220 return new_str; 221 } 222 223 #utfCodepoint(codepoint) { 224 return `//utfcodepoint(${globalThis.String.fromCodePoint(codepoint.value)})`; 225 } 226 227 #bit_array(bits) { 228 if (bits.bitSize === 0) { 229 return "<<>>"; 230 } 231 232 let acc = "<<"; 233 234 for (let i = 0; i < bits.byteSize - 1; i++) { 235 acc += bits.byteAt(i).toString(); 236 acc += ", "; 237 } 238 239 if (bits.byteSize * 8 === bits.bitSize) { 240 acc += bits.byteAt(bits.byteSize - 1).toString(); 241 } else { 242 const trailingBitsCount = bits.bitSize % 8; 243 acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount); 244 acc += `:size(${trailingBitsCount})`; 245 } 246 247 acc += ">>"; 248 return acc; 249 } 250}