/** * codecs.js — Minimal codecs for decoding tree results. * * Implements: decodeResult (from Research.hs) * - Leaf → "t" * - Numbers: toNumber * - Strings: toString * - Lists: toList * - Fallback: raw tree format */ // ── toNumber ──────────────────────────────────────────────────────────────── /** * Decode a tree as a binary number (big-endian). * Leaf = 0, Fork(Leaf, rest) = 2*n, Fork(Stem Leaf, rest) = 2*n+1. */ export function toNumber(t) { if (!Array.isArray(t)) return null; if (t.length === 0) return 0; // Leaf = 0 if (t.length !== 2) return null; // must be Fork const [right, left] = t; // Fork structure: [right, left] // left child determines bit: Leaf = 0, Stem(Leaf) = 1 let bit; if (Array.isArray(left) && left.length === 0) { bit = 0; // Leaf } else if (Array.isArray(left) && left.length === 1) { const child = left[0]; if (Array.isArray(child) && child.length === 0) { bit = 1; // Stem(Leaf) = 1 } else { return null; // Stem of something other than Leaf } } else { return null; } const rest = toNumber(right); if (rest === null) return null; return bit + 2 * rest; } // ── toString ──────────────────────────────────────────────────────────────── /** * Decode a tree as a list of numbers (characters). * Fork(x, rest) = x : list. */ export function toList(t) { if (!Array.isArray(t)) return null; if (t.length === 0) return []; // Leaf = empty list if (t.length !== 2) return null; // must be Fork const [right, left] = t; const rest = toList(right); if (rest === null) return null; return [left, ...rest]; } /** * Decode a tree as a string. */ export function toString(t) { const list = toList(t); if (list === null) return null; try { return list.map((ch) => String.fromCharCode(ch)).join(""); } catch { return null; } } // ── decodeResult ──────────────────────────────────────────────────────────── /** * Decode a tree result using multiple strategies: * 1. Leaf → "t" * 2. String (if all chars are printable) * 3. Number * 4. List * 5. Raw tree format */ export function decodeResult(t) { if (!Array.isArray(t)) { return String(t); } // Leaf if (t.length === 0) { return "t"; } // Try string first (list of char codes) const list = toList(t); if (list !== null && list.length > 0) { const str = list.map((n) => { if (n < 32 || n > 126) return null; return String.fromCharCode(n); }).join(""); if (str) return `"${str}"`; } // Try number const num = toNumber(t); if (num !== null) { return String(num); } // Try list (elements are trees) if (t.length === 2) { const elements = toList(t); if (elements !== null) { const decoded = elements.map((e) => decodeResult(e)); return `[${decoded.join(", ")}]`; } } // Raw tree format return formatTree(t); } /** * Format a tree as a parenthesized expression. */ export function formatTree(t) { if (!Array.isArray(t)) return String(t); if (t.length === 0) return "Leaf"; if (t.length === 1) return `Stem(${formatTree(t[0])})`; if (t.length === 2) return `Fork(${formatTree(t[1])}, ${formatTree(t[0])})`; return `[${t.map(formatTree).join(", ")}]`; }