diff --git a/src/xml/mod.rs b/src/xml/mod.rs
index 0e84155..2d20ca0 100644
--- a/src/xml/mod.rs
+++ b/src/xml/mod.rs
@@ -4,6 +4,7 @@ use crate::error::Error;
pub mod composers;
pub mod parsers;
+pub mod parsers_complete;
/// [1] NSAttName ::= PrefixedAttName | DefaultAttName
#[derive(Clone, Debug)]
diff --git a/src/xml/parsers_complete.rs b/src/xml/parsers_complete.rs
new file mode 100644
index 0000000..fff8bf2
--- /dev/null
+++ b/src/xml/parsers_complete.rs
@@ -0,0 +1,1522 @@
+use nom::{
+ branch::alt,
+ bytes::complete::{is_a, is_not, tag, take, take_while},
+ character::complete::{char, none_of, one_of, satisfy},
+ combinator::{map, not, opt, peek, recognize, value},
+ error::{Error, ErrorKind},
+ multi::{many0, many1, many_till},
+ sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
+ Err, IResult, Parser as NomParser,
+};
+
+use crate::xml::NSAttName;
+
+use super::{
+ AttDef, AttDefName, AttType, AttValue, AttValueData, AttlistDecl, Attribute, CDEnd, CDSect,
+ CDStart, CData, Char, CharData, CharRef, Children, ChildrenKind, Choice, Comment,
+ ConditionalSect, Content, ContentItem, Contentspec, Cp, CpKind, DeclSep, DefaultAttName,
+ DefaultDecl, DoctypeDecl, Document, ETag, Element, Elementdecl, EmptyElemTag, EncName,
+ EncodingDecl, EntityDecl, EntityDef, EntityRef, EntityValue, EntityValueData, EnumeratedType,
+ Enumeration, Eq, ExtParsedEnt, ExtSubset, ExtSubsetDecl, ExtSubsetDeclaration, ExternalID,
+ GEDecl, Ignore, IgnoreSect, IgnoreSectContents, IncludeSect, IntSubset, IntSubsetDeclaration,
+ LocalPart, MarkupDecl, Misc, Mixed, NCName, NDataDecl, Name, NameChar, NameStartChar, Names,
+ Nmtoken, Nmtokens, NotationDecl, NotationDeclID, NotationType, Occurence, PEDecl, PEDef,
+ PEReference, PITarget, Prefix, PrefixedAttName, PrefixedName, Prolog, PubidChar, PubidLiteral,
+ PublicID, QName, Reference, SDDecl, STag, Seq, StringType, SystemLiteral, TextDecl,
+ TokenizedType, UnprefixedName, VersionInfo, VersionNum, XMLDecl, PI, S,
+};
+
+pub trait Parser<'s, T> {
+ fn parse(input: &'s str) -> IResult<&str, T>;
+}
+
+/// [1] NSAttName ::= PrefixedAttName | DefaultAttName
+impl<'s> Parser<'s, NSAttName<'s>> for NSAttName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, NSAttName<'s>> {
+ alt((
+ map(PrefixedAttName::parse, |prefixed_att_name| {
+ NSAttName::PrefixedAttName(prefixed_att_name)
+ }),
+ value(NSAttName::DefaultAttName, DefaultAttName::parse),
+ ))(input)
+ }
+}
+
+/// [2] PrefixedAttName ::= 'xmlns:' NCName
+impl<'s> Parser<'s, PrefixedAttName<'s>> for PrefixedAttName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PrefixedAttName<'s>> {
+ map(preceded(tag("xmlns:"), NCName::parse), |nc_name| {
+ PrefixedAttName(nc_name)
+ })(input)
+ }
+}
+
+/// [3] DefaultAttName ::= 'xmlns';
+impl Parser<'_, DefaultAttName> for DefaultAttName {
+ fn parse(input: &str) -> IResult<&str, DefaultAttName> {
+ value(DefaultAttName, tag("xmlns"))(input)
+ }
+}
+
+/// [5] Name ::= NameStartChar (NameChar)*
+/// [4] NCName ::= Name - (Char* ':' Char*)
+impl<'s> Parser<'s, NCName<'s>> for NCName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, NCName<'s>> {
+ let (rest, name) = peek(recognize(Name::parse))(input)?;
+ if let Some(char) = name.find(':') {
+ map(take(char), |nc_name| NCName(nc_name))(input)
+ } else {
+ map(recognize(Name::parse), |nc_name| NCName(nc_name))(input)
+ }
+ }
+}
+
+/// [7] QName ::= PrefixedName | UnprefixedName
+impl<'s> Parser<'s, QName<'s>> for QName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, QName<'s>> {
+ alt((
+ map(PrefixedName::parse, |prefixed_name| {
+ QName::PrefixedName(prefixed_name)
+ }),
+ map(UnprefixedName::parse, |unprefixed_name| {
+ QName::UnprefixedName(unprefixed_name)
+ }),
+ ))(input)
+ }
+}
+
+/// [8] PrefixedName ::= Prefix ':' LocalPart
+impl<'s> Parser<'s, PrefixedName<'s>> for PrefixedName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PrefixedName<'s>> {
+ map(
+ separated_pair(Prefix::parse, char(':'), LocalPart::parse),
+ |(prefix, local_part)| PrefixedName { prefix, local_part },
+ )(input)
+ }
+}
+
+/// [9] UnprefixedName ::= LocalPart
+impl<'s> Parser<'s, UnprefixedName<'s>> for UnprefixedName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, UnprefixedName<'s>> {
+ map(LocalPart::parse, |local_part| UnprefixedName(local_part))(input)
+ }
+}
+
+/// [10] Prefix ::= NCName
+impl<'s> Parser<'s, Prefix<'s>> for Prefix<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Prefix<'s>> {
+ map(NCName::parse, |nc_name| Prefix(nc_name))(input)
+ }
+}
+
+/// [11] LocalPart ::= NCName
+impl<'s> Parser<'s, LocalPart<'s>> for LocalPart<'s> {
+ fn parse(input: &'s str) -> IResult<&str, LocalPart<'s>> {
+ map(NCName::parse, |nc_name| LocalPart(nc_name))(input)
+ }
+}
+
+// xml spec
+
+/// [1] document ::= prolog element Misc*
+impl<'s> Parser<'s, Document<'s>> for Document<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Document<'s>> {
+ tuple((Prolog::parse, Element::parse, many0(Misc::parse)))(input)
+ }
+}
+
+/// [2] Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
+impl Parser<'_, Char> for Char {
+ fn parse(input: &str) -> IResult<&str, Char> {
+ map(
+ satisfy(
+ |c| matches!(c, '\u{9}' | '\u{A}' | '\u{D}' | '\u{20}'..='\u{D7FF}' | '\u{E000}'..='\u{FFFD}' | '\u{10000}'..='\u{10FFFF}'),
+ ),
+ |char| Char(char),
+ )(input)
+ }
+}
+
+/// [3] S ::= (#x20 | #x9 | #xD | #xA)+
+impl Parser<'_, S> for S {
+ fn parse(input: &str) -> IResult<&str, S> {
+ // TODO?: whitespacing
+ // map(is_a("\u{20}\u{9}\u{D}\u{A}"), |s| S(s))(input)
+ value(S, is_a("\u{20}\u{9}\u{D}\u{A}"))(input)
+ }
+}
+
+/// [4] NameStartChar ::= ":" | [A-Z] | "_" | [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] | [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
+impl Parser<'_, NameStartChar> for NameStartChar {
+ fn parse(input: &str) -> IResult<&str, NameStartChar> {
+ map(
+ satisfy(
+ |c| matches!(c, ':' | 'A'..='Z' | '_' | 'a'..='z' | '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' | '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' | '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' | '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' | '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' | '\u{10000}'..='\u{EFFFF}'),
+ ),
+ |c| NameStartChar(c),
+ )(input)
+ }
+}
+
+/// [4a] NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
+impl Parser<'_, NameChar> for NameChar {
+ fn parse(input: &str) -> IResult<&str, NameChar> {
+ map(
+ alt((
+ map(NameStartChar::parse, |NameStartChar(c)| c),
+ satisfy(
+ |c| matches!(c, '-' | '.' | '0'..='9' | '\u{B7}' | '\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}'),
+ ),
+ )),
+ |c| NameChar(c),
+ )(input)
+ }
+}
+
+/// [5] Name ::= NameStartChar (NameChar)*
+impl<'s> Parser<'s, Name<'s>> for Name<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Name<'s>> {
+ map(
+ recognize(pair(NameStartChar::parse, many0(NameChar::parse))),
+ |name| Name(name),
+ )(input)
+ }
+}
+
+/// [6] Names ::= Name (#x20 Name)*
+impl<'s> Parser<'s, Names<'s>> for Names<'s> {
+ // TODO: fix
+ fn parse(input: &'s str) -> IResult<&str, Names<'s>> {
+ map(
+ pair(Name::parse, many0(preceded(char('\u{20}'), Name::parse))),
+ |(head, tail)| Names(vec![vec![head], tail].concat()),
+ )(input)
+ }
+}
+
+/// [7] Nmtoken ::= (NameChar)+
+impl<'s> Parser<'s, Nmtoken<'s>> for Nmtoken<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Nmtoken<'s>> {
+ map(recognize(many1(NameChar::parse)), |nmtoken| {
+ Nmtoken(nmtoken)
+ })(input)
+ }
+}
+
+/// [8] Nmtokens ::= Nmtoken (#x20 Nmtoken)*
+impl<'s> Parser<'s, Nmtokens<'s>> for Nmtokens<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Nmtokens<'s>> {
+ map(
+ pair(
+ Nmtoken::parse,
+ many0(preceded(char('\u{20}'), Nmtoken::parse)),
+ ),
+ |(head, tail)| Nmtokens(vec![vec![head], tail].concat()),
+ )(input)
+ }
+}
+
+/// [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"'
+/// | "'" ([^%&'] | PEReference | Reference)* "'"
+impl<'s> Parser<'s, EntityValue<'s>> for EntityValue<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EntityValue<'s>> {
+ alt((
+ map(
+ delimited(
+ char('"'),
+ many0(alt((
+ map(
+ recognize(many_till(take(1usize), peek(one_of("%&\"")))),
+ |string| EntityValueData::String(string),
+ ),
+ map(PEReference::parse, |pe_reference| {
+ EntityValueData::PEReference(pe_reference)
+ }),
+ map(Reference::parse, |reference| {
+ EntityValueData::Reference(reference)
+ }),
+ ))),
+ char('"'),
+ ),
+ |entity_value| EntityValue::DoubleQuoted(entity_value),
+ ),
+ map(
+ delimited(
+ char('\''),
+ many0(alt((
+ map(
+ recognize(many_till(take(1usize), peek(one_of("%&'")))),
+ |string| EntityValueData::String(string),
+ ),
+ map(PEReference::parse, |pe_reference| {
+ EntityValueData::PEReference(pe_reference)
+ }),
+ map(Reference::parse, |reference| {
+ EntityValueData::Reference(reference)
+ }),
+ ))),
+ char('\''),
+ ),
+ |entity_value| EntityValue::SingleQuoted(entity_value),
+ ),
+ ))(input)
+ }
+}
+
+/// [10] AttValue ::= '"' ([^<&"] | Reference)* '"'
+/// | "'" ([^<&'] | Reference)* "'"
+impl<'s> Parser<'s, AttValue<'s>> for AttValue<'s> {
+ fn parse(input: &'s str) -> IResult<&str, AttValue<'s>> {
+ alt((
+ map(
+ delimited(
+ char('"'),
+ many0(alt((
+ map(is_not("<&\""), |string| AttValueData::String(string)),
+ map(Reference::parse, |reference| {
+ AttValueData::Reference(reference)
+ }),
+ ))),
+ char('"'),
+ ),
+ |att_value| AttValue::DoubleQuoted(att_value),
+ ),
+ map(
+ delimited(
+ char('\''),
+ many0(alt((
+ map(is_not("<&'"), |string| AttValueData::String(string)),
+ map(Reference::parse, |reference| {
+ AttValueData::Reference(reference)
+ }),
+ ))),
+ char('\''),
+ ),
+ |att_value| AttValue::SingleQuoted(att_value),
+ ),
+ ))(input)
+ }
+}
+
+/// [11] SystemLiteral ::= ('"' [^"]* '"') | ("'" [^']* "'")
+impl<'s> Parser<'s, SystemLiteral<'s>> for SystemLiteral<'s> {
+ fn parse(input: &'s str) -> IResult<&str, SystemLiteral<'s>> {
+ alt((
+ map(
+ delimited(char('"'), recognize(many0(none_of("\""))), char('"')),
+ |system_literal| SystemLiteral::DoubleQuoted(system_literal),
+ ),
+ map(
+ delimited(char('\''), recognize(many0(none_of("'"))), char('\'')),
+ |system_literal| SystemLiteral::SingleQuoted(system_literal),
+ ),
+ ))(input)
+ }
+}
+
+/// [12] PubidLiteral ::= '"' PubidChar* '"' | "'" (PubidChar - "'")* "'"
+impl<'s> Parser<'s, PubidLiteral<'s>> for PubidLiteral<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PubidLiteral<'s>> {
+ alt((
+ map(
+ delimited(char('"'), recognize(many0(PubidChar::parse)), char('"')),
+ |pubid_literal| PubidLiteral::DoubleQuoted(pubid_literal),
+ ),
+ map(
+ delimited(
+ char('\''),
+ recognize(many0(recognize(not(char('\''))).and_then(PubidChar::parse))),
+ char('\''),
+ ),
+ |pubid_literal| PubidLiteral::SingleQuoted(pubid_literal),
+ ),
+ ))(input)
+ }
+}
+
+/// [13] PubidChar ::= #x20 | #xD | #xA | [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%]
+impl Parser<'_, PubidChar> for PubidChar {
+ fn parse(input: &'_ str) -> IResult<&str, PubidChar> {
+ map(
+ satisfy(
+ |c| matches!(c, '\u{20}' | '\u{D}' | '\u{A}' | 'a'..='z' | 'A'..='Z' | '0'..='9'),
+ ),
+ |pubid_char| PubidChar(pubid_char),
+ )(input)
+ }
+}
+
+/// [14] CharData ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
+impl<'s> Parser<'s, CharData<'s>> for CharData<'s> {
+ fn parse(input: &'s str) -> IResult<&str, CharData<'s>> {
+ map(
+ recognize(many_till(
+ none_of("<&"),
+ peek(alt((recognize(one_of("<&")), tag("]]>")))),
+ )),
+ |char_data| CharData(char_data),
+ )(input)
+ }
+}
+
+/// Comment ::= ''
+impl<'s> Parser<'s, Comment<'s>> for Comment<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Comment<'s>> {
+ map(
+ delimited(
+ tag(""),
+ ),
+ |comment| Comment(comment),
+ )(input)
+ }
+}
+
+/// [16] PI ::= '' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
+impl<'s> Parser<'s, PI<'s>> for PI<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PI<'s>> {
+ map(
+ delimited(
+ tag(""),
+ pair(
+ PITarget::parse,
+ opt(recognize(pair(
+ S::parse,
+ many_till(Char::parse, peek(tag("?>"))),
+ ))),
+ ),
+ tag("?>"),
+ ),
+ |(target, instruction)| PI {
+ target,
+ instruction,
+ },
+ )(input)
+ }
+}
+
+/// [17] PITarget ::= Name - (('X' | 'x') ('M' | 'm') ('L' | 'l'))
+impl<'s> Parser<'s, PITarget<'s>> for PITarget<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PITarget<'s>> {
+ let (rest, name) = Name::parse(input)?;
+ if name.0.to_lowercase() == "xml" {
+ return Err(Err::Error(Error {
+ input,
+ // TODO: check if better error to return
+ code: ErrorKind::Tag,
+ }));
+ } else {
+ return Ok((rest, PITarget(name)));
+ }
+ }
+}
+
+/// [18] CDSect ::= CDStart CData CDEnd
+impl<'s> Parser<'s, CDSect<'s>> for CDSect<'s> {
+ fn parse(input: &'s str) -> IResult<&str, CDSect<'s>> {
+ map(
+ delimited(CDStart::parse, CData::parse, CDEnd::parse),
+ |c_data| CDSect(c_data),
+ )(input)
+ }
+}
+
+/// [19] CDStart ::= ' for CDStart {
+ fn parse(input: &'_ str) -> IResult<&str, CDStart> {
+ value(CDStart, tag("' Char*))
+impl<'s> Parser<'s, CData<'s>> for CData<'s> {
+ fn parse(input: &'s str) -> IResult<&str, CData<'s>> {
+ map(
+ recognize(many_till(Char::parse, peek(tag("]]>")))),
+ |c_data| CData(c_data),
+ )(input)
+ }
+}
+
+/// [21] CDEnd ::= ']]>'
+impl Parser<'_, CDEnd> for CDEnd {
+ fn parse(input: &'_ str) -> IResult<&str, CDEnd> {
+ value(CDEnd, tag("]]>"))(input)
+ }
+}
+
+/// [22] prolog ::= XMLDecl? Misc* (doctypedecl Misc*)?
+impl<'s> Parser<'s, Prolog<'s>> for Prolog<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Prolog<'s>> {
+ tuple((
+ opt(XMLDecl::parse),
+ many0(Misc::parse),
+ opt(tuple((DoctypeDecl::parse, many0(Misc::parse)))),
+ ))(input)
+ }
+}
+
+/// [23] XMLDecl ::= ''
+impl<'s> Parser<'s, XMLDecl<'s>> for XMLDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, XMLDecl<'s>> {
+ map(
+ delimited(
+ tag("")),
+ ),
+ |(version_info, encoding_decl, sd_decl)| XMLDecl {
+ version_info,
+ encoding_decl,
+ sd_decl,
+ },
+ )(input)
+ }
+}
+
+/// [24] VersionInfo ::= S 'version' Eq ("'" VersionNum "'" | '"' VersionNum '"')
+impl Parser<'_, VersionInfo> for VersionInfo {
+ fn parse(input: &'_ str) -> IResult<&str, VersionInfo> {
+ preceded(
+ tuple((S::parse, tag("version"), Eq::parse)),
+ alt((
+ map(
+ delimited(char('\''), VersionNum::parse, char('\'')),
+ |version_info| VersionInfo::SingleQuoted(version_info),
+ ),
+ map(
+ delimited(char('"'), VersionNum::parse, char('"')),
+ |version_info| VersionInfo::DoubleQuoted(version_info),
+ ),
+ )),
+ )(input)
+ }
+}
+
+/// [25] Eq ::= S? '=' S?
+impl Parser<'_, Eq> for Eq {
+ fn parse(input: &'_ str) -> IResult<&str, Eq> {
+ value(
+ Eq,
+ recognize(tuple((opt(S::parse), char('='), opt(S::parse)))),
+ )(input)
+ }
+}
+
+/// [26] VersionNum ::= '1.' [0-9]+
+impl Parser<'_, VersionNum> for VersionNum {
+ fn parse(input: &'_ str) -> IResult<&str, VersionNum> {
+ preceded(
+ tag("1."),
+ alt((
+ value(VersionNum::One, char('0')),
+ value(VersionNum::OneDotOne, char('1')),
+ )),
+ )(input)
+ }
+}
+
+/// [27] Misc ::= Comment | PI | S
+impl<'s> Parser<'s, Misc<'s>> for Misc<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Misc<'s>> {
+ alt((
+ map(Comment::parse, |comment| Misc::Comment(comment)),
+ map(PI::parse, |pi| Misc::PI(pi)),
+ value(Misc::S, S::parse),
+ ))(input)
+ }
+}
+
+/// [16] doctypedecl ::= ''
+/// [28] doctypedecl ::= ''
+impl<'s> Parser<'s, DoctypeDecl<'s>> for DoctypeDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, DoctypeDecl<'s>> {
+ map(
+ delimited(
+ pair(tag(""),
+ ),
+ |(name, external_id, int_subset)| DoctypeDecl {
+ name,
+ external_id,
+ int_subset,
+ },
+ )(input)
+ }
+}
+
+/// [28a] DeclSep ::= PEReference | S
+impl<'s> Parser<'s, DeclSep<'s>> for DeclSep<'s> {
+ fn parse(input: &'s str) -> IResult<&str, DeclSep<'s>> {
+ alt((
+ map(PEReference::parse, |pe_reference| {
+ DeclSep::PEReference(pe_reference)
+ }),
+ value(DeclSep::S, S::parse),
+ ))(input)
+ }
+}
+
+/// [28b] intSubset ::= (markupdecl | DeclSep)*
+impl<'s> Parser<'s, IntSubset<'s>> for IntSubset<'s> {
+ fn parse(input: &'s str) -> IResult<&str, IntSubset<'s>> {
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ IntSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ IntSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ )))(input)
+ }
+}
+
+/// [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment
+impl<'s> Parser<'s, MarkupDecl<'s>> for MarkupDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, MarkupDecl<'s>> {
+ alt((
+ map(Elementdecl::parse, |elementdecl| {
+ MarkupDecl::Elementdecl(elementdecl)
+ }),
+ map(AttlistDecl::parse, |attlist_decl| {
+ MarkupDecl::AttlistDecl(attlist_decl)
+ }),
+ map(EntityDecl::parse, |entity_decl| {
+ MarkupDecl::EntityDecl(entity_decl)
+ }),
+ map(NotationDecl::parse, |notation_decl| {
+ MarkupDecl::NotationDecl(notation_decl)
+ }),
+ map(PI::parse, |pi| MarkupDecl::PI(pi)),
+ map(Comment::parse, |comment| MarkupDecl::Comment(comment)),
+ ))(input)
+ }
+}
+
+/// [30] extSubset ::= TextDecl? extSubsetDecl
+impl<'s> Parser<'s, ExtSubset<'s>> for ExtSubset<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ExtSubset<'s>> {
+ map(
+ pair(opt(TextDecl::parse), ExtSubsetDecl::parse),
+ |(text_decl, ext_subset_decl)| ExtSubset {
+ text_decl,
+ ext_subset_decl,
+ },
+ )(input)
+ }
+}
+
+/// [31] extSubsetDecl ::= ( markupdecl | conditionalSect | DeclSep)*
+impl<'s> Parser<'s, ExtSubsetDecl<'s>> for ExtSubsetDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ExtSubsetDecl<'s>> {
+ many0(alt((
+ map(MarkupDecl::parse, |markup_decl| {
+ ExtSubsetDeclaration::MarkupDecl(markup_decl)
+ }),
+ map(ConditionalSect::parse, |conditional_sect| {
+ ExtSubsetDeclaration::ConditionalSect(conditional_sect)
+ }),
+ map(DeclSep::parse, |decl_sep| {
+ ExtSubsetDeclaration::DeclSep(decl_sep)
+ }),
+ )))(input)
+ }
+}
+
+/// [32] SDDecl ::= S 'standalone' Eq (("'" ('yes' | 'no') "'") | ('"' ('yes' | 'no') '"'))
+impl Parser<'_, SDDecl> for SDDecl {
+ fn parse(input: &'_ str) -> IResult<&str, SDDecl> {
+ preceded(
+ tuple((S::parse, tag("standalone"), Eq::parse)),
+ alt((
+ delimited(
+ char('\''),
+ alt((
+ value(SDDecl::SingleQuoted(true), tag("yes")),
+ value(SDDecl::SingleQuoted(false), tag("no")),
+ )),
+ char('\''),
+ ),
+ delimited(
+ char('"'),
+ alt((
+ value(SDDecl::DoubleQuoted(true), tag("yes")),
+ value(SDDecl::DoubleQuoted(false), tag("no")),
+ )),
+ char('"'),
+ ),
+ )),
+ )(input)
+ }
+}
+
+// (Productions 33 through 38 have been removed.)
+
+/// [39] element ::= EmptyElemTag | STag content ETag
+impl<'s> Parser<'s, Element<'s>> for Element<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Element<'s>> {
+ alt((
+ map(EmptyElemTag::parse, |empty_elem_tag| {
+ Element::Empty(empty_elem_tag)
+ }),
+ map(
+ tuple((STag::parse, Content::parse, ETag::parse)),
+ |(s_tag, content, e_tag)| Element::NotEmpty(s_tag, content, e_tag),
+ ),
+ ))(input)
+ }
+}
+
+/// [12] STag ::= '<' QName (S Attribute)* S? '>'
+/// [40] STag ::= '<' Name (S Attribute)* S? '>'
+impl<'s> Parser<'s, STag<'s>> for STag<'s> {
+ fn parse(input: &'s str) -> IResult<&str, STag<'s>> {
+ map(
+ delimited(
+ tag("<"),
+ pair(QName::parse, many0(preceded(S::parse, Attribute::parse))),
+ pair(opt(S::parse), tag(">")),
+ ),
+ |(name, attributes)| STag { name, attributes },
+ )(input)
+ }
+}
+
+/// [15] Attribute ::= NSAttName Eq AttValue | QName Eq AttValue
+impl<'s> Parser<'s, Attribute<'s>> for Attribute<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Attribute<'s>> {
+ alt((
+ map(
+ separated_pair(NSAttName::parse, Eq::parse, AttValue::parse),
+ |(ns_name, value)| Attribute::NamespaceDeclaration { ns_name, value },
+ ),
+ map(
+ separated_pair(QName::parse, Eq::parse, AttValue::parse),
+ |(name, value)| Attribute::Attribute { name, value },
+ ),
+ ))(input)
+ }
+}
+// pub type Attribute<'s> = (Name<'s>, AttValue<'s>);
+/// [41] Attribute ::= Name Eq AttValue
+// pub fn attribute(input: &str) -> IResult<&str, Attribute> {
+// separated_pair(name, eq, att_value)(input)
+// }
+
+/// [13] ETag ::= '' QName S? '>'
+/// [42] ETag ::= '' Name S? '>'
+impl<'s> Parser<'s, ETag<'s>> for ETag<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ETag<'s>> {
+ map(
+ delimited(tag(""), QName::parse, pair(opt(S::parse), tag(">"))),
+ |name| ETag { name },
+ )(input)
+ }
+}
+
+impl<'s> Parser<'s, ContentItem<'s>> for ContentItem<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ContentItem<'s>> {
+ alt((
+ map(Element::parse, |element| ContentItem::Element(element)),
+ map(Reference::parse, |reference| {
+ ContentItem::Reference(reference)
+ }),
+ map(CDSect::parse, |cd_sect| ContentItem::CDSect(cd_sect)),
+ map(PI::parse, |pi| ContentItem::PI(pi)),
+ map(Comment::parse, |comment| ContentItem::Comment(comment)),
+ ))(input)
+ }
+}
+
+/// [43] content ::= CharData? ((element | Reference | CDSect | PI | Comment) CharData?)*
+impl<'s> Parser<'s, Content<'s>> for Content<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Content<'s>> {
+ map(
+ pair(
+ opt(CharData::parse),
+ many0(pair(
+ alt((
+ map(Element::parse, |element| ContentItem::Element(element)),
+ map(Reference::parse, |reference| {
+ ContentItem::Reference(reference)
+ }),
+ map(CDSect::parse, |cd_sect| ContentItem::CDSect(cd_sect)),
+ map(PI::parse, |pi| ContentItem::PI(pi)),
+ map(Comment::parse, |comment| ContentItem::Comment(comment)),
+ )),
+ opt(CharData::parse),
+ )),
+ ),
+ |(char_data, content)| Content { char_data, content },
+ )(input)
+ }
+}
+
+/// [14] EmptyElemTag ::= '<' QName (S Attribute)* S? '/>'
+/// [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>' [WFC: Unique Att Spec]
+impl<'s> Parser<'s, EmptyElemTag<'s>> for EmptyElemTag<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EmptyElemTag<'s>> {
+ map(
+ delimited(
+ tag("<"),
+ pair(QName::parse, many0(preceded(S::parse, Attribute::parse))),
+ pair(opt(S::parse), tag("/>")),
+ ),
+ |(name, attributes)| EmptyElemTag { name, attributes },
+ )(input)
+ }
+}
+
+/// [17] elementdecl ::= ''
+/// [45] elementdecl ::= ''
+impl<'s> Parser<'s, Elementdecl<'s>> for Elementdecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Elementdecl> {
+ map(
+ delimited(
+ pair(tag("")),
+ ),
+ |(name, contentspec)| Elementdecl { name, contentspec },
+ )(input)
+ }
+}
+
+/// [46] contentspec ::= 'EMPTY' | 'ANY' | Mixed | children
+impl<'s> Parser<'s, Contentspec<'s>> for Contentspec<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Contentspec<'s>> {
+ alt((
+ value(Contentspec::Empty, tag("EMPTY")),
+ value(Contentspec::Any, tag("ANY")),
+ map(Mixed::parse, |mixed| Contentspec::Mixed(mixed)),
+ map(Children::parse, |children| Contentspec::Children(children)),
+ ))(input)
+ }
+}
+
+/// Occurence ::= ('?' | '*' | '+')?
+impl Parser<'_, Occurence> for Occurence {
+ fn parse(input: &'_ str) -> IResult<&str, Occurence> {
+ map(
+ opt(alt((tag("?"), tag("*"), tag("+")))),
+ |occurence| match occurence {
+ Some("?") => Occurence::Optional,
+ Some("*") => Occurence::Many0,
+ Some("+") => Occurence::Many1,
+ _ => Occurence::Once,
+ },
+ )(input)
+ }
+}
+
+/// [47] children ::= (choice | seq) ('?' | '*' | '+')?
+impl<'s> Parser<'s, Children<'s>> for Children<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Children<'s>> {
+ map(
+ pair(
+ alt((
+ map(Choice::parse, |choice| ChildrenKind::Choice(choice)),
+ map(Seq::parse, |seq| ChildrenKind::Seq(seq)),
+ )),
+ Occurence::parse,
+ ),
+ |(kind, occurence)| Children { kind, occurence },
+ )(input)
+ }
+}
+
+/// [18] cp ::= (QName | choice | seq) ('?' | '*' | '+')?
+/// [48] cp ::= (Name | choice | seq) ('?' | '*' | '+')?
+impl<'s> Parser<'s, Cp<'s>> for Cp<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Cp<'s>> {
+ map(
+ pair(
+ alt((
+ map(QName::parse, |name| CpKind::Name(name)),
+ map(Choice::parse, |choice| CpKind::Choice(choice)),
+ map(Seq::parse, |seq| CpKind::Seq(seq)),
+ )),
+ Occurence::parse,
+ ),
+ |(kind, occurence)| Cp { kind, occurence },
+ )(input)
+ }
+}
+
+/// [49] choice ::= '(' S? cp ( S? '|' S? cp )+ S? ')'
+impl<'s> Parser<'s, Choice<'s>> for Choice<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Choice<'s>> {
+ map(
+ delimited(
+ pair(tag("("), opt(S::parse)),
+ pair(
+ Cp::parse,
+ many1(preceded(
+ tuple((opt(S::parse), tag("|"), opt(S::parse))),
+ Cp::parse,
+ )),
+ ),
+ pair(opt(S::parse), tag(")")),
+ ),
+ |(head, tail)| {
+ let choice = vec![vec![head], tail].concat();
+ Choice(choice)
+ },
+ )(input)
+ }
+}
+
+/// [50] seq ::= '(' S? cp ( S? ',' S? cp )* S? ')'
+impl<'s> Parser<'s, Seq<'s>> for Seq<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Seq<'s>> {
+ map(
+ delimited(
+ pair(tag("("), opt(S::parse)),
+ pair(
+ Cp::parse,
+ many0(preceded(
+ tuple((opt(S::parse), tag(","), opt(S::parse))),
+ Cp::parse,
+ )),
+ ),
+ pair(opt(S::parse), tag(")")),
+ ),
+ |(head, tail)| {
+ let seq = vec![vec![head], tail].concat();
+ Seq(seq)
+ },
+ )(input)
+ }
+}
+
+/// [19] Mixed ::= '(' S? '#PCDATA' (S? '|' S? QName)* S? ')*' | '(' S? '#PCDATA' S? ')'
+/// [51] Mixed ::= '(' S? '#PCDATA' (S? '|' S? Name)* S? ')*' | '(' S? '#PCDATA' S? ')'
+impl<'s> Parser<'s, Mixed<'s>> for Mixed<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Mixed<'s>> {
+ alt((
+ map(
+ delimited(
+ tuple((tag("("), S::parse, tag("#PCDATA"))),
+ many0(preceded(
+ tuple((opt(S::parse), tag("|"), opt(S::parse))),
+ QName::parse,
+ )),
+ pair(opt(S::parse), tag(")*")),
+ ),
+ |names| Mixed(names),
+ ),
+ value(
+ Mixed(Vec::new()),
+ tuple((
+ tag("("),
+ opt(S::parse),
+ tag("#PCDATA"),
+ opt(S::parse),
+ tag(")"),
+ )),
+ ),
+ ))(input)
+ }
+}
+
+/// [20] AttlistDecl ::= ''
+/// [52] AttlistDecl ::= ''
+impl<'s> Parser<'s, AttlistDecl<'s>> for AttlistDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, AttlistDecl<'s>> {
+ map(
+ delimited(
+ pair(tag("")),
+ ),
+ |(element_type, att_defs)| AttlistDecl {
+ element_type,
+ att_defs,
+ },
+ )(input)
+ }
+}
+
+/// [21] AttDef ::= S (QName | NSAttName) S AttType S DefaultDecl
+/// [53] AttDef ::= S Name S AttType S DefaultDecl
+impl<'s> Parser<'s, AttDef<'s>> for AttDef<'s> {
+ fn parse(input: &'s str) -> IResult<&str, AttDef<'s>> {
+ map(
+ tuple((
+ preceded(
+ S::parse,
+ alt((
+ map(QName::parse, |q_name| AttDefName::QName(q_name)),
+ map(NSAttName::parse, |ns_att_name| {
+ AttDefName::NSAttName(ns_att_name)
+ }),
+ )),
+ ),
+ preceded(S::parse, AttType::parse),
+ preceded(S::parse, DefaultDecl::parse),
+ )),
+ |(name, att_type, default_decl)| AttDef {
+ name,
+ att_type,
+ default_decl,
+ },
+ )(input)
+ }
+}
+
+/// [54] AttType ::= StringType | TokenizedType | EnumeratedType
+impl<'s> Parser<'s, AttType<'s>> for AttType<'s> {
+ fn parse(input: &'s str) -> IResult<&str, AttType<'s>> {
+ alt((
+ value(AttType::StringType, StringType::parse),
+ map(TokenizedType::parse, |tokenized_type| {
+ AttType::TokenizedType(tokenized_type)
+ }),
+ map(EnumeratedType::parse, |enumerated_type| {
+ AttType::EnumeratedType(enumerated_type)
+ }),
+ ))(input)
+ }
+}
+
+/// [55] StringType ::= 'CDATA'
+impl Parser<'_, StringType> for StringType {
+ fn parse(input: &'_ str) -> IResult<&str, StringType> {
+ value(StringType, tag("CDATA"))(input)
+ }
+}
+
+/// [56] TokenizedType ::= 'ID' | 'IDREF' | 'IDREFS' | 'ENTITY' | 'ENTITIES' | 'NMTOKEN' | 'NMTOKENS'
+impl Parser<'_, TokenizedType> for TokenizedType {
+ fn parse(input: &'_ str) -> IResult<&str, TokenizedType> {
+ alt((
+ value(TokenizedType::ID, tag("ID")),
+ // TODO: check if this is required
+ // try idrefs first to avoid losing 'S'
+ value(TokenizedType::IDRefs, tag("IDREFS")),
+ value(TokenizedType::IDRef, tag("IDREF")),
+ value(TokenizedType::Entity, tag("ENTITY")),
+ value(TokenizedType::Entities, tag("ENTITIES")),
+ // same here
+ value(TokenizedType::NMTokens, tag("NMTOKENS")),
+ value(TokenizedType::NMToken, tag("NMTOKEN")),
+ ))(input)
+ }
+}
+
+/// [57] EnumeratedType ::= NotationType | Enumeration
+impl<'s> Parser<'s, EnumeratedType<'s>> for EnumeratedType<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EnumeratedType<'s>> {
+ alt((
+ map(NotationType::parse, |notation_type| {
+ EnumeratedType::NotationType(notation_type)
+ }),
+ map(Enumeration::parse, |enumeration| {
+ EnumeratedType::Enumeration(enumeration)
+ }),
+ ))(input)
+ }
+}
+
+/// [58] NotationType ::= 'NOTATION' S '(' S? Name (S? '|' S? Name)* S? ')'
+impl<'s> Parser<'s, NotationType<'s>> for NotationType<'s> {
+ fn parse(input: &'s str) -> IResult<&str, NotationType<'s>> {
+ map(
+ delimited(
+ tuple((tag("NOTATION"), S::parse, tag("("), opt(S::parse))),
+ pair(
+ Name::parse,
+ many0(preceded(
+ tuple((opt(S::parse), tag("|"), opt(S::parse))),
+ Name::parse,
+ )),
+ ),
+ pair(opt(S::parse), tag(")")),
+ ),
+ |(head, tail)| {
+ let notation_type = vec![vec![head], tail].concat();
+ NotationType(notation_type)
+ },
+ )(input)
+ }
+}
+
+/// [59] Enumeration ::= '(' S? Nmtoken (S? '|' S? Nmtoken)* S? ')'
+impl<'s> Parser<'s, Enumeration<'s>> for Enumeration<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Enumeration<'s>> {
+ map(
+ delimited(
+ pair(tag("("), opt(S::parse)),
+ pair(
+ Nmtoken::parse,
+ many0(preceded(
+ tuple((opt(S::parse), tag("|"), opt(S::parse))),
+ Nmtoken::parse,
+ )),
+ ),
+ pair(opt(S::parse), tag(")")),
+ ),
+ |(head, tail)| {
+ let enumeration = vec![vec![head], tail].concat();
+ Enumeration(enumeration)
+ },
+ )(input)
+ }
+}
+
+/// [60] DefaultDecl ::= '#REQUIRED' | '#IMPLIED' | (('#FIXED' S)? AttValue)
+impl<'s> Parser<'s, DefaultDecl<'s>> for DefaultDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, DefaultDecl<'s>> {
+ alt((
+ value(DefaultDecl::Required, tag("#REQUIRED")),
+ value(DefaultDecl::Implied, tag("#IMPLIED")),
+ map(
+ pair(opt(pair(tag("#FIXED"), S::parse)), AttValue::parse),
+ |(must, att_value)| DefaultDecl::Fixed(must.is_some(), att_value),
+ ),
+ ))(input)
+ }
+}
+
+/// [61] conditionalSect ::= includeSect | ignoreSect
+impl<'s> Parser<'s, ConditionalSect<'s>> for ConditionalSect<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ConditionalSect<'s>> {
+ alt((
+ map(IncludeSect::parse, |include_sect| {
+ ConditionalSect::IncludeSect(include_sect)
+ }),
+ map(IgnoreSect::parse, |ignore_sect| {
+ ConditionalSect::IgnoreSect(ignore_sect)
+ }),
+ ))(input)
+ }
+}
+
+/// [62] includeSect ::= ''
+impl<'s> Parser<'s, IncludeSect<'s>> for IncludeSect<'s> {
+ fn parse(input: &'s str) -> IResult<&str, IncludeSect<'s>> {
+ map(
+ delimited(
+ tuple((
+ tag(""),
+ ),
+ |ext_subset_decl| IncludeSect(ext_subset_decl),
+ )(input)
+ }
+}
+
+/// [63] ignoreSect ::= ''
+impl<'s> Parser<'s, IgnoreSect<'s>> for IgnoreSect<'s> {
+ fn parse(input: &'s str) -> IResult<&str, IgnoreSect<'s>> {
+ map(
+ delimited(
+ tuple((
+ tag(""),
+ ),
+ |ignore_sect_contents| IgnoreSect(ignore_sect_contents),
+ )(input)
+ }
+}
+
+/// [64] ignoreSectContents ::= Ignore ('' Ignore)*
+impl<'s> Parser<'s, IgnoreSectContents<'s>> for IgnoreSectContents<'s> {
+ fn parse(input: &'s str) -> IResult<&str, IgnoreSectContents<'s>> {
+ map(
+ pair(
+ Ignore::parse,
+ many0(tuple((
+ delimited(tag("")),
+ Ignore::parse,
+ ))),
+ ),
+ |(ignore, ignore_list)| IgnoreSectContents {
+ ignore,
+ ignore_list,
+ },
+ )(input)
+ }
+}
+
+/// [65] Ignore ::= Char* - (Char* ('') Char*)
+impl<'s> Parser<'s, Ignore<'s>> for Ignore<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Ignore<'s>> {
+ map(
+ recognize(many_till(Char::parse, peek(alt((tag("")))))),
+ |ignore| Ignore(ignore),
+ )(input)
+ }
+}
+
+/// [66] CharRef ::= '' [0-9]+ ';' | '' [0-9a-fA-F]+ ';'
+impl<'s> Parser<'s, CharRef<'s>> for CharRef<'s> {
+ fn parse(input: &'s str) -> IResult<&str, CharRef<'s>> {
+ alt((
+ delimited(
+ tag(""),
+ map(take_while(|c| matches!(c, '0'..='9')), |decimal| {
+ CharRef::Decimal(decimal)
+ }),
+ tag(";"),
+ ),
+ delimited(
+ tag(""),
+ map(
+ take_while(|c| matches!(c, '0'..='9' | 'a'..='f' | 'A'..='F' )),
+ |hexadecimal| CharRef::Hexadecimal(hexadecimal),
+ ),
+ tag(";"),
+ ),
+ ))(input)
+ }
+}
+
+/// [67] Reference ::= EntityRef | CharRef
+impl<'s> Parser<'s, Reference<'s>> for Reference<'s> {
+ fn parse(input: &'s str) -> IResult<&str, Reference<'s>> {
+ alt((
+ map(EntityRef::parse, |entity_ref| {
+ Reference::EntityRef(entity_ref)
+ }),
+ map(CharRef::parse, |char_ref| Reference::CharRef(char_ref)),
+ ))(input)
+ }
+}
+
+/// [68] EntityRef ::= '&' Name ';'
+impl<'s> Parser<'s, EntityRef<'s>> for EntityRef<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EntityRef<'s>> {
+ map(delimited(tag("&"), Name::parse, tag(";")), |entity_ref| {
+ EntityRef(entity_ref)
+ })(input)
+ }
+}
+
+/// [69] PEReference ::= '%' Name ';'
+impl<'s> Parser<'s, PEReference<'s>> for PEReference<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PEReference<'s>> {
+ map(delimited(tag("%"), Name::parse, tag(";")), |pe_reference| {
+ PEReference(pe_reference)
+ })(input)
+ }
+}
+
+/// [70] EntityDecl ::= GEDecl | PEDecl
+impl<'s> Parser<'s, EntityDecl<'s>> for EntityDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EntityDecl<'s>> {
+ alt((
+ map(GEDecl::parse, |ge_decl| EntityDecl::GEDecl(ge_decl)),
+ map(PEDecl::parse, |pe_decl| EntityDecl::PEDecl(pe_decl)),
+ ))(input)
+ }
+}
+
+/// [71] GEDecl ::= ''
+impl<'s> Parser<'s, GEDecl<'s>> for GEDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, GEDecl<'s>> {
+ map(
+ delimited(
+ pair(tag("")),
+ ),
+ |(name, entity_def)| GEDecl { name, entity_def },
+ )(input)
+ }
+}
+
+/// [72] PEDecl ::= ''
+impl<'s> Parser<'s, PEDecl<'s>> for PEDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PEDecl<'s>> {
+ map(
+ delimited(
+ tuple((tag("")),
+ ),
+ |(name, pe_def)| PEDecl { name, pe_def },
+ )(input)
+ }
+}
+
+/// [73] EntityDef ::= EntityValue | (ExternalID NDataDecl?)
+impl<'s> Parser<'s, EntityDef<'s>> for EntityDef<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EntityDef<'s>> {
+ alt((
+ map(EntityValue::parse, |entity_value| {
+ EntityDef::EntityValue(entity_value)
+ }),
+ map(
+ pair(ExternalID::parse, opt(NDataDecl::parse)),
+ |(external_id, n_data_decl)| EntityDef::ExternalID {
+ external_id,
+ n_data_decl,
+ },
+ ),
+ ))(input)
+ }
+}
+
+/// [74] PEDef ::= EntityValue | ExternalID
+impl<'s> Parser<'s, PEDef<'s>> for PEDef<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PEDef<'s>> {
+ alt((
+ map(EntityValue::parse, |entity_value| {
+ PEDef::EntityValue(entity_value)
+ }),
+ map(ExternalID::parse, |external_id| {
+ PEDef::ExternalID(external_id)
+ }),
+ ))(input)
+ }
+}
+
+/// [75] ExternalID ::= 'SYSTEM' S SystemLiteral | 'PUBLIC' S PubidLiteral S SystemLiteral
+// pub fn external_id(input: &str) -> IResult<&str, ExternalID> {
+impl<'s> Parser<'s, ExternalID<'s>> for ExternalID<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ExternalID<'s>> {
+ alt((
+ map(
+ preceded(pair(tag("SYSTEM"), S::parse), SystemLiteral::parse),
+ |system_identifier| ExternalID::SYSTEM { system_identifier },
+ ),
+ map(
+ preceded(
+ pair(tag("PUBLIC"), S::parse),
+ separated_pair(PubidLiteral::parse, S::parse, SystemLiteral::parse),
+ ),
+ |(public_identifier, system_identifier)| ExternalID::PUBLIC {
+ public_identifier,
+ system_identifier,
+ },
+ ),
+ ))(input)
+ }
+}
+
+/// [76] NDataDecl ::= S 'NDATA' S Name
+impl<'s> Parser<'s, NDataDecl<'s>> for NDataDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, NDataDecl<'s>> {
+ map(
+ preceded(tuple((S::parse, tag("NDATA"), S::parse)), Name::parse),
+ |n_data_decl| NDataDecl(n_data_decl),
+ )(input)
+ }
+}
+
+/// [77] TextDecl ::= ''
+impl<'s> Parser<'s, TextDecl<'s>> for TextDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, TextDecl<'s>> {
+ map(
+ delimited(
+ tag(""),
+ ),
+ |(version_info, encoding_decl)| TextDecl {
+ version_info,
+ encoding_decl,
+ },
+ )(input)
+ }
+}
+
+/// [78] extParsedEnt ::= TextDecl? content
+impl<'s> Parser<'s, ExtParsedEnt<'s>> for ExtParsedEnt<'s> {
+ fn parse(input: &'s str) -> IResult<&str, ExtParsedEnt<'s>> {
+ map(
+ pair(opt(TextDecl::parse), Content::parse),
+ |(text_decl, content)| ExtParsedEnt { text_decl, content },
+ )(input)
+ }
+}
+
+/// [80] EncodingDecl ::= S 'encoding' Eq ('"' EncName '"' | "'" EncName
+impl<'s> Parser<'s, EncodingDecl<'s>> for EncodingDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EncodingDecl<'s>> {
+ map(
+ preceded(
+ tuple((S::parse, tag("encoding"), Eq::parse)),
+ alt((
+ delimited(char('"'), EncName::parse, char('"')),
+ delimited(char('\''), EncName::parse, char('\'')),
+ )),
+ ),
+ |encoding_decl| EncodingDecl(encoding_decl),
+ )(input)
+ }
+}
+
+/// [81] EncName ::= [A-Za-z] ([A-Za-z0-9._] | '-')*
+impl<'s> Parser<'s, EncName<'s>> for EncName<'s> {
+ fn parse(input: &'s str) -> IResult<&str, EncName<'s>> {
+ map(
+ recognize(pair(
+ satisfy(|c| matches!(c, 'A'..='Z' | 'a'..='z' )),
+ many0(satisfy(
+ |c| matches!(c, 'A'..='Z' | 'a'..='z' | '0'..='9' | '.' | '_' | '-' ),
+ )),
+ )),
+ |enc_name| EncName(enc_name),
+ )(input)
+ }
+}
+
+/// [82] NotationDecl ::= ''
+impl<'s> Parser<'s, NotationDecl<'s>> for NotationDecl<'s> {
+ fn parse(input: &'s str) -> IResult<&str, NotationDecl<'s>> {
+ map(
+ delimited(
+ pair(tag("")),
+ ),
+ |(name, id)| NotationDecl { name, id },
+ )(input)
+ }
+}
+
+/// [83] PublicID ::= 'PUBLIC' S PubidLiteral
+impl<'s> Parser<'s, PublicID<'s>> for PublicID<'s> {
+ fn parse(input: &'s str) -> IResult<&str, PublicID<'s>> {
+ map(
+ preceded(pair(tag("PUBLIC"), S::parse), PubidLiteral::parse),
+ |public_id| PublicID(public_id),
+ )(input)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::num::NonZero;
+
+ use super::*;
+
+ #[test]
+ fn test_char_data() {
+ assert_eq!(
+ Ok(("&def]]>ghi", CharData("abc"))),
+ CharData::parse("abc&def]]>ghi")
+ );
+ assert_eq!(
+ Ok(("]]>ghi", CharData("abcdef"))),
+ CharData::parse("abcdef]]>ghi")
+ );
+ assert_eq!(
+ Ok(("&defghi", CharData("abc"))),
+ CharData::parse("abc&defghi")
+ );
+ assert_eq!(
+ Ok(("]]>def&ghi", CharData("abc"))),
+ CharData::parse("abc]]>def&ghi")
+ );
+ assert_eq!(
+ Ok(("&ghi", CharData("abc]>def"))),
+ CharData::parse("abc]>def&ghi")
+ );
+ }
+
+ #[test]
+ fn test_comment() {
+ assert_eq!(Ok(("", Comment(""))), Comment::parse(""));
+ assert_eq!(Ok(("", Comment("asdf"))), Comment::parse(""));
+ assert_eq!(Ok(("", Comment("as-df"))), Comment::parse(""));
+ }
+
+ #[test]
+ fn test_pi_target() {
+ assert_eq!(Ok((" ", PITarget(Name("asdf")))), PITarget::parse("asdf "));
+ assert_eq!(
+ Ok((" ", PITarget(Name("xmlasdf")))),
+ PITarget::parse("xmlasdf ")
+ );
+ assert_eq!(
+ Err(Err::Error(Error {
+ input: "xml ",
+ code: ErrorKind::Tag
+ })),
+ PITarget::parse("xml ")
+ );
+ assert_eq!(
+ Err(Err::Error(Error {
+ input: "xMl ",
+ code: ErrorKind::Tag
+ })),
+ PITarget::parse("xMl ")
+ );
+ }
+
+ #[test]
+ fn test_cd_sect() {
+ assert_eq!(
+ Ok(("", CDSect(CData("Hello, world!")))),
+ CDSect::parse("Hello, world!]]>")
+ )
+ }
+
+ #[test]
+ fn test_cd_start() {
+ assert_eq!(Ok(("asdf", CDStart)), CDStart::parse("asdf", CData("asdf"))), CData::parse("asdf]]>asdf"));
+ assert_eq!(
+ Ok(("]]>asdf", CData("asdf")
+ );
+ assert_eq!(
+ Ok(("]]>asdf", CData("Hello, world!"))),
+ CData::parse("Hello, world!]]>asdf")
+ )
+ }
+
+ #[test]
+ fn test_cd_end() {
+ assert_eq!(Ok(("asdf", CDEnd)), CDEnd::parse("]]>asdf"))
+ }
+}