Home Manual Reference Source

src/transform/simplify.js

import assert from 'node:assert';
import {ast} from '@formal-language/grammar';

import leaves from '../leaves.js';
import {visitor, extend} from './visitor.js';

import {iter, next, StopIteration} from './lib.js';

const lines = async (tree) => {
	// TODO use depth first traversal or make newline a terminal
	const result = [];
	const it = iter(leaves(tree));
	let position = null;
	let contents = [];
	let newline = '';
	for (;;) {
		try {
			const leaf = await next(it);
			const current = leaf.buffer;
			if (position === null) position = leaf.position;
			if (current === '\r') {
				assert(newline === '');
				newline += '\r';
			} else if (current === '\n') {
				assert(newline === '' || newline === '\r');
				newline += '\n';
				result.push({
					contents: contents.join(''),
					newline,
				});
				position = null;
				contents = [];
				newline = '';
			} else {
				contents.push(current);
			}
		} catch (error) {
			if (error instanceof StopIteration) {
				if (position !== null) {
					result.push({
						contents: contents.join(''),
						newline,
					});
				}

				return result;
			}

			throw error;
		}
	}
};

const t = ast.transform;

const tailRecurse = async function* (tree, match, ctx) {
	while (tree.type === 'node') {
		let n = tree.children.length;
		assert(Number.isInteger(n));
		if (n === 0) return;
		const it = iter(tree.children);
		while (--n !== 0) {
			const child = await next(it);
			yield child.type === 'leaf' ? child : await t(child, match, ctx);
		}

		tree = await next(it);
	}

	yield tree;
};

const prunefn = async (tree) => ({
	type: 'leaf',
	terminal: tree.nonterminal,
	lines: await lines(tree),
});

const prune = (...keys) =>
	Object.fromEntries(keys.map((key) => [key, prunefn]));

const simplify = extend(visitor, {
	documents: {
		add: (tree, match, ctx) => ({
			type: 'node',
			nonterminal: 'documents',
			production: 'main',
			children: tailRecurse(tree, match, ctx),
		}),
	},
	'A*': {
		add: (tree, match, ctx) => ({
			type: 'node',
			nonterminal: 'A*',
			production: 'main',
			children: tailRecurse(tree, match, ctx),
		}),
	},
	'R*': {
		add: (tree, match, ctx) => ({
			type: 'node',
			nonterminal: 'R*',
			production: 'main',
			children: tailRecurse(tree, match, ctx),
		}),
	},
	'doctor-nihdi': prune('0'),
	'doctor-name': prune('0'),
	'doctor-address': prune('0'),
	'doctor-phone': prune('0'),
	'doctor-extra': prune('0'),
	'lab-identifier': prune('0'),
	'lab-name': prune('0'),
	'lab-address': prune('0'),
	'lab-extra': prune('0'),
	date: prune('0'),
	'requestor-nihdi': prune('0'),
	'requestor-name': prune('0'),
	'#A': prune('0'),
	'A-name': prune('0'),
	'A-birthdate': prune('0'),
	'A-sex': prune('0'),
	'A-date': prune('0'),
	'A-reference': prune('0'),
	'A-code': prune('0'),
	'A-extra': prune('0'),
	'#A/': prune('0'),
	'#R': prune('0'),
	'R-title': prune('free', 'code'),
	'R-body': prune('0'),
	'#R/': prune('0'),
	footer: prune('0'),
});

export default simplify;