// Transform function definitions to support tail call optimization via trampolines
function transformToTCO(functionDefs) {
    const functions = new Map();
    
    // Parse function definitions
    functionDefs.forEach(([name, params, body]) => {
        functions.set(name, { params, body });
    });
    
    // Transform each function body to use trampolines
    const transformedFunctions = {};
    
    functionDefs.forEach(([name, params, body]) => {
        const transformedBody = transformFunctionBody(body, functions);
        
        // Create the trampoline wrapper
        transformedFunctions[name] = new Function(...params, `
            function trampoline(fn) {
                let result = fn;
                while (typeof result === 'function') {
                    result = result();
                }
                return result;
            }
            
            function ${name}_impl(${params.join(', ')}) {
                ${transformedBody}
            }
            
            return trampoline(() => ${name}_impl(${params.join(', ')}));
        `);
    });
    
    return transformedFunctions;
}

function transformFunctionBody(body, functions) {
    // Remove 'return ' prefix if present
    const cleanBody = body.replace(/^return\s+/, '');
    
    // Transform the body to use continuation-passing style
    return `return ${transformExpression(cleanBody, functions)};`;
}

function transformExpression(expr, functions) {
    // Handle ternary operator (condition ? expr1 : expr2)
    const ternaryMatch = expr.match(/^(.+?)\s*\?\s*(.+?)\s*:\s*(.+)$/);
    if (ternaryMatch) {
        const [, condition, thenExpr, elseExpr] = ternaryMatch;
        const transformedThen = transformExpression(thenExpr.trim(), functions);
        const transformedElse = transformExpression(elseExpr.trim(), functions);
        return `(${condition}) ? ${transformedThen} : ${transformedElse}`;
    }
    
    // Handle function calls - detect if it's a recursive call
    const functionCallMatch = expr.match(/^(\w+)\s*\(([^)]*)\)$/);
    if (functionCallMatch) {
        const [, funcName, args] = functionCallMatch;
        
        // If it's a function we're transforming, make it return a thunk
        if (functions.has(funcName)) {
            return `(() => ${funcName}_impl(${args}))`;
        }
    }
    
    // Handle throw statements
    if (expr.trim().startsWith('throw ')) {
        return expr; // Keep throw statements as-is
    }
    
    // For non-function expressions (like strings, numbers), return as-is
    return expr;
}

// Example usage and test cases
console.log("=== Testing TCO Transformer ===\n");

// Test Case 1: Simple countdown
const simpleCountdown = [
    ['f', ['x'], 'return x ? f(--x) : "Yay"']
];

console.log("Test 1: Simple countdown");
const tco1 = transformToTCO(simpleCountdown);
try {
    console.log("f(5):", tco1.f(5));
    console.log("f(100000):", tco1.f(100000)); // Should not stack overflow
    console.log("✓ Success");
} catch (e) {
    console.log("✗ Failed:", e.message);
}
console.log();

// Test Case 2: Function with multiple parameters
const multiParam = [
    ['g', ['n', 's'], 'return n ? g(--n, s || 0) : s']
];

console.log("Test 2: Multiple parameters");
const tco2 = transformToTCO(multiParam);
try {
    console.log("g(3, 'hello'):", tco2.g(3, 'hello'));
    console.log("g(5, undefined):", tco2.g(5, undefined));
    console.log("g(50000, 'result'):", tco2.g(50000, 'result')); // Should not stack overflow
    console.log("✓ Success");
} catch (e) {
    console.log("✗ Failed:", e.message);
}
console.log();

// Test Case 3: Circular tail calls (mutual recursion)
const circularCalls = [
    ['f0', ['x', 'y'], 'return f1(x, y)'],
    ['f1', ['x', 'y'], 'return f2(x, y)'],
    ['f2', ['x', 'y'], 'return x > 0 ? f0(x - 1, y + 1) : y']
];

console.log("Test 3: Circular tail calls");
const tco3 = transformToTCO(circularCalls);
try {
    console.log("f0(5, 10):", tco3.f0(5, 10));
    console.log("f0(1000, 0):", tco3.f0(1000, 0)); // Should not stack overflow
    console.log("✓ Success");
} catch (e) {
    console.log("✗ Failed:", e.message);
}
console.log();

// Test Case 4: Large circular call chain (like your 10000 functions example)
const largeCircular = [...Array(100)].map((_, i) => [
    `f${i}`, 
    ['x', 'y'], 
    i === 99 
        ? 'return x > 0 ? f0(x - 1, y + 1) : y'  // Last function loops back to f0
        : `return f${i + 1}(x, y)`               // Each function calls the next
]);

console.log("Test 4: Large circular call chain (100 functions)");
const tco4 = transformToTCO(largeCircular);
try {
    console.log("f0(5, 0):", tco4.f0(5, 0));
    console.log("f0(200, 100):", tco4.f0(200, 100)); // Should not stack overflow
    console.log("✓ Success");
} catch (e) {
    console.log("✗ Failed:", e.message);
}
console.log();

// Test Case 5: With error throwing
const withErrors = [
    ['errorTest', ['x'], 'return x < 0 ? throw new Error("Negative!") : x === 0 ? "Done" : errorTest(x - 1)']
];

console.log("Test 5: With error throwing");
const tco5 = transformToTCO(withErrors);
try {
    console.log("errorTest(3):", tco5.errorTest(3));
    console.log("errorTest(0):", tco5.errorTest(0));
    try {
        console.log("errorTest(-1):", tco5.errorTest(-1));
    } catch (e) {
        console.log("errorTest(-1) threw:", e.message);
    }
    console.log("✓ Success");
} catch (e) {
    console.log("✗ Failed:", e.message);
}

// Performance comparison
console.log("\n=== Performance Comparison ===");

// Original recursive function (will stack overflow at high values)
function originalF(x) {
    return x ? originalF(x - 1) : "Yay";
}

// TCO version
const tcoF = transformToTCO([['f', ['x'], 'return x ? f(--x) : "Yay"']]);

console.time("TCO f(10000)");
console.log("TCO result:", tcoF.f(10000));
console.timeEnd("TCO f(10000)");

console.time("Original f(1000)");
console.log("Original result:", originalF(1000));
console.timeEnd("Original f(1000)");

console.log("\nNote: Original function will stack overflow at ~10000+ iterations");
console.log("TCO version can handle much larger values without stack overflow");

Embed on website

To embed this program on your website, copy the following code and paste it into your website's HTML: