Move language files into dedicated folder

This commit is contained in:
Yura Dupyn 2026-02-07 10:43:30 +01:00
parent 3d1cd89067
commit 1b406899e0
15 changed files with 7 additions and 343 deletions

107
src/lang/debug/expr_show.ts Normal file
View file

@ -0,0 +1,107 @@
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)}`;
}