1use std::{fmt::Display, ops::Deref};
2
3use nom::{
4 branch::alt,
5 bytes::complete::tag,
6 character::complete::{multispace0, multispace1, space0},
7 combinator::{map, opt, value},
8 multi::{many0, many1},
9 sequence::{preceded, terminated, tuple},
10};
11use nom_tracable::tracable_parser;
12
13use super::{name, MixedPCDATA, Name, Repeatable, Result, Span};
14
15#[derive(Debug, Display)]
21#[display(fmt = "<!ELEMENT {} {}>", name, category)]
22pub struct ElementDecl {
23 name: Name,
24 category: ElementCategory,
25}
26
27impl ElementDecl {
28 pub fn name(&self) -> &str {
29 self.name.deref()
30 }
31
32 pub fn category(&self) -> &ElementCategory {
33 &self.category
34 }
35}
36
37#[derive(Debug, Display)]
38pub enum ElementCategory {
39 #[display(fmt = "EMPTY")]
40 Empty,
41 #[display(fmt = "#PCDATA")]
42 PCDATA,
43 #[display(fmt = "#CDATA")]
44 CDATA,
45 #[display(fmt = "ANY")]
46 Any,
47 #[display(fmt = "{}", "_0")]
48 Mixed(Repeatable<MixedPCDATA>),
49 #[display(fmt = "{}", "_0")]
50 Children(Repeatable<Child>),
51}
52
53#[tracable_parser]
54fn parenthesis_open(i: Span) -> Result<Span> {
55 tag("(")(i)
56}
57
58#[tracable_parser]
59fn parenthesis_close(i: Span) -> Result<Span> {
60 tag(")")(i)
61}
62
63#[tracable_parser]
66fn mixed(i: Span) -> Result<ElementCategory> {
67 alt((
68 map(mixed_many_names, |x| {
69 ElementCategory::Mixed(Repeatable::ZeroOrManyTimes(MixedPCDATA(x)))
70 }),
71 map(mixed_no_names, |_| {
72 ElementCategory::Mixed(Repeatable::Once(MixedPCDATA(Vec::new())))
73 }),
74 ))(i)
75}
76
77#[tracable_parser]
78fn mixed_no_names(i: Span) -> Result<()> {
79 value(
80 (),
81 tuple((
82 tag("("),
83 multispace0,
84 tag("#PCDATA"),
85 multispace0,
86 tag(")"),
87 space0,
88 )),
89 )(i)
90}
91
92#[tracable_parser]
93fn mixed_many_names(i: Span) -> Result<Vec<Name>> {
94 map(
95 tuple((
96 tuple((tag("("), multispace0, tag("#PCDATA"), multispace0)),
97 many0(preceded(tuple((multispace0, tag("|"), multispace0)), name)),
98 tuple((multispace0, tag(")*"))),
99 )),
100 |(_, x, _)| x,
101 )(i)
102}
103
104#[derive(AsRef, AsMut, Debug, Deref, DerefMut, Display, IntoIterator)]
105#[display(
106 fmt = "({})",
107 "_0.iter().map(|v|v.to_string()).collect::<Vec<_>>().join(\", \")"
108)]
109pub struct Seq<T>(Vec<T>)
110where
111 T: Display;
112
113#[derive(AsRef, AsMut, Debug, Deref, DerefMut, Display, IntoIterator)]
114#[display(
115 fmt = "({})",
116 "_0.iter().map(|v|v.to_string()).collect::<Vec<_>>().join(\" | \")"
117)]
118pub struct Choices<T>(Vec<T>)
119where
120 T: Display;
121
122#[derive(Debug, Display)]
123pub enum Child {
124 #[display(fmt = "{}", "_0")]
125 Name(Name),
126 #[display(fmt = "{}", "_0")]
127 Seq(Seq<Repeatable<Child>>),
128 #[display(fmt = "{}", "_0")]
129 Choices(Choices<Repeatable<Child>>),
130}
131
132#[tracable_parser]
134fn children(i: Span) -> Result<ElementCategory> {
135 map(
136 tuple((
137 alt((map(choices, Child::Choices), map(sequence, Child::Seq))),
138 opt(alt((tag("?"), tag("*"), tag("+")))),
139 )),
140 |(child, repeatable)| {
141 let repeatable = if let Some(repeatable) = repeatable {
142 match *repeatable {
143 "?" => |child| Repeatable::AtMostOnce(child),
144 "*" => |child| Repeatable::ZeroOrManyTimes(child),
145 "+" => |child| Repeatable::AtLeastOnce(child),
146 _ => unreachable!(),
147 }
148 } else {
149 |child| Repeatable::Once(child)
150 };
151 ElementCategory::Children(repeatable(child))
152 },
153 )(i)
154}
155
156#[tracable_parser]
158fn sequence(i: Span) -> Result<Seq<Repeatable<Child>>> {
159 map(
160 tuple((
161 preceded(tuple((parenthesis_open, multispace0)), cuple),
162 terminated(
163 many0(preceded(tuple((multispace0, tag(","), multispace0)), cuple)),
164 tuple((multispace0, parenthesis_close)),
165 ),
166 )),
167 |(cp, many)| Seq(std::iter::once(cp).chain(many.into_iter()).collect()),
168 )(i)
169}
170
171#[tracable_parser]
173fn choices(i: Span) -> Result<Choices<Repeatable<Child>>> {
174 map(
175 tuple((
176 parenthesis_open,
177 multispace0,
178 cuple,
179 many1(tuple((multispace0, tag("|"), multispace0, cuple))),
180 multispace0,
181 parenthesis_close,
182 )),
183 |(_, _, cp, many, _, _)| {
184 Choices(
185 std::iter::once(cp)
186 .chain(many.into_iter().map(|(_, _, _, x)| x))
187 .collect(),
188 )
189 },
190 )(i)
191}
192
193#[tracable_parser]
195fn cuple(i: Span) -> Result<Repeatable<Child>> {
196 map(
197 tuple((
198 alt((
199 map(name, Child::Name),
200 map(choices, Child::Choices),
201 map(sequence, Child::Seq),
202 )),
203 opt(alt((tag("?"), tag("*"), tag("+")))),
204 )),
205 |(child, repeatable)| {
206 let repeatable = if let Some(repeatable) = repeatable {
207 match *repeatable {
208 "?" => |child| Repeatable::AtMostOnce(child),
209 "*" => |child| Repeatable::ZeroOrManyTimes(child),
210 "+" => |child| Repeatable::AtLeastOnce(child),
211 _ => unreachable!(),
212 }
213 } else {
214 |child| Repeatable::Once(child)
215 };
216 repeatable(child)
217 },
218 )(i)
219}
220
221#[tracable_parser]
222fn empty(i: Span) -> Result<ElementCategory> {
223 map(tag("EMPTY"), |_| ElementCategory::Empty)(i)
224}
225
226#[tracable_parser]
227fn any(i: Span) -> Result<ElementCategory> {
228 map(tag("ANY"), |_| ElementCategory::Any)(i)
229}
230
231#[tracable_parser]
233pub(super) fn element_decl(i: Span) -> Result<ElementDecl> {
234 map(
235 tuple((
236 tag("<!ELEMENT"),
237 multispace1,
238 name,
239 multispace1,
240 alt((empty, any, mixed, children)),
241 multispace0,
242 tag(">"),
243 )),
244 |(_, _, n, _, c, _, _)| ElementDecl {
245 name: n,
246 category: c,
247 },
248 )(i)
249}
250
251#[cfg(test)]
297mod tests {
298
299 use super::{children, element_decl, mixed};
300 use crate::assert_ok;
301 use crate::span;
302
303 use nom::Finish;
304
305 #[test]
306 fn test_element_decl() {
307 let input = span("<!ELEMENT b (#PCDATA)>");
308 let el = element_decl(input).finish();
309 assert_ok!(input, el);
310 let input = span("<!ELEMENT p (#PCDATA|a|ul|b|i|em)*>");
311 let el = element_decl(input).finish();
312 assert_ok!(input, el);
313 let el = element_decl(span(
314 "<!ELEMENT p (#PCDATA | %font; | %phrase; | %special; | %form;)* >",
315 ))
316 .finish();
317 assert_ok!(input, el);
318 }
319
320 #[test]
321 fn test_element_decl_multiline() {
322 let input = span(
323 r#"<!ELEMENT generated (#PCDATA | emphasis | strong | literal | math
324 | reference | footnote_reference | citation_reference
325 | substitution_reference | title_reference
326 | abbreviation | acronym | subscript | superscript
327 | inline | problematic | generated
328 | target | image | raw
329 )* >"#,
330 );
331 let el = element_decl(input).finish();
332 assert_ok!(input, el);
333 }
334
335 #[test]
336 fn test_element_decl_multiline2() {
337 let el = element_decl(span(
338 r#"<!ELEMENT document
339 ( (title, subtitle?)?,
340 meta?,
341 decoration?,
342 (docinfo, transition?)?,
343 ( ( ( paragraph | compound | container | literal_block | doctest_block
344 | math_block | line_block | block_quote
345 | table | figure | image | footnote | citation | rubric
346 | bullet_list | enumerated_list | definition_list | field_list
347 | option_list
348 | attention | caution | danger | error | hint | important | note
349 | tip | warning | admonition
350 | reference | target | substitution_definition | comment | pending
351 | system_message | raw
352 | topic | sidebar)+, transition? )*,
353 ( ( section
354 ), (transition?, ( section
355 ) )* )? ) )>"#,
356 ));
357 assert!(el.is_ok(), "{:?}", el.as_ref().unwrap_err());
358 }
359
360 #[test]
361 fn test_child() {
362 let el = children(span("(title, subtitle?)?"));
363 assert!(el.is_ok(), "{:?}", el.as_ref().unwrap_err());
364 }
365
366 #[test]
367 fn test_mixed_simple() {
368 let input = span("<!ELEMENT option_string (#PCDATA)>");
369 let el = element_decl(input).finish();
370 assert_ok!(input, el);
371 }
372}