about summary refs log tree commit diff stats
path: root/js/baba-yaga/tests/data_structures.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/tests/data_structures.test.js')
-rw-r--r--js/baba-yaga/tests/data_structures.test.js211
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