import assert from 'assert'; import { createLexer } from '../src/core/lexer.js'; import { createParser } from '../src/core/parser.js'; import { createInterpreter } from '../src/core/interpreter.js'; describe('Recursive Function Calls', () => { function interpret(code) { const lexer = createLexer(code); const tokens = lexer.allTokens(); const parser = createParser(tokens); const ast = parser.parse(); const interpreter = createInterpreter(ast); interpreter.interpret(); // Execute the code return interpreter; // Return the interpreter instance to access scope } it('should correctly handle simple function calls', () => { const code = ` simpleFunc : n -> n + 1; result : simpleFunc 5; `; const interpreter = interpret(code); const result = interpreter.scope.get('result'); assert.strictEqual(result.value, 6); }); it('should correctly handle when expressions', () => { const code = ` checkNumber : num -> when num is 1 then "One" 2 then "Two" _ then "Something else"; result : checkNumber 3; `; const interpreter = interpret(code); assert.strictEqual(interpreter.scope.get('result'), 'Something else'); }); it('should correctly compute factorial recursively', () => { const code = ` factorial : n -> when n is 0 then 1 1 then 1 _ then n * (factorial (n - 1)); result1 : factorial 0; result2 : factorial 1; result3 : factorial 5; result4 : factorial 6; `; const interpreter = interpret(code); const result1 = interpreter.scope.get('result1'); const result2 = interpreter.scope.get('result2'); const result3 = interpreter.scope.get('result3'); const result4 = interpreter.scope.get('result4'); assert.strictEqual(result1.value, 1); assert.strictEqual(result2.value, 1); assert.strictEqual(result3.value, 120); // 5! = 120 assert.strictEqual(result4.value, 720); // 6! = 720 }); it('should correctly compute Fibonacci numbers recursively', () => { const code = ` fib : n -> when n is 0 then 0 1 then 1 _ then (fib (n - 1)) + (fib (n - 2)); fib0 : fib 0; fib1 : fib 1; fib2 : fib 2; fib3 : fib 3; fib4 : fib 4; fib5 : fib 5; fib6 : fib 6; `; const interpreter = interpret(code); const fib0 = interpreter.scope.get('fib0'); const fib1 = interpreter.scope.get('fib1'); const fib2 = interpreter.scope.get('fib2'); const fib3 = interpreter.scope.get('fib3'); const fib4 = interpreter.scope.get('fib4'); const fib5 = interpreter.scope.get('fib5'); const fib6 = interpreter.scope.get('fib6'); assert.strictEqual(fib0.value, 0); assert.strictEqual(fib1.value, 1); assert.strictEqual(fib2.value, 1); assert.strictEqual(fib3.value, 2); assert.strictEqual(fib4.value, 3); assert.strictEqual(fib5.value, 5); assert.strictEqual(fib6.value, 8); }); it('should correctly compute sum of digits recursively', () => { const code = ` sumDigits : n -> when n is 0 then 0 _ then (n % 10) + (sumDigits ((n - (n % 10)) / 10)); result1 : sumDigits 123; result2 : sumDigits 456; result3 : sumDigits 999; `; const interpreter = interpret(code); const result1 = interpreter.scope.get('result1'); const result2 = interpreter.scope.get('result2'); const result3 = interpreter.scope.get('result3'); assert.strictEqual(result1.value, 6); assert.strictEqual(result2.value, 15); assert.strictEqual(result3.value, 27); }); it('should correctly compute power recursively', () => { const code = ` power : base exp -> when exp is 0 then 1 1 then base _ then base * (power base (exp - 1)); result1 : power 2 0; result2 : power 2 1; result3 : power 2 3; result4 : power 3 4; result5 : power 5 2; `; const interpreter = interpret(code); const result1 = interpreter.scope.get('result1'); const result2 = interpreter.scope.get('result2'); const result3 = interpreter.scope.get('result3'); const result4 = interpreter.scope.get('result4'); const result5 = interpreter.scope.get('result5'); assert.strictEqual(result1.value, 1); assert.strictEqual(result2.value, 2); assert.strictEqual(result3.value, 8); // 2^3 = 8 assert.strictEqual(result4.value, 81); // 3^4 = 81 assert.strictEqual(result5.value, 25); // 5^2 = 25 }); it('should correctly compute greatest common divisor recursively', () => { const code = ` gcd : a b -> when b is 0 then a _ then gcd b (a % b); result1 : gcd 48 18; result2 : gcd 54 24; result3 : gcd 7 13; result4 : gcd 100 25; `; const interpreter = interpret(code); const result1 = interpreter.scope.get('result1'); const result2 = interpreter.scope.get('result2'); const result3 = interpreter.scope.get('result3'); const result4 = interpreter.scope.get('result4'); assert.strictEqual(result1.value, 6); assert.strictEqual(result2.value, 6); assert.strictEqual(result3.value, 1); assert.strictEqual(result4.value, 25); }); it('should handle mutual recursion correctly', () => { const code = ` isEven : n -> when n is 0 then true 1 then false _ then isOdd (n - 1); isOdd : n -> when n is 0 then false 1 then true _ then isEven (n - 1); result1 : isEven 0; result2 : isEven 1; result3 : isEven 2; result4 : isEven 3; result5 : isOdd 0; result6 : isOdd 1; result7 : isOdd 2; result8 : isOdd 3; `; const interpreter = interpret(code); assert.strictEqual(interpreter.scope.get('result1'), true); assert.strictEqual(interpreter.scope.get('result2'), false); assert.strictEqual(interpreter.scope.get('result3'), true); assert.strictEqual(interpreter.scope.get('result4'), false); assert.strictEqual(interpreter.scope.get('result5'), false); assert.strictEqual(interpreter.scope.get('result6'), true); assert.strictEqual(interpreter.scope.get('result7'), false); assert.strictEqual(interpreter.scope.get('result8'), true); }); it('should handle deep recursion without stack overflow', () => { const code = ` countDown : n -> when n is 0 then 0 _ then 1 + (countDown (n - 1)); result : countDown 100; `; const interpreter = interpret(code); const result = interpreter.scope.get('result'); assert.strictEqual(result.value, 100); }); });