Skip to content

Commit 02b099b

Browse files
committed
Update inspector
1 parent d6ef6c5 commit 02b099b

File tree

1 file changed

+145
-155
lines changed

1 file changed

+145
-155
lines changed

compiler-core/templates/echo.mjs

Lines changed: 145 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ function echo(value, file, line) {
22
const grey = "\u001b[90m";
33
const reset_color = "\u001b[39m";
44
const file_line = `${file}:${line}`;
5-
const string_value = echo$inspect(value);
5+
const string_value = new Echo$Inspector.inspect(value);
66

77
if (globalThis.process?.stderr?.write) {
88
// If we're in Node.js, use `stderr`
@@ -22,184 +22,174 @@ function echo(value, file, line) {
2222
return value;
2323
}
2424

25-
function 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;
25+
class Echo$Inspector {
26+
#references = new Set();
27+
28+
inspect(v) {
29+
const t = typeof v;
30+
if (v === true) return "True";
31+
if (v === false) return "False";
32+
if (v === null) return "//js(null)";
33+
if (v === undefined) return "Nil";
34+
if (t === "string") return this.#string(v);
35+
if (t === "bigint" || Number.isInteger(v)) return v.toString();
36+
if (t === "number") return float_to_string(v);
37+
if (v instanceof UtfCodepoint) return this.#utfCodepoint(v);
38+
if (v instanceof BitArray) return this.#bit_array(v);
39+
if (v instanceof RegExp) return `//js(${v})`;
40+
if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`;
41+
if (v instanceof globalThis.Error) return `//js(${v.toString()})`;
42+
if (v instanceof Function) {
43+
const args = [];
44+
for (const i of Array(v.length).keys())
45+
args.push(String.fromCharCode(i + 97));
46+
return `//fn(${args.join(", ")}) { ... }`;
4247
}
43-
}
44-
new_str += '"';
45-
return new_str;
46-
}
47-
48-
function 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-
}
6448

65-
function 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-
}
49+
if (this.#references.size === this.#references.add(v).size) {
50+
return "//js(circular reference)";
51+
}
7652

77-
function 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])}`);
53+
if (Array.isArray(v))
54+
return `#(${v.map((v) => this.inspect(v)).join(", ")})`;
55+
if (v instanceof List) return this.#list(v);
56+
if (v instanceof CustomType) return this.#customType(v);
57+
if (v instanceof Dict) return this.#dict(v);
58+
if (v instanceof Set)
59+
return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`;
60+
return this.#object(v);
8261
}
83-
const body = props.length ? " " + props.join(", ") + " " : "";
84-
const head = name === "Object" ? "" : name + " ";
85-
return `//js(${head}{${body}})`;
86-
}
8762

88-
function 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 (v instanceof $UtfCodepoint)
97-
return `//utfcodepoint(${String.fromCodePoint(v.value)})`;
98-
if (v instanceof $BitArray) return echo$inspectBitArray(v);
99-
if (v instanceof RegExp) return `//js(${v})`;
100-
if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`;
101-
if (v instanceof globalThis.Error) return `//js(${v.toString()})`;
102-
if (v instanceof Function) {
103-
const args = [];
104-
for (const i of Array(v.length).keys())
105-
args.push(String.fromCharCode(i + 97));
106-
return `//fn(${args.join(", ")}) { ... }`;
63+
#object(v) {
64+
const name = Object.getPrototypeOf(v)?.constructor?.name || "Object";
65+
const props = [];
66+
for (const k of Object.keys(v)) {
67+
props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`);
68+
}
69+
const body = props.length ? " " + props.join(", ") + " " : "";
70+
const head = name === "Object" ? "" : name + " ";
71+
return `//js(${head}{${body}})`;
10772
}
10873

109-
try {
110-
if (globalThis.Array.isArray(v))
111-
return `#(${v.map(echo$inspect).join(", ")})`;
112-
if (v instanceof $List) return echo$inspectList(v);
113-
if (v instanceof $CustomType) return echo$inspectCustomType(v);
114-
if (echo$isDict(v)) return echo$inspectDict(v);
115-
if (v instanceof Set)
116-
return `//js(Set(${[...v].map(echo$inspect).join(", ")}))`;
117-
return echo$inspectObject(v);
118-
} catch (e) {
119-
if (e instanceof RangeError) return "//js(circular)";
120-
throw e;
74+
#dict(map) {
75+
let body = "dict.from_list([";
76+
let first = true;
77+
map.forEach((value, key) => {
78+
if (!first) body = body + ", ";
79+
body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")";
80+
first = false;
81+
});
82+
return body + "])";
12183
}
122-
}
12384

