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