58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { Closure, Value, Env, EnvFrame } from 'src/lang/eval/value';
|
|
import { exprToString, productPatternToString } from './Expr';
|
|
|
|
export function Val(prop: { value: Value }) {
|
|
return (
|
|
<code>{ valueToString(prop.value) }</code>
|
|
);
|
|
}
|
|
|
|
export function valueToString(val: Value): string {
|
|
switch (val.tag) {
|
|
case "number": return val.value.toString();
|
|
case "string": return `"${val.value}"`;
|
|
case "tag": return `#${val.tag_name}`;
|
|
case "tagged": return `#${val.tag_name} ${valueToString(val.value)}`;
|
|
case "tuple": return `(${val.values.map(valueToString).join(", ")})`;
|
|
case "record": {
|
|
const entries = Array.from(val.fields.entries())
|
|
.map(([k, v]) => `${k} = ${valueToString(v)}`)
|
|
.join(", ");
|
|
return `{ ${entries} }`;
|
|
}
|
|
case "closure": return closureToString(val.closure);
|
|
}
|
|
}
|
|
|
|
function closureToString(c: Closure): string {
|
|
const params = c.parameters.map(productPatternToString).join(", ");
|
|
const envStr = envToString(c.env);
|
|
// We represent the closure as the code + a summary of its captured scope
|
|
return `fn { ${params} . ${exprToString(c.body)} } [captured: ${envStr}]`;
|
|
}
|
|
|
|
function envToString(env: Env): string {
|
|
if (env.tag === "nil") return "∅";
|
|
|
|
const frames: string[] = [];
|
|
let current: Env = env;
|
|
|
|
while (current.tag === "frame") {
|
|
frames.push(frameToString(current.frame));
|
|
current = current.parent;
|
|
}
|
|
|
|
// Shows stack from inner-most to outer-most
|
|
return frames.join(" ⮕ ");
|
|
}
|
|
|
|
function frameToString(frame: EnvFrame): string {
|
|
const entries = Array.from(frame.entries());
|
|
if (entries.length === 0) return "{}";
|
|
|
|
const formattedEntries = entries.map(([name, val]) => {
|
|
return `${name} = ${valueToString(val)}`;
|
|
});
|
|
|
|
return `{ ${formattedEntries.join(", ")} }`;
|
|
}
|