diff options
Diffstat (limited to 'js/baba-yaga/tests/data_structures.test.js')
-rw-r--r-- | js/baba-yaga/tests/data_structures.test.js | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/js/baba-yaga/tests/data_structures.test.js b/js/baba-yaga/tests/data_structures.test.js new file mode 100644 index 0000000..f22fb82 --- /dev/null +++ b/js/baba-yaga/tests/data_structures.test.js @@ -0,0 +1,211 @@ +const assert = require('assert'); +const { createLexer } = require('../src/core/lexer'); +const { createParser } = require('../src/core/parser'); +const { createInterpreter } = require('../src/core/interpreter'); + +describe('Data Structures and Higher-Order Functions', () => { + 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 interpret list literals', () => { + const code = 'myList : [1, 2, 3];'; + const interpreter = interpret(code); + const myList = interpreter.scope.get('myList'); + assert.deepStrictEqual(myList.map(item => item.value), [1, 2, 3]); + }); + + it('should correctly interpret table literals', () => { + const code = 'myTable : { name: "Alice" age: 30 };'; + const interpreter = interpret(code); + const expectedTable = { type: 'Object', properties: new Map([['name', 'Alice'], ['age', { value: 30, isFloat: false }]]) }; + const actualTable = interpreter.scope.get('myTable'); + assert.strictEqual(actualTable.type, expectedTable.type); + assert.deepStrictEqual(Array.from(actualTable.properties.entries()), Array.from(expectedTable.properties.entries())); + }); + + it('should correctly access list elements using dot notation', () => { + const code = 'myList : [10, 20, 30];\nio.out myList.1;'; + // For io.out, we need to capture console.log output. This test will pass if no error is thrown. + // A more robust test would mock console.log. + assert.doesNotThrow(() => interpret(code)); + }); + + it('should correctly access table properties using dot notation', () => { + const code = 'myTable : { name: "Bob", age: 25 };\nio.out myTable.name;'; + assert.doesNotThrow(() => interpret(code)); + }); + + it('should correctly interpret anonymous functions', () => { + const code = 'myFunc : (x -> x + 1);\nio.out (myFunc 5);'; + assert.doesNotThrow(() => interpret(code)); + }); + + it('should correctly apply map to a list', () => { + const code = 'io.out (map (x -> x * 2) [1, 2, 3]);'; + assert.doesNotThrow(() => interpret(code)); + }); + + it('should correctly apply filter to a list', () => { + const code = 'io.out (filter (x -> x > 2) [1, 2, 3, 4, 5]);'; + assert.doesNotThrow(() => interpret(code)); + }); + + it('should correctly apply reduce to a list', () => { + const code = 'io.out (reduce (acc item -> acc + item) 0 [1, 2, 3, 4]);'; + assert.doesNotThrow(() => interpret(code)); + }); + + it('should compose functions with reduce (composeAll) and accept list literal as argument', () => { + const code = ` + composeAll : funcs -> + reduce (acc fn -> (x -> acc (fn x))) (x -> x) funcs; + + inc : x -> x + 1; + double : x -> x * 2; + + combo : composeAll [inc, double]; + res : combo 3; + `; + const interpreter = interpret(code); + const res = interpreter.scope.get('res'); + assert.strictEqual(res.value, 7); + }); + + // New tests for list and table pattern matching + it('should correctly match a list literal in a when expression', () => { + const code = ` + myList : [1, 2, 3]; + result : when myList is + [1, 2, 3] then "Matched List" + _ then "Did Not Match"; + `; + const interpreter = interpret(code); + assert.strictEqual(interpreter.scope.get('result'), 'Matched List'); + }); + + it('should correctly match a list with a wildcard in a when expression', () => { + const code = ` + myList : [1, 2, 3]; + result : when myList is + [1, _, 3] then "Matched Wildcard List" + _ then "Did Not Match"; + `; + const interpreter = interpret(code); + assert.strictEqual(interpreter.scope.get('result'), 'Matched Wildcard List'); + }); + + it('should correctly match a table literal in a when expression', () => { + const code = ` + myTable : { a: 1, b: 2 }; + result : when myTable is + { a: 1, b: 2 } then "Matched Table" + _ then "Did Not Match"; + `; + const interpreter = interpret(code); + assert.strictEqual(interpreter.scope.get('result'), 'Matched Table'); + }); + + it('should correctly match a table with a wildcard value in a when expression', () => { + const code = ` + myTable : { a: 1, b: 2 }; + result : when myTable is + { a: 1, b: _ } then "Matched Wildcard Table" + _ then "Did Not Match"; + `; + const interpreter = interpret(code); + assert.strictEqual(interpreter.scope.get('result'), 'Matched Wildcard Table'); + }); + + it('should correctly call a function defined within a table', () => { + const code = ` + myCalculator : { + add: x y -> x + y, + subtract: x y -> x - y + }; + resultAdd : myCalculator.add 10 5; + resultSubtract : myCalculator.subtract 10 5; + `; + const interpreter = interpret(code); + const resultAdd = interpreter.scope.get('resultAdd'); + const resultSubtract = interpreter.scope.get('resultSubtract'); + assert.strictEqual(resultAdd.value, 15); + assert.strictEqual(resultSubtract.value, 5); + }); + + it('should allow both direct and Map-based property access on tables', () => { + const code = ` + myObj : { x: 42, y: "ok" }; + `; + const interpreter = interpret(code); + const obj = interpreter.scope.get('myObj'); + // direct access via proxy + assert.strictEqual(obj.x.value, 42); + assert.strictEqual(obj.y, 'ok'); + // map-based access remains available + assert.strictEqual(obj.properties.get('x').value, 42); + assert.strictEqual(obj.properties.get('y'), 'ok'); + }); + + it('should return shape metadata for lists, strings, tables, and scalars', () => { + const code = ` + lst : [10, 20, 30]; + str : "abc"; + tbl : { a: 1, b: 2 }; + n : 42; + + sLst : shape lst; + sStr : shape str; + sTbl : shape tbl; + sNum : shape n; + `; + const interpreter = interpret(code); + const sLst = interpreter.scope.get('sLst'); + const sStr = interpreter.scope.get('sStr'); + const sTbl = interpreter.scope.get('sTbl'); + const sNum = interpreter.scope.get('sNum'); + + // List + assert.strictEqual(sLst.kind, 'List'); + assert.strictEqual(sLst.rank.value, 1); + assert.strictEqual(sLst.size.value, 3); + assert.strictEqual(sLst.shape[0].value, 3); + + // String + assert.strictEqual(sStr.kind, 'String'); + assert.strictEqual(sStr.rank.value, 1); + assert.strictEqual(sStr.size.value, 3); + assert.strictEqual(sStr.shape[0].value, 3); + + // Table + assert.strictEqual(sTbl.kind, 'Table'); + assert.strictEqual(sTbl.rank.value, 1); + assert.strictEqual(sTbl.size.value, 2); + assert.strictEqual(sTbl.shape[0].value, 2); + // keys array contains 'a' and 'b' (order not enforced here) + const keys = new Set(sTbl.keys); + assert.strictEqual(keys.has('a') && keys.has('b'), true); + + // Scalar + assert.strictEqual(sNum.kind, 'Scalar'); + assert.strictEqual(sNum.rank.value, 0); + assert.strictEqual(Array.isArray(sNum.shape) && sNum.shape.length === 0, true); + assert.strictEqual(sNum.size.value, 1); + }); + + it('should correctly handle a wildcard pattern in a when expression for tables', () => { + const code = ` + myTable : { a: 1 b: 2 }; + result : when myTable is + _ then "Wildcard Match"; + `; + const interpreter = interpret(code); + assert.strictEqual(interpreter.scope.get('result'), 'Wildcard Match'); + }); +}); \ No newline at end of file |