scrowl/src/ui/Component/Value.tsx
2026-02-15 19:26:32 +01:00

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(", ")} }`;
}