import { Expr, Pattern, ProductPattern, Literal, FieldAssignment, FieldPattern } from '../value'; export function exprToString(expr: Expr): string { switch (expr.tag) { case "literal": return literalToString(expr.literal); case "var_use": return `\$${expr.name}`; case "call": return `${expr.name}(${expr.args.map(exprToString).join(", ")})`; case "tuple": return `(${expr.exprs.map(exprToString).join(", ")})`; case "record": { const fields = expr.fields.map(fieldAssignmentToString).join(", "); return `{ ${fields} }`; } case "tag": return `#${expr.tag_name}`; case "tagged": { // We wrap the payload in parens if it's complex to be safe, // but strictly speaking #foo 1 is valid. // Let's just output space + expr. const payload = exprToString(expr.expr); // Heuristic: if payload starts with '(', '{', or is simple, we might not need parens, // but for "tagged" expression precedence, it's often safer to wrap unless atomic. return `#${expr.tag_name} ${payload}`; } case "let": { const bindings = expr.bindings .map(b => `${productPatternToString(b.pattern)} = ${exprToString(b.expr)}`) .join(", "); return `let { ${bindings} . ${exprToString(expr.body)} }`; } case "lambda": { const params = expr.parameters.map(productPatternToString).join(", "); return `fn { ${params} . ${exprToString(expr.body)} }`; } case "apply": { const args = expr.args.map(exprToString).join(", "); return `apply(${exprToString(expr.callee)} ! ${args})`; } case "match": { const branches = expr.branches .map(b => `${patternToString(b.pattern)} . ${exprToString(b.body)}`) .join(" | "); return `match ${exprToString(expr.arg)} { ${branches} }`; } } } // === Helpers === function literalToString(lit: Literal): string { switch (lit.tag) { case "number": return lit.value.toString(); case "string": return `"${lit.value}"`; // simplistic string escaping } } function fieldAssignmentToString(f: FieldAssignment): string { return `${f.name} = ${exprToString(f.expr)}`; } // === Pattern Printers === export function patternToString(pat: Pattern): string { switch (pat.tag) { case "tag": return `#${pat.tag_name}`; case "tagged": return `#${pat.tag_name} ${patternToString(pat.pattern)}`; // If it's a product pattern (any, tuple, record) default: return productPatternToString(pat); } } export function productPatternToString(pat: ProductPattern): string { switch (pat.tag) { case "any": return pat.name; case "tuple": return `(${pat.patterns.map(productPatternToString).join(", ")})`; case "record": return `{ ${pat.fields.map(fieldPatternToString).join(", ")} }`; } } function fieldPatternToString(f: FieldPattern): string { // Check for punning: if pattern is "any" and name matches fieldName if (f.pattern.tag === "any" && f.pattern.name === f.fieldName) { return f.fieldName; } return `${f.fieldName} = ${productPatternToString(f.pattern)}`; }