124-
export function echo$inspectList(list) {
125-
if (list instanceof $Empty) {
126-
return "[]";
85+
#customType(record) {
86+
const props = Object.keys(record)
87+
.map((label) => {
88+
const value = this.inspect(record[label]);
89+
return isNaN(parseInt(label)) ? `${label}: ${value}` : value;
90+
})
91+
.join(", ");
92+
return props
93+
? `${record.constructor.name}(${props})`
94+
: record.constructor.name;
12795
}
12896

129-
let char_out = 'charlist.from_string("';
130-
let list_out = "[";
97+
#list(list) {
98+
if (list instanceof Empty) {
99+
return "[]";
100+
}
101+
102+
let char_out = 'charlist.from_string("';
103+
let list_out = "[";
131104

132-
let current = list;
133-
while (current instanceof $NonEmpty) {
134-
let element = current.head;
135-
current = current.tail;
105+
let current = list;
106+
while (current instanceof NonEmpty) {
107+
let element = current.head;
108+
current = current.tail;
136109

137-
if (list_out !== "[") {
138-
list_out += ", ";
110+
if (list_out !== "[") {
111+
list_out += ", ";
112+
}
113+
list_out += this.inspect(element);
114+
115+
if (char_out) {
116+
if (Number.isInteger(element) && element >= 32 && element <= 126) {
117+
char_out += String.fromCharCode(element);
118+
} else {
119+
char_out = null;
120+
}
121+
}
139122
}
140-
list_out += echo$inspect(element);
141123

142124
if (char_out) {
143-
if (Number.isInteger(element) && element >= 32 && element <= 126) {
144-
char_out += String.fromCharCode(element);
145-
} else {
146-
char_out = null;
125+
return char_out + '")';
126+
} else {
127+
return list_out + "]";
128+
}
129+
}
130+
131+
#string(str) {
132+
let new_str = '"';
133+
for (let i = 0; i < str.length; i++) {
134+
const char = str[i];
135+
switch (char) {
136+
case "\n":
137+
new_str += "\\n";
138+
break;
139+
case "\r":
140+
new_str += "\\r";
141+
break;
142+
case "\t":
143+
new_str += "\\t";
144+
break;
145+
case "\f":
146+
new_str += "\\f";
147+
break;
148+
case "\\":
149+
new_str += "\\\\";
150+
break;
151+
case '"':
152+
new_str += '\\"';
153+
break;
154+
default:
155+
if (char < " " || (char > "~" && char < "\u{00A0}")) {
156+
new_str +=
157+
"\\u{" +
158+
char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") +
159+
"}";
160+
} else {
161+
new_str += char;
162+
}
147163
}
148164
}
165+
new_str += '"';
166+
return new_str;
149167
}
150168

151-
if (char_out) {
152-
return char_out + '")';
153-
} else {
154-
return list_out + "]";
169+
#utfCodepoint(codepoint) {
170+
return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`;
155171
}
156-
}
157172

158-
function echo$inspectBitArray(bitArray) {
159-
// We take all the aligned bytes of the bit array starting from `bitOffset`
160-
// up to the end of the section containing all the aligned bytes.
161-
let endOfAlignedBytes =
162-
bitArray.bitOffset + 8 * Math.trunc(bitArray.bitSize / 8);
163-
let alignedBytes = bitArraySlice(
164-
bitArray,
165-
bitArray.bitOffset,
166-
endOfAlignedBytes,
167-
);
168-
169-
// Now we need to get the remaining unaligned bits at the end of the bit array.
170-
// They will start after `endOfAlignedBytes` and end at `bitArray.bitSize`
171-
let remainingUnalignedBits = bitArray.bitSize % 8;
172-
if (remainingUnalignedBits > 0) {
173-
let remainingBits = bitArraySliceToInt(
174-
bitArray,
175-
endOfAlignedBytes,
176-
bitArray.bitSize,
177-
false,
178-
false,
179-
);
180-
let alignedBytesArray = Array.from(alignedBytes.rawBuffer);
181-
let suffix = `${remainingBits}:size(${remainingUnalignedBits})`;
182-
if (alignedBytesArray.length === 0) {
183-
return `<<${suffix}>>`;
173+
#bit_array(bits) {
174+
let acc = "<<";
175+
if (bits.bitSize === 0) {
176+
return acc;
177+
}
178+
179+
for (let i = 0; i < bits.byteSize - 1; i++) {
180+
acc += bits.byteAt(i).toString();
181+
acc += ", ";
182+
}
183+
184+
if (bits.byteSize * 8 === bits.bitSize) {
185+
acc += bits.byteAt(bits.byteSize - 1).toString();
184186
} else {
185-
return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}, ${suffix}>>`;
187+
const trailingBitsCount = bits.bitSize % 8;
188+
acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount);
189+
acc += `:size(${trailingBitsCount})`;
186190
}
187-
} else {
188-
return `<<${Array.from(alignedBytes.rawBuffer).join(", ")}>>`;
189-
}
190-
}
191191

192-
function echo$isDict(value) {
193-
try {
194-
// We can only check if an object is a stdlib Dict if it is one of the
195-
// project's dependencies.
196-
// The `Dict` class is the default export of `stdlib/dict.mjs`
197-
// that we import as `$stdlib$dict`.
198-
return value instanceof $stdlib$dict.default;
199-
} catch {
200-
// If stdlib is not one of the project's dependencies then `$stdlib$dict`
201-
// will not have been imported and the check will throw an exception meaning
202-
// we can't check if something is actually a `Dict`.
203-
return false;
192+
acc += ">>";
193+
return acc;
204194
}
205195
}

0 commit comments

Comments
 (0)