import { AndRule, type DataRef, LiteralRule, RepetitionRule, type Rule, type RuleTestResponse, } from '@os-team/lexical-rules'; import type { Parser } from './Parser.js'; import CpParser, { type Cp } from './CpParser.js'; import SRule from '../rules/SRule.js'; export interface Seq { type: 'SEQ'; items: Cp[]; } /** * See https://www.w3.org/TR/xml/#NT-seq */ class SeqParser implements Parser { private readonly cpParser: CpParser; private readonly rule: Rule< [ string, undefined, Cp, Array<[undefined, string, undefined, Cp]>, undefined, string, ] >; public constructor() { const prefixRule = new LiteralRule('('); // '(' const anySRule = new SRule('*'); // S? this.cpParser = new CpParser(); // cp const separatorRule = new LiteralRule(','); // ',' const moreCpRule = new AndRule([ anySRule, separatorRule, anySRule, this.cpParser, ]); // S? ',' S? cp const anyMoreCpRule = new RepetitionRule(moreCpRule, 0); // ( S? ',' S? cp )* const suffixRule = new LiteralRule(')'); // ')' this.rule = new AndRule([ prefixRule, anySRule, this.cpParser, anyMoreCpRule, anySRule, suffixRule, ]); // '(' S? cp ( S? ',' S? cp )* S? ')' } public test(ref: DataRef, pos: number): RuleTestResponse { const [isValid, nextPos, res] = this.rule.test(ref, pos); if (!isValid || res === undefined) return [false, nextPos]; const [, , cp, moreCp] = res; const seq: Seq = { type: 'SEQ', items: [cp, ...moreCp.map((item) => item[3])], }; return [true, nextPos, seq]; } public build(data: Seq) { return `(${data.items.map((item) => this.cpParser.build(item)).join(',')})`; } } export default SeqParser;