71 lines
2.6 KiB
TypeScript
71 lines
2.6 KiB
TypeScript
|
|
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/);
|
||
|
|
});
|
||
|
|
});
|