about summary refs log tree commit diff stats
path: root/js/baba-yaga/tests/interpreter-with-header.test.js
blob: 0f50be488117a77c7345c5d255b34e2c503bad63 (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
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/);
  });
});