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