import { Closure, Value, Env, EnvFrame } from 'src/lang/eval/value'; import { exprToString, productPatternToString } from './Expr'; export function Val(prop: { value: Value }) { return ( { valueToString(prop.value) } ); } 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(", ")} }`; }