import { AndRule, RepetitionRule, type Rule, type RuleTestResponse, } from '@os-team/lexical-rules'; import type { DataWithOptionsRef, Parser } from './Parser.js'; import PrologParser, { type Prolog } from './PrologParser.js'; import MiscParser, { type Misc } from './MiscParser.js'; import ElementParser, { type Element } from './ElementParser.js'; export type Document = Element & { '#prolog'?: Prolog; }; /** * The document. * See https://www.w3.org/TR/xml/#NT-document */ class DocumentParser implements Parser { private readonly prologParser: PrologParser; private readonly elementParser: ElementParser; private readonly miscParser: MiscParser; private readonly rule: Rule<[Prolog, Element, Misc[]]>; public constructor() { this.prologParser = new PrologParser(); // prolog this.elementParser = new ElementParser(); // element this.miscParser = new MiscParser(); // Misc const anyMiscRule = new RepetitionRule(this.miscParser, 0); // Misc* this.rule = new AndRule([ this.prologParser, this.elementParser, anyMiscRule, ]); // prolog element Misc* } public test( ref: DataWithOptionsRef, pos: number ): RuleTestResponse { const { include = '' } = ref.options || {}; const [isValid, nextPos, res] = this.rule.test(ref, pos); if (!isValid || res === undefined) return [false, nextPos]; const [prolog, element, misc] = res; const document: Document = element; if (['PROLOG', 'ALL'].includes(include)) { misc.forEach((item) => { if (!item) return; const [, target, content = ''] = item; document[`?${target}`] = content; }); } if (['PROLOG', 'ALL'].includes(include) && Object.keys(prolog).length > 0) { document['#prolog'] = prolog; } return [true, nextPos, document]; } public build(data: Document) { const { '#prolog': prolog, ...element } = data; const strProlog = prolog ? this.prologParser.build(prolog) : ''; let strElement = ''; let strMisc = ''; Object.entries(element).forEach(([key, value]) => { if (key[0] === '?' && typeof value === 'string') { strMisc = `${strMisc}${this.miscParser.build([ 'PI', key.slice(1), value, ])}`; } else if (!strElement) { strElement = this.elementParser.build({ [key]: value }); } else { throw new Error('The root element must contain only one element'); } }); return `${strProlog}${strElement}${strMisc}`; } } export default DocumentParser;