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'; 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(); return interpreter; } describe('with header: advanced patterns', () => { it('handles empty with blocks', () => { const code = ` testEmptyWith : x -> with () -> x; result : testEmptyWith 42; `; const itp = interpret(code); assert.strictEqual(itp.scope.get('result').value, 42); }); it('handles single entry with blocks', () => { const code = ` testSingleEntry : x -> with (value : x + 1;) -> value; result : testSingleEntry 5; `; const itp = interpret(code); assert.strictEqual(itp.scope.get('result').value, 6); }); it('handles complex dependencies between entries', () => { const code = ` testDependencies : x y -> with ( a : x + y; b : a * 2; c : b - x; d : c + a; e : d * b; ) -> e; result : testDependencies 3 4; `; const itp = interpret(code); // a = 3 + 4 = 7 // b = 7 * 2 = 14 // c = 14 - 3 = 11 // d = 11 + 7 = 18 // e = 18 * 14 = 252 assert.strictEqual(itp.scope.get('result').value, 252); }); it('handles deep nesting of when expressions beyond 4 levels', () => { const code = ` testDeepNesting : x -> with ( level1 : when x is 0 then "zero" _ then when (x < 5) is true then "small" _ then when (x < 10) is true then "medium" _ then when (x < 20) is true then "large" _ then when (x < 50) is true then "huge" _ then when (x < 100) is true then "massive" _ then "gigantic"; ) -> level1; result1 : testDeepNesting 0; result2 : testDeepNesting 3; result3 : testDeepNesting 7; result4 : testDeepNesting 15; result5 : testDeepNesting 30; result6 : testDeepNesting 70; result7 : testDeepNesting 150; `; const itp = interpret(code); assert.strictEqual(itp.scope.get('result1'), 'zero'); assert.strictEqual(itp.scope.get('result2'), 'small'); assert.strictEqual(itp.scope.get('result3'), 'medium'); assert.strictEqual(itp.scope.get('result4'), 'large'); assert.strictEqual(itp.scope.get('result5'), 'huge'); assert.strictEqual(itp.scope.get('result6'), 'massive'); assert.strictEqual(itp.scope.get('result7'), 'gigantic'); }); it('handles mixed types in with blocks', () => { const code = ` testMixedTypes : x -> with ( num Int; num : x + 1; str String; str : str.concat "Value: " "number"; isValid Bool; isValid : x > 0; list List; list : [x, x * 2, x * 3]; table Table; table : { value: x, doubled: x * 2 }; ) -> { num: num, str: str, isValid: isValid, list: list, table: table }; result : testMixedTypes 10; `; const itp = interpret(code); const result = itp.scope.get('result'); assert.strictEqual(result.num.value, 11); assert.strictEqual(result.str, 'Value: number'); assert.strictEqual(result.isValid, true); assert.deepStrictEqual(result.list.map(x => x.value), [10, 20, 30]); // Baba Yaga objects have properties Map structure assert.strictEqual(result.table.properties.get('value').value, 10); assert.strictEqual(result.table.properties.get('doubled').value, 20); }); it('handles recursive with rec with complex functions', () => { const code = ` testComplexRecursive : n -> with rec ( factorial : x -> when x is 0 then 1 _ then x * (factorial (x - 1)); fibonacci : x -> when x is 0 then 0 1 then 1 _ then (fibonacci (x - 1)) + (fibonacci (x - 2)); ackermann : m n -> when m is 0 then n + 1 _ then when n is 0 then ackermann (m - 1) 1 _ then ackermann (m - 1) (ackermann m (n - 1)); ) -> { fact: factorial n, fib: fibonacci n, ack: ackermann 2 3 }; result : testComplexRecursive 5; `; const itp = interpret(code); const result = itp.scope.get('result'); assert.strictEqual(result.fact.value, 120); // 5! = 120 assert.strictEqual(result.fib.value, 5); // fib(5) = 5 assert.strictEqual(result.ack.value, 9); // ack(2,3) = 9 }); it('handles complex mathematical expressions with type validation', () => { const code = ` testComplexMath : x y z -> with ( a : x * x; b : y * y; c : z * z; sumSquares : a + b; hypotenuse : math.sqrt sumSquares; result : hypotenuse + (math.sqrt (a + b + c)); ) -> result; result : testComplexMath 3 4 5; `; const itp = interpret(code); const result = itp.scope.get('result'); // a = 3² = 9, b = 4² = 16, c = 5² = 25 // sumSquares = 9 + 16 = 25 // hypotenuse = √25 = 5 // result = 5 + √(9 + 16 + 25) = 5 + √50 = 5 + 7.07... ≈ 12.07 assert(Math.abs(result.value - 12.07) < 0.1); }); it('handles string operations with type validation', () => { const code = ` testStringOps : input -> with ( length : str.length input; trimmed : str.trim input; upper : str.upper input; isEmpty : length = 0; hasContent : length > 0; description : str.concat "Length: " (when (length > 10) is true then "long" _ then "short"); ) -> { length: length, trimmed: trimmed, upper: upper, isEmpty: isEmpty, hasContent: hasContent, description: description }; result : testStringOps " Hello World "; `; const itp = interpret(code); const result = itp.scope.get('result'); assert.strictEqual(result.length.value, 15); assert.strictEqual(result.trimmed, 'Hello World'); assert.strictEqual(result.upper, ' HELLO WORLD '); assert.strictEqual(result.isEmpty, false); assert.strictEqual(result.hasContent, true); assert.strictEqual(result.description, 'Length: long'); }); it('handles list and table edge cases with type validation', () => { const code = ` testListTableEdgeCases : items -> with ( count : length items; isEmpty : count = 0; hasItems : count > 0; firstItem : when count is 0 then "none" _ then items.0; lastItem : when count is 0 then "none" _ then when count is 1 then items.0 _ then when count is 2 then items.1 _ then when count is 3 then items.2 _ then "many"; summary : { count: count, empty: isEmpty, hasItems: hasItems, first: firstItem, last: lastItem }; ) -> summary; result1 : testListTableEdgeCases []; result2 : testListTableEdgeCases [42]; result3 : testListTableEdgeCases [1, 2]; result4 : testListTableEdgeCases [10, 20, 30]; `; const itp = interpret(code); const result1 = itp.scope.get('result1'); const result2 = itp.scope.get('result2'); const result3 = itp.scope.get('result3'); const result4 = itp.scope.get('result4'); assert.strictEqual(result1.count.value, 0); assert.strictEqual(result1.empty, true); assert.strictEqual(result1.hasItems, false); assert.strictEqual(result1.first, 'none'); assert.strictEqual(result1.last, 'none'); assert.strictEqual(result2.count.value, 1); assert.strictEqual(result2.empty, false); assert.strictEqual(result2.hasItems, true); assert.strictEqual(result2.first.value, 42); assert.strictEqual(result2.last.value, 42); assert.strictEqual(result3.count.value, 2); assert.strictEqual(result3.empty, false); assert.strictEqual(result3.hasItems, true); assert.strictEqual(result3.first.value, 1); assert.strictEqual(result3.last.value, 2); assert.strictEqual(result4.count.value, 3); assert.strictEqual(result4.empty, false); assert.strictEqual(result4.hasItems, true); assert.strictEqual(result4.first.value, 10); assert.strictEqual(result4.last.value, 30); // items.2 when count is 3 }); it('handles error handling edge cases', () => { const code = ` testErrorHandling : x -> with ( isValid : x >= 0; safeValue : when isValid is true then x _ then 0; result : when isValid is true then { value: safeValue, status: "valid" } _ then { value: safeValue, status: "invalid", error: "negative value" }; ) -> result; result1 : testErrorHandling 5; result2 : testErrorHandling -3; `; const itp = interpret(code); const result1 = itp.scope.get('result1'); const result2 = itp.scope.get('result2'); assert.strictEqual(result1.properties.get('value').value, 5); assert.strictEqual(result1.properties.get('status'), 'valid'); assert.strictEqual(result1.properties.get('error'), undefined); assert.strictEqual(result2.properties.get('value').value, 0); assert.strictEqual(result2.properties.get('status'), 'invalid'); assert.strictEqual(result2.properties.get('error'), 'negative value'); }); });