import { createLexer } from '../src/core/lexer.js'; import { createParser } from '../src/core/parser.js'; import { createInterpreter } from '../src/core/interpreter.js'; function runBabaYaga(code) { const lexer = createLexer(code); const tokens = lexer.allTokens(); const parser = createParser(tokens); const ast = parser.parse(); const outputs = []; const debugOutputs = []; const host = { io: { out: (...args) => outputs.push(args.join(' ')), debug: (...args) => debugOutputs.push(args.join(' ')), in: () => '', }, }; const interpreter = createInterpreter(ast, host); const result = interpreter.interpret(); return { outputs, debugOutputs, result }; } describe('Utility Functions', () => { describe('validate namespace', () => { test('validate.notEmpty', () => { const code = ` io.out (validate.notEmpty "hello"); io.out (validate.notEmpty ""); io.out (validate.notEmpty [1, 2, 3]); io.out (validate.notEmpty []); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true', 'false', 'true', 'false']); }); test('validate.range', () => { const code = ` io.out (validate.range 1 10 5); io.out (validate.range 1 10 15); io.out (validate.range 1 10 1); io.out (validate.range 1 10 10); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true', 'false', 'true', 'true']); }); test('validate.email', () => { const code = ` io.out (validate.email "test@example.com"); io.out (validate.email "invalid-email"); io.out (validate.email "user@domain.co.uk"); io.out (validate.email "@domain.com"); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true', 'false', 'true', 'false']); }); test('validate.type', () => { const code = ` io.out (validate.type "Int" 42); io.out (validate.type "String" 42); io.out (validate.type "String" "hello"); io.out (validate.type "Bool" true); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true', 'false', 'true', 'true']); }); }); describe('text namespace', () => { test('text.lines', () => { const code = ` // Test with single line (since escape sequences aren't implemented yet) lines : text.lines "hello world test"; io.out (length lines); first : lines.0; io.out first; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['1', 'hello world test']); }); test('text.words', () => { const code = ` words : text.words "hello world test"; io.out (length words); io.out words.0; io.out words.1; io.out words.2; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['3', 'hello', 'world', 'test']); }); test('text.padLeft and text.padRight', () => { const code = ` io.out (text.padLeft 10 "hi"); io.out (text.padRight 10 "hi"); io.out (str.length (text.padLeft 5 "test")); `; const { outputs } = runBabaYaga(code); expect(outputs[0]).toBe(' hi'); expect(outputs[1]).toBe('hi '); expect(outputs[2]).toBe('5'); }); }); describe('utility functions', () => { test('chunk', () => { const code = ` numbers : [1, 2, 3, 4, 5, 6]; chunks : chunk numbers 2; io.out (length chunks); firstChunk : chunks.0; io.out (length firstChunk); io.out firstChunk.0; io.out firstChunk.1; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['3', '2', '1', '2']); }); test('range', () => { const code = ` r1 : range 1 5; r2 : range 5 1; io.out (length r1); io.out r1.0; io.out r1.4; io.out (length r2); io.out r2.0; io.out r2.4; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['5', '1', '5', '5', '5', '1']); }); test('repeat', () => { const code = ` repeated : repeat 3 "hello"; io.out (length repeated); io.out repeated.0; io.out repeated.1; io.out repeated.2; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['3', 'hello', 'hello', 'hello']); }); }); describe('sort namespace', () => { test('sort.by with numbers', () => { const code = ` numbers : [3, 1, 4, 1, 5, 9, 2, 6]; sorted : sort.by numbers (x -> x); io.out sorted.0; io.out sorted.1; io.out sorted.7; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['1', '1', '9']); }); test('sort.by with objects', () => { const code = ` people : [ {name: "Alice", age: 30}, {name: "Bob", age: 25}, {name: "Charlie", age: 35} ]; sortedByAge : sort.by people (p -> p.age); first : sortedByAge.0; second : sortedByAge.1; third : sortedByAge.2; io.out first.name; io.out second.name; io.out third.name; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['Bob', 'Alice', 'Charlie']); }); }); describe('group namespace', () => { test('group.by', () => { const code = ` numbers : [1, 2, 3, 4, 5, 6]; grouped : group.by numbers (x -> x % 2 = 0); evenGroup : grouped."true"; oddGroup : grouped."false"; io.out (length evenGroup); io.out (length oddGroup); io.out (evenGroup.0); io.out (oddGroup.0); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['3', '3', '2', '1']); }); }); describe('random namespace', () => { test('random.choice', () => { const code = ` list : [1, 2, 3]; choice : random.choice list; io.out (validate.range 1 3 choice); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true']); }); test('random.shuffle', () => { const code = ` list : [1, 2, 3, 4, 5]; shuffled : random.shuffle list; io.out (length shuffled); io.out (length list); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['5', '5']); }); test('random.range', () => { const code = ` r : random.range 1 10; io.out (validate.range 1 10 r); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true']); }); }); describe('debug namespace', () => { test('debug.print', () => { const code = ` testFunc : x -> x * 2; debug.print 42; debug.print testFunc; `; const { debugOutputs } = runBabaYaga(code); expect(debugOutputs.length).toBe(2); expect(debugOutputs[0]).toContain('42'); expect(debugOutputs[1]).toContain('function'); }); test('debug.inspect', () => { const code = ` testFunc : x -> x * 2; inspection : debug.inspect testFunc; len : str.length inspection; io.out (len > 10); `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['true']); }); }); describe('assert function', () => { test('assert success', () => { const code = ` assert (2 + 2 = 4) "Math works"; io.out "Success"; `; const { outputs } = runBabaYaga(code); expect(outputs).toEqual(['Success']); }); test('assert failure', () => { const code = `assert (2 + 2 = 5) "This should fail";`; expect(() => runBabaYaga(code)).toThrow('Assertion failed: This should fail'); }); }); });