about summary refs log tree commit diff stats
path: root/js/baba-yaga/tests/data_structures.test.js
blob: f22fb82c25b3005b0cf895c8c42e3e4de70c0ce5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
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');
  });
});