import { AndRule, type DataRef, LiteralRule, MaybeRule, OrRule, type Rule, type RuleTestResponse, } from '@os-team/lexical-rules'; import AttValueRule from '../rules/AttValueRule.js'; import SRule from '../rules/SRule.js'; import type { Parser } from './Parser.js'; export interface RequiredDefaultDecl { type: 'REQUIRED'; } export interface ImpliedDefaultDecl { type: 'IMPLIED'; } export interface FixedDefaultDecl { type: 'FIXED'; attValue: string; } export type DefaultDecl = | RequiredDefaultDecl | ImpliedDefaultDecl | FixedDefaultDecl; /** * The attribute default declaration. * See https://www.w3.org/TR/xml/#NT-DefaultDecl */ class DefaultDeclParser implements Parser { private readonly rule: Rule< string | [[string, undefined] | undefined, string] >; public constructor() { const requiredRule = new LiteralRule('#REQUIRED'); // '#REQUIRED' const impliedRule = new LiteralRule('#IMPLIED'); // '#IMPLIED' const fixedRule = new LiteralRule('#FIXED'); // '#FIXED' const sRule = new SRule('+'); // S const fixedWithSRule = new AndRule([fixedRule, sRule]); // '#FIXED' S const maybeFixedWithSRule = new MaybeRule(fixedWithSRule); // ('#FIXED' S)? const attValueRule = new AttValueRule(); // AttValue const fixedAttValueRule = new AndRule([maybeFixedWithSRule, attValueRule]); // ('#FIXED' S)? AttValue this.rule = new OrRule([requiredRule, impliedRule, fixedAttValueRule]); // '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue) } public test(ref: DataRef, pos: number): RuleTestResponse { const [isValid, nextPos, res] = this.rule.test(ref, pos); if (!isValid || res === undefined) return [false, nextPos]; const defaultDecl: DefaultDecl = Array.isArray(res) ? { type: 'FIXED', attValue: res[1] } : { type: res === '#REQUIRED' ? 'REQUIRED' : 'IMPLIED' }; return [true, nextPos, defaultDecl]; } public build(data: DefaultDecl) { return `#${data.type}${ data.type === 'FIXED' ? ` "${data.attValue.replaceAll('"', "'")}"` : '' }`; } } export default DefaultDeclParser;