import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { parse, Node } from '../src/parser.js'; describe('parser', () => { it('parses integer literals', () => { const node = parse('(42)'); assert.equal(node.kind, 'list'); if (node.kind !== 'list') throw new Error('unreachable'); assert.equal(node.items.length, 1); assert.deepStrictEqual(node.items[0], { kind: 'int', value: 42n }); }); it('parses negative integer literals', () => { const node = parse('(-5)'); if (node.kind !== 'list') throw new Error('unreachable'); assert.deepStrictEqual(node.items[0], { kind: 'int', value: -5n }); }); it('parses boolean literals', () => { const node = parse('(TRUE FALSE)'); if (node.kind !== 'list') throw new Error('unreachable'); assert.deepStrictEqual(node.items[0], { kind: 'bool', value: true }); assert.deepStrictEqual(node.items[1], { kind: 'bool', value: false }); }); it('parses instructions', () => { const node = parse('(DYADIC.+ BOOLEAN.NOT EXEC.IF)'); if (node.kind !== 'list') throw new Error('unreachable'); assert.deepStrictEqual(node.items[0], { kind: 'instr', name: 'DYADIC.+' }); assert.deepStrictEqual(node.items[1], { kind: 'instr', name: 'BOOLEAN.NOT' }); assert.deepStrictEqual(node.items[2], { kind: 'instr', name: 'EXEC.IF' }); }); it('parses unbound names', () => { const node = parse('(TAXRATE STAKED)'); if (node.kind !== 'list') throw new Error('unreachable'); assert.deepStrictEqual(node.items[0], { kind: 'name', text: 'TAXRATE' }); assert.deepStrictEqual(node.items[1], { kind: 'name', text: 'STAKED' }); }); it('parses nested lists', () => { const node = parse('(1 (2 3))'); if (node.kind !== 'list') throw new Error('unreachable'); assert.equal(node.items.length, 2); const inner = node.items[1]; assert.equal(inner.kind, 'list'); if (inner.kind !== 'list') throw new Error('unreachable'); assert.equal(inner.items.length, 2); }); it('strips comments', () => { const node = parse('(;; this is a comment\n42)'); if (node.kind !== 'list') throw new Error('unreachable'); assert.equal(node.items.length, 1); assert.deepStrictEqual(node.items[0], { kind: 'int', value: 42n }); }); it('throws on empty program', () => { assert.throws(() => parse(''), /Empty program/); }); it('throws on unmatched open paren', () => { assert.throws(() => parse('(1 2'), /Unmatched/); }); it('throws on trailing tokens', () => { assert.throws(() => parse('(1) 2'), /Unexpected tokens/); }); });