diff options
Diffstat (limited to 'js/baba-yaga/tests/with-advanced-patterns.test.js')
-rw-r--r-- | js/baba-yaga/tests/with-advanced-patterns.test.js | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/js/baba-yaga/tests/with-advanced-patterns.test.js b/js/baba-yaga/tests/with-advanced-patterns.test.js new file mode 100644 index 0000000..2ea2d44 --- /dev/null +++ b/js/baba-yaga/tests/with-advanced-patterns.test.js @@ -0,0 +1,290 @@ +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'); + }); +}); |