import { AndRule, type DataRef, MaybeRule, RepetitionRule, type Rule, type RuleTestResponse, } from '@os-team/lexical-rules'; import type { Parser } from './Parser.js'; import XMLDeclParser, { type XMLDecl } from './XMLDeclParser.js'; import MiscParser, { type Misc } from './MiscParser.js'; import DoctypeDeclParser, { type DoctypeDecl } from './DoctypeDeclParser.js'; export interface Prolog { xml?: XMLDecl; misc?: Misc[]; doctype?: DoctypeDecl; } /** * The prolog. * See https://www.w3.org/TR/xml/#NT-prolog */ class PrologParser implements Parser { private readonly xmlDeclParser: XMLDeclParser; private readonly miscParser: MiscParser; private readonly doctypeDeclParser: DoctypeDeclParser; private rule: Rule< [XMLDecl | undefined, Misc[], [DoctypeDecl, Misc[]] | undefined] >; public constructor() { this.xmlDeclParser = new XMLDeclParser(); // XMLDecl const maybeXMLDeclRule = new MaybeRule(this.xmlDeclParser); // XMLDecl? this.miscParser = new MiscParser(); // Misc const anyMiscRule = new RepetitionRule(this.miscParser, 0); // Misc* this.doctypeDeclParser = new DoctypeDeclParser(); // doctypedecl const doctypeAndAnyMiscRule = new AndRule([ this.doctypeDeclParser, anyMiscRule, ]); // doctypedecl Misc* const maybeDoctypeAndAnyMiscRule = new MaybeRule(doctypeAndAnyMiscRule); // (doctypedecl Misc*)? this.rule = new AndRule([ maybeXMLDeclRule, anyMiscRule, maybeDoctypeAndAnyMiscRule, ]); // XMLDecl? Misc* (doctypedecl Misc*)? } public test(ref: DataRef, pos: number): RuleTestResponse { const [isValid, nextPos, res] = this.rule.test(ref, pos); if (!isValid || res === undefined) return [false, nextPos]; const [xml, misc, rawDoctype] = res; const prolog: Prolog = {}; if (xml) prolog.xml = xml; if (misc.length > 0) prolog.misc = misc.filter((item) => item !== undefined); if (rawDoctype) { const [doctype, secondMisc] = rawDoctype; prolog.doctype = doctype; if (secondMisc.length > 0) { prolog.misc = [ ...(prolog.misc || []), ...secondMisc.filter((item) => item !== undefined), ]; } } return [true, nextPos, prolog]; } public build(data: Prolog) { const xml = data.xml ? this.xmlDeclParser.build(data.xml) : ''; const misc = data.misc ? data.misc.reduce( (acc, item) => `${acc}${this.miscParser.build(item)}`, '' ) : ''; const doctype = data.doctype ? this.doctypeDeclParser.build(data.doctype) : ''; return `${xml}${misc}${doctype}`; } } export default PrologParser;