1use super::{CombineOp, DefCombiner, Pattern, Relaxng};
23
24pub fn simplify_top(rng: &mut Relaxng, raw: Vec<Pattern>) -> Vec<Pattern> {
26 raw
27 .into_iter()
28 .flat_map(|p| simplify(rng, p, "", "", None))
29 .collect()
30}
31
32pub fn simplify(
41 rng: &mut Relaxng,
42 form: Pattern,
43 binding: &str,
44 parent: &str,
45 container: Option<&str>,
46) -> Vec<Pattern> {
47 match form {
48 Pattern::Grammar { name, body } => {
49 let new_body = simplify_args(rng, body, &name, binding, container);
50 vec![Pattern::Grammar { name, body: new_body }]
51 },
52
53 Pattern::Override { module, replacements } => {
54 simplify_override(rng, *module, replacements, binding, parent, container)
55 },
56
57 Pattern::Module { name, body } => {
58 let idx = rng.modules.len();
62 rng.modules.push(Pattern::Module {
63 name: name.clone(),
64 body: Vec::new(),
65 });
66 let new_body: Vec<Pattern> = body
67 .into_iter()
68 .flat_map(|p| simplify(rng, p, binding, parent, container))
69 .collect();
70 if let Some(Pattern::Module { body: slot, .. }) = rng.modules.get_mut(idx) {
71 *slot = new_body.clone();
72 }
73 vec![Pattern::Module { name, body: new_body }]
74 },
75
76 Pattern::Element { name, body } => {
77 let elem_container = match container {
85 Some(c) => {
86 let host = c.split_once('@').map_or(c, |(_, h)| h);
87 if host.starts_with("pattern:") {
88 format!("element:{}@{}", name, host)
89 } else {
90 format!("element:{}", name)
91 }
92 },
93 None => format!("element:{}", name),
94 };
95 let new_body: Vec<Pattern> = body
96 .into_iter()
97 .flat_map(|p| simplify(rng, p, binding, parent, Some(&elem_container)))
98 .collect();
99 rng
100 .elements
101 .entry(name.clone())
102 .or_default()
103 .extend(new_body.clone());
104 vec![Pattern::Element { name, body: new_body }]
105 },
106
107 Pattern::Ref { qname } => simplify_ref(rng, &qname, binding, container, false),
108 Pattern::ParentRef { qname } => simplify_ref(rng, &qname, parent, container, true),
109
110 Pattern::Def { combiner, name, body } => {
111 simplify_def(rng, combiner, name, body, binding, parent, container)
112 },
113
114 Pattern::Combination { op, body } => {
116 let new_body = simplify_args(rng, body, binding, parent, container);
117 vec![Pattern::Combination { op, body: new_body }]
118 },
119 Pattern::Start { body } => {
120 let new_body = simplify_args(rng, body, binding, parent, container);
121 vec![Pattern::Start { body: new_body }]
122 },
123 Pattern::Attribute { name, body } => {
124 let new_body = simplify_args(rng, body, binding, parent, container);
125 vec![Pattern::Attribute { name, body: new_body }]
126 },
127
128 other @ (Pattern::Value(_)
130 | Pattern::Data(_)
131 | Pattern::Doc(_)
132 | Pattern::Text
133 | Pattern::ElementRef { .. }) => vec![other],
134 }
135}
136
137fn simplify_args(
138 rng: &mut Relaxng,
139 forms: Vec<Pattern>,
140 binding: &str,
141 parent: &str,
142 container: Option<&str>,
143) -> Vec<Pattern> {
144 forms
145 .into_iter()
146 .flat_map(|p| simplify(rng, p, binding, parent, container))
147 .collect()
148}
149
150fn simplify_ref(
151 rng: &mut Relaxng,
152 name: &str,
153 bind: &str,
154 container: Option<&str>,
155 _is_parent: bool,
156) -> Vec<Pattern> {
157 let qname = format!("{}:{}", bind, name);
161 if let Some(c) = container {
162 rng
163 .uses_name
164 .entry(qname.clone())
165 .or_default()
166 .insert(c.to_string());
167 }
168 vec![Pattern::Ref { qname }]
169}
170
171fn simplify_def(
172 rng: &mut Relaxng,
173 combiner: DefCombiner,
174 name: String,
175 body: Vec<Pattern>,
176 binding: &str,
177 parent: &str,
178 container: Option<&str>,
179) -> Vec<Pattern> {
180 let qname = format!("{}:{}", binding, name);
181 if let Some(c) = container {
182 rng
183 .uses_name
184 .entry(qname.clone())
185 .or_default()
186 .insert(c.to_string());
187 }
188 let pattern_container = format!("pattern:{}", qname);
189 let args = simplify_args(rng, body, binding, parent, Some(&pattern_container));
190
191 if combiner == DefCombiner::Group
200 && args.len() == 1
201 && let Pattern::Element { name: el_name, .. } = &args[0]
202 {
203 rng.elementdefs.insert(qname.clone(), el_name.clone());
204 rng
205 .element_reverse_defs
206 .insert(el_name.clone(), qname.clone());
207 return vec![Pattern::Def {
208 combiner,
209 name: qname,
210 body: args,
211 }];
212 }
213
214 let xargs: Vec<Pattern> = args
216 .iter()
217 .filter(|p| !matches!(p, Pattern::Doc(_)))
218 .cloned()
219 .collect();
220 let prev = rng.defs.get(&qname).cloned();
221 let prev_combiner = rng.def_combiner.get(&qname).copied();
222
223 let mut effective = combiner;
224 let mut prev_args: Vec<Pattern> = Vec::new();
225 let mut keep_prev = prev.is_some();
226 if let Some(prev_pat) = &prev {
227 if let Pattern::Combination { body, .. } = prev_pat {
228 prev_args = body.clone();
229 } else {
230 prev_args = vec![prev_pat.clone()];
231 }
232 match (combiner, prev_combiner) {
233 (DefCombiner::Group, Some(DefCombiner::Group)) => {
234 keep_prev = false;
236 },
237 (DefCombiner::Group, Some(other)) => {
238 effective = other;
241 },
242 _ => {},
243 }
244 }
245 if !keep_prev {
246 prev_args.clear();
247 }
248
249 let combination_op = match effective {
250 DefCombiner::Group => CombineOp::Group,
251 DefCombiner::Choice => CombineOp::Choice,
252 DefCombiner::Interleave => CombineOp::Interleave,
253 };
254 let mut combined = prev_args;
255 combined.extend(xargs);
256 let combined_pat = simplify_combination(Pattern::Combination {
257 op: combination_op,
258 body: combined,
259 });
260 rng.defs.insert(qname.clone(), combined_pat);
261 rng.def_combiner.insert(qname.clone(), effective);
262
263 vec![Pattern::Def {
266 combiner,
267 name: qname,
268 body: args,
269 }]
270}
271
272pub fn simplify_combination(pat: Pattern) -> Pattern {
275 match pat {
276 Pattern::Combination { op, body } => {
277 let recursed: Vec<Pattern> = body.into_iter().map(simplify_combination).collect();
278 let flattened: Vec<Pattern> = if matches!(op, CombineOp::Group | CombineOp::Choice) {
279 let mut out = Vec::with_capacity(recursed.len());
280 for s in recursed {
281 match s {
282 Pattern::Combination { op: inner_op, body: inner_body } if inner_op == op => {
283 out.extend(inner_body);
284 },
285 other => out.push(other),
286 }
287 }
288 out
289 } else {
290 recursed
291 };
292 if op == CombineOp::Group && flattened.len() == 1 {
293 flattened.into_iter().next().unwrap()
294 } else {
295 Pattern::Combination { op, body: flattened }
296 }
297 },
298 other => other,
299 }
300}
301
302fn simplify_override(
303 rng: &mut Relaxng,
304 module: Pattern,
305 replacements: Vec<Pattern>,
306 binding: &str,
307 parent: &str,
308 container: Option<&str>,
309) -> Vec<Pattern> {
310 let (mod_name, mut patterns) = match module {
311 Pattern::Module { name, body } => (name, body),
312 other => {
313 return simplify(rng, other, binding, parent, container);
315 },
316 };
317
318 let has_replacement_start = replacements
320 .iter()
321 .any(|p| matches!(p, Pattern::Start { .. }));
322 if has_replacement_start {
323 patterns.retain(|p| !matches!(p, Pattern::Start { .. }));
324 }
325
326 let replacement_defs: Vec<(DefCombiner, String)> = replacements
330 .iter()
331 .filter_map(|p| match p {
332 Pattern::Def { combiner, name, .. } => Some((*combiner, name.clone())),
333 _ => None,
334 })
335 .collect();
336 patterns.retain(|p| match p {
337 Pattern::Def { combiner: c, name: n, .. } => {
338 !replacement_defs.iter().any(|(rc, rn)| rc == c && rn == n)
339 },
340 _ => true,
341 });
342
343 let mut combined = patterns;
344 combined.extend(replacements);
345 let new_module = Pattern::Module {
346 name: format!("{} (overridden)", mod_name),
347 body: combined,
348 };
349 simplify(rng, new_module, binding, parent, container)
350}
351
352pub fn extract_start(items: &[Pattern]) -> Vec<Pattern> {
356 let mut out = Vec::new();
357 for item in items {
358 match item {
359 Pattern::Start { body } => out.extend(body.iter().cloned()),
360 Pattern::Module { body, .. } | Pattern::Grammar { body, .. } => {
361 out.extend(extract_start(body));
362 },
363 _ => {},
364 }
365 }
366 out
367}
368
369#[cfg(test)]
372mod tests {
373 use super::*;
374 use crate::common::relaxng::scan::scan_string;
375
376 fn simplify_xml(xml: &str) -> (Relaxng, Vec<Pattern>) {
377 let mut rng = Relaxng::default();
378 let raw = scan_string(&mut rng, xml).expect("scan");
379 let simp = simplify_top(&mut rng, raw);
380 (rng, simp)
381 }
382
383 #[test]
384 fn simplify_collapses_singleton_group() {
385 let inner = Pattern::Element { name: "x".into(), body: vec![] };
386 let combo = Pattern::Combination {
387 op: CombineOp::Group,
388 body: vec![inner.clone()],
389 };
390 let result = simplify_combination(combo);
391 assert!(matches!(result, Pattern::Element { ref name, .. } if name == "x"));
392 }
393
394 #[test]
395 fn simplify_flattens_nested_choice() {
396 let inner_choice = Pattern::Combination {
397 op: CombineOp::Choice,
398 body: vec![
399 Pattern::Element { name: "a".into(), body: vec![] },
400 Pattern::Element { name: "b".into(), body: vec![] },
401 ],
402 };
403 let outer = Pattern::Combination {
404 op: CombineOp::Choice,
405 body: vec![inner_choice, Pattern::Element {
406 name: "c".into(),
407 body: vec![],
408 }],
409 };
410 let result = simplify_combination(outer);
411 let body = match &result {
412 Pattern::Combination { op: CombineOp::Choice, body } => body,
413 other => panic!("expected flat Choice, got {:?}", other),
414 };
415 assert_eq!(body.len(), 3);
416 }
417
418 #[test]
419 fn simplify_records_modules_in_document_order() {
420 let xml = r#"
424 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
425 <define name="A"><element name="a"><empty/></element></define>
426 <define name="B"><element name="b"><empty/></element></define>
427 </grammar>
428 "#;
429 let mut rng = Relaxng::default();
430 let raw = scan_string(&mut rng, xml).expect("scan");
431 let wrapped = vec![Pattern::Module {
432 name: "wrapper".into(),
433 body: raw,
434 }];
435 let _ = simplify_top(&mut rng, wrapped);
436 assert!(!rng.modules.is_empty(), "modules should be recorded");
437 let names: Vec<&str> = rng
438 .modules
439 .iter()
440 .filter_map(|m| match m {
441 Pattern::Module { name, .. } => Some(name.as_str()),
442 _ => None,
443 })
444 .collect();
445 assert_eq!(names, vec!["wrapper"]);
446 }
447
448 #[test]
449 fn simplify_singleton_element_def_records_elementdefs() {
450 let xml = r#"
451 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
452 <define name="MY"><element name="my-el"><empty/></element></define>
453 </grammar>
454 "#;
455 let (rng, _) = simplify_xml(xml);
456 assert_eq!(
458 rng.elementdefs.get("grammar1:MY"),
459 Some(&"my-el".to_string())
460 );
461 assert_eq!(
462 rng.element_reverse_defs.get("my-el"),
463 Some(&"grammar1:MY".to_string())
464 );
465 }
466
467 #[test]
468 fn simplify_complex_def_recorded_in_defs() {
469 let xml = r#"
470 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
471 <define name="X">
472 <choice>
473 <element name="a"><empty/></element>
474 <element name="b"><empty/></element>
475 </choice>
476 </define>
477 </grammar>
478 "#;
479 let (rng, _) = simplify_xml(xml);
480 assert!(rng.defs.contains_key("grammar1:X"));
481 assert_eq!(
482 rng.def_combiner.get("grammar1:X").copied(),
483 Some(DefCombiner::Group)
484 );
485 }
486
487 #[test]
488 fn simplify_combine_choice_accumulates() {
489 let xml = r#"
493 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
494 <define name="X"><ref name="A"/></define>
495 <define name="X" combine="choice"><ref name="B"/></define>
496 </grammar>
497 "#;
498 let (rng, _) = simplify_xml(xml);
499 let combined = rng.defs.get("grammar1:X").expect("defs entry");
500 let body = match combined {
501 Pattern::Combination { op: CombineOp::Choice, body } => body,
502 other => panic!("expected Choice combination, got {:?}", other),
503 };
504 let qnames: Vec<&str> = body
505 .iter()
506 .filter_map(|p| match p {
507 Pattern::Ref { qname } => Some(qname.as_str()),
508 _ => None,
509 })
510 .collect();
511 assert!(
512 qnames.contains(&"grammar1:A"),
513 "A ref missing: {:?}",
514 qnames
515 );
516 assert!(
517 qnames.contains(&"grammar1:B"),
518 "B ref missing: {:?}",
519 qnames
520 );
521 assert_eq!(
522 rng.def_combiner.get("grammar1:X").copied(),
523 Some(DefCombiner::Choice)
524 );
525 }
526
527 #[test]
528 fn simplify_records_uses_name() {
529 let _xml = r#"
530 <grammar xmlns="http://relaxml.org/ns/structure/1.0"
531 xmlns:rng="http://relaxng.org/ns/structure/1.0">
532 </grammar>
533 "#;
534 let xml = r#"
536 <grammar xmlns="http://relaxng.org/ns/structure/1.0">
537 <define name="P">
538 <element name="p"><ref name="Q"/></element>
539 </define>
540 <define name="Q"><text/></define>
541 </grammar>
542 "#;
543 let (rng, _) = simplify_xml(xml);
544 let p_uses = rng.uses_name.get("grammar1:Q");
546 assert!(
547 p_uses.is_some(),
548 "uses_name for grammar1:Q should be populated, got {:?}",
549 rng.uses_name.keys().collect::<Vec<_>>()
550 );
551 let containers = p_uses.unwrap();
555 assert!(
556 containers.contains("element:p@pattern:grammar1:P"),
557 "expected Q usage under element:p@pattern:grammar1:P, got {:?}",
558 containers
559 );
560 }
561
562 #[test]
563 fn simplify_extract_start_descends_grammar_and_module() {
564 let inner = Pattern::Start {
565 body: vec![Pattern::Element {
566 name: "root".into(),
567 body: vec![],
568 }],
569 };
570 let nested = vec![Pattern::Module {
571 name: "m".into(),
572 body: vec![Pattern::Grammar {
573 name: "g".into(),
574 body: vec![inner],
575 }],
576 }];
577 let starts = extract_start(&nested);
578 assert_eq!(starts.len(), 1);
579 assert!(matches!(starts[0], Pattern::Element { ref name, .. } if name == "root"));
580 }
581
582 #[test]
583 fn simplify_override_drops_overridden_def_and_keeps_replacement() {
584 let module = Pattern::Module {
588 name: "m".into(),
589 body: vec![
590 Pattern::Def {
591 combiner: DefCombiner::Group,
592 name: "X".into(),
593 body: vec![Pattern::Element {
594 name: "original".into(),
595 body: vec![],
596 }],
597 },
598 Pattern::Def {
599 combiner: DefCombiner::Group,
600 name: "Y".into(),
601 body: vec![Pattern::Element {
602 name: "y-el".into(),
603 body: vec![],
604 }],
605 },
606 ],
607 };
608 let override_pat = Pattern::Override {
609 module: Box::new(module),
610 replacements: vec![Pattern::Def {
611 combiner: DefCombiner::Group,
612 name: "X".into(),
613 body: vec![Pattern::Element {
614 name: "replacement".into(),
615 body: vec![],
616 }],
617 }],
618 };
619
620 let mut rng = Relaxng::default();
621 let _ = simplify(&mut rng, override_pat, "g", "", None);
622
623 assert_eq!(rng.elementdefs.get("g:X"), Some(&"replacement".to_string()));
625 assert_eq!(rng.elementdefs.get("g:Y"), Some(&"y-el".to_string()));
627 assert_eq!(rng.element_reverse_defs.get("original"), None);
628 }
629}