import { AndRule, type DataRef, LiteralRule, MaybeRule, type Rule, type RuleTestResponse, } from '@os-team/lexical-rules'; import type { Parser } from './Parser.js'; import SRule from '../rules/SRule.js'; import PITargetRule from '../rules/PITargetRule.js'; import AnyCharRule from '../rules/AnyCharRule.js'; import CharRule from '../rules/CharRule.js'; export type PI = ['PI', string] | ['PI', string, string]; // [, target, content] /** * The processing instruction. * See https://www.w3.org/TR/xml/#NT-PI */ class PIParser implements Parser { private readonly rule: Rule< [string, string, [undefined, string] | undefined, string] >; public constructor() { const prefixRule = new LiteralRule('']); // Char* - (Char* '?>' Char*) const sAndAnyCharRule = new AndRule([sRule, anyCharRule]); // S (Char* - (Char* '?>' Char*)) const piContentRule = new MaybeRule(sAndAnyCharRule); // (S (Char* - (Char* '?>' Char*)))? const suffixRule = new LiteralRule('?>'); // '?>' this.rule = new AndRule([ prefixRule, piTargetRule, piContentRule, suffixRule, ]); // '' Char*)))? '?>' } public test(ref: DataRef, pos: number): RuleTestResponse { const [isValid, nextPos, res] = this.rule.test(ref, pos); if (!isValid || res === undefined) return [false, nextPos]; const [, target, parsedContent] = res; const pi: PI = ['PI', target]; if (Array.isArray(parsedContent)) pi.push(parsedContent[1]); return [true, nextPos, pi]; } public build(data: PI) { const [, target, content = ''] = data; if (target.toLowerCase() === 'xml') { throw new Error( 'The target of the processing instruction should not be "xml"' ); } if (content.includes('?>')) { throw new Error( 'The content of the processing instruction should not contain "?>"' ); } return ``; } } export default PIParser;