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'); }); });