about summary refs log tree commit diff stats
path: root/js/baba-yaga/tests/interpreter-with-header.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/baba-yaga/tests/interpreter-with-header.test.js')
-rw-r--r--js/baba-yaga/tests/interpreter-with-header.test.js90
1 files changed, 90 insertions, 0 deletions
diff --git a/js/baba-yaga/tests/interpreter-with-header.test.js b/js/baba-yaga/tests/interpreter-with-header.test.js
new file mode 100644
index 0000000..0f50be4
--- /dev/null
+++ b/js/baba-yaga/tests/interpreter-with-header.test.js
@@ -0,0 +1,90 @@
+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 locals', () => {
+  it('evaluates untyped locals', () => {
+    const code = `
+      addMul : x y -> with (inc : x + 1; prod : inc * y;) -> inc + prod;
+      r : addMul 2 5;
+    `;
+    const itp = interpret(code);
+    assert.strictEqual(itp.scope.get('r').value, (2+1) + ((2+1)*5));
+  });
+
+  it('evaluates typed locals with validation', () => {
+    const code = `
+      sumNext : (x: Int, y: Int) -> Int ->
+        with (nx Int; ny Int; nx : x + 1; ny : y + 1;) -> nx + ny;
+      r : sumNext 2 3;
+    `;
+    const itp = interpret(code);
+    assert.strictEqual(itp.scope.get('r').value, 7);
+  });
+
+  it('rejects typed local mismatch', () => {
+    const code = `
+      bad : (x: Int) -> Int ->
+        with (s String; s : x + 1;) -> 0;
+      r : bad 2;
+    `;
+    assert.throws(() => interpret(code), /Type mismatch for s: expected String/);
+  });
+
+  it('works with when expressions', () => {
+    const code = `
+      classify : n ->
+        with (lo Int; hi Int; lo : 10; hi : 100;) ->
+          when n is
+            0 then "zero"
+            _ then when (n > hi) is
+                     true then "large"
+                     _    then when (n > lo) is
+                                true then "medium"
+                                _    then "small";
+      a : classify 0;
+      b : classify 50;
+      c : classify 200;
+    `;
+    const itp = interpret(code);
+    assert.strictEqual(itp.scope.get('a'), 'zero');
+    assert.strictEqual(itp.scope.get('b'), 'medium');
+    assert.strictEqual(itp.scope.get('c'), 'large');
+  });
+
+  it('supports with rec for mutual recursion', () => {
+    const code = `
+      isEvenOdd : z ->
+        with rec (
+          isEven : n -> when n is 0 then true _ then isOdd (n - 1);
+          isOdd : n -> when n is 0 then false _ then isEven (n - 1);
+        ) -> { e: isEven 10, o: isOdd 7 };
+      r : isEvenOdd 0;
+    `;
+    const itp = interpret(code);
+    const r = itp.scope.get('r');
+    assert.strictEqual(r.e, true);
+    assert.strictEqual(r.o, true);
+  });
+
+  it('errors if with rec binding is not a function', () => {
+    const code = `
+      bad : z -> with rec (x : 1;) -> 0;
+      r : bad 0;
+    `;
+    assert.throws(() => interpret(code), /with rec expects function-valued bindings/);
+  });
+});
+
+