1use crate::{
8 AstarHeuristic, AstarOptions, BottomUpTa, EvaluatedAlgebraValue, Explicit, Irtg, IrtgError,
9 MaterializationStrategy, StateId, Symbol, TreeValue,
10};
11use packed_term_arena::{
12 parser::parse_tree,
13 tree::{Tree, TreeArena},
14};
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct AutomatonSummary {
19 pub rule_count: usize,
21 pub state_count: u32,
23 pub maximum_rank: usize,
25 pub is_empty: bool,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub enum LanguageCardinality {
32 Finite(usize),
34 Infinite,
36 TooLarge,
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct InterpretationInfo {
43 pub name: String,
45 pub class_name: String,
47 pub input_capable: bool,
49 pub non_null_filter_capable: bool,
51}
52
53#[derive(Clone, Debug, PartialEq)]
55pub struct ResolvedRule {
56 pub parent: String,
58 pub parent_is_final: bool,
60 pub symbol: String,
62 pub children: Vec<String>,
64 pub weight: f64,
66 pub interpretation_terms: Vec<(String, String)>,
68}
69
70#[derive(Debug)]
72pub enum RenderedValue {
73 Text(String),
75 Tree(TreeValue),
77}
78
79#[derive(Debug)]
81pub struct RenderedInterpretation {
82 pub name: String,
84 pub value: RenderedValue,
86}
87
88#[derive(Debug)]
90pub struct EvaluatedInterpretation {
91 pub name: String,
93 pub value: EvaluatedAlgebraValue,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq)]
99pub enum ParseStrategy {
100 TopDownCondensed,
102 IndexedCondensed,
104 AstarZero {
106 stop_at_first_goal: bool,
108 beam: Option<f64>,
110 },
111}
112
113impl ParseStrategy {
114 pub fn materialization_strategy(&self) -> MaterializationStrategy<'static> {
116 match *self {
117 Self::TopDownCondensed => MaterializationStrategy::TopDownCondensed,
118 Self::IndexedCondensed => MaterializationStrategy::IndexedCondensed,
119 Self::AstarZero {
120 stop_at_first_goal,
121 beam,
122 } => MaterializationStrategy::Astar {
123 heuristic: AstarHeuristic::Zero,
124 options: AstarOptions {
125 stop_at_first_goal,
126 beam,
127 },
128 },
129 }
130 }
131}
132
133impl Explicit {
134 pub fn application_summary(&self) -> AutomatonSummary {
136 AutomatonSummary {
137 rule_count: self.rules().count(),
138 state_count: self.num_states(),
139 maximum_rank: self
140 .rules()
141 .map(|rule| rule.children.len())
142 .max()
143 .unwrap_or(0),
144 is_empty: self.is_empty(),
145 }
146 }
147
148 pub fn language_cardinality(&self) -> LanguageCardinality {
150 let state_count = self.num_states() as usize;
151 let rules = self.rules().collect::<Vec<_>>();
152 let mut productive = vec![false; state_count];
153 let mut changed = true;
154 while changed {
155 changed = false;
156 for rule in &rules {
157 if !productive[rule.result.index()]
158 && rule.children.iter().all(|child| productive[child.index()])
159 {
160 productive[rule.result.index()] = true;
161 changed = true;
162 }
163 }
164 }
165
166 let mut relevant = vec![false; state_count];
167 let mut stack = (0..state_count)
168 .map(|index| StateId(index as u32))
169 .filter(|state| self.is_accepting(state) && productive[state.index()])
170 .collect::<Vec<_>>();
171 while let Some(state) = stack.pop() {
172 if std::mem::replace(&mut relevant[state.index()], true) {
173 continue;
174 }
175 for rule in rules.iter().filter(|rule| rule.result == state) {
176 if rule.children.iter().all(|child| productive[child.index()]) {
177 stack.extend(rule.children.iter().copied());
178 }
179 }
180 }
181
182 fn has_cycle(
183 state: StateId,
184 rules: &[crate::Rule<'_>],
185 productive: &[bool],
186 relevant: &[bool],
187 colors: &mut [u8],
188 ) -> bool {
189 colors[state.index()] = 1;
190 for child in rules
191 .iter()
192 .filter(|rule| {
193 rule.result == state
194 && rule.children.iter().all(|child| productive[child.index()])
195 })
196 .flat_map(|rule| rule.children.iter().copied())
197 .filter(|child| relevant[child.index()])
198 {
199 if colors[child.index()] == 1
200 || (colors[child.index()] == 0
201 && has_cycle(child, rules, productive, relevant, colors))
202 {
203 return true;
204 }
205 }
206 colors[state.index()] = 2;
207 false
208 }
209
210 let mut colors = vec![0; state_count];
211 for index in 0..state_count {
212 if relevant[index]
213 && colors[index] == 0
214 && has_cycle(
215 StateId(index as u32),
216 &rules,
217 &productive,
218 &relevant,
219 &mut colors,
220 )
221 {
222 return LanguageCardinality::Infinite;
223 }
224 }
225
226 fn count_state(
227 state: StateId,
228 rules: &[crate::Rule<'_>],
229 productive: &[bool],
230 memo: &mut [Option<Option<usize>>],
231 ) -> Option<usize> {
232 if let Some(count) = memo[state.index()] {
233 return count;
234 }
235 let mut total = 0usize;
236 for rule in rules.iter().filter(|rule| {
237 rule.result == state && rule.children.iter().all(|child| productive[child.index()])
238 }) {
239 let mut combinations = 1usize;
240 for &child in rule.children {
241 combinations =
242 combinations.checked_mul(count_state(child, rules, productive, memo)?)?;
243 }
244 total = total.checked_add(combinations)?;
245 }
246 memo[state.index()] = Some(Some(total));
247 Some(total)
248 }
249
250 let mut memo = vec![None; state_count];
251 let mut total = 0usize;
252 for index in 0..state_count {
253 let state = StateId(index as u32);
254 if self.is_accepting(&state) && productive[index] {
255 let Some(count) = count_state(state, &rules, &productive, &mut memo) else {
256 return LanguageCardinality::TooLarge;
257 };
258 let Some(next) = total.checked_add(count) else {
259 return LanguageCardinality::TooLarge;
260 };
261 total = next;
262 }
263 }
264 LanguageCardinality::Finite(total)
265 }
266
267 pub fn resolve_rules(
269 &self,
270 mut state_name: impl FnMut(StateId) -> String,
271 mut symbol_name: impl FnMut(Symbol) -> String,
272 ) -> Vec<ResolvedRule> {
273 self.rules()
274 .map(|rule| ResolvedRule {
275 parent: state_name(rule.result),
276 parent_is_final: self.is_accepting(&rule.result),
277 symbol: symbol_name(rule.symbol),
278 children: rule.children.iter().copied().map(&mut state_name).collect(),
279 weight: rule.weight,
280 interpretation_terms: Vec::new(),
281 })
282 .collect()
283 }
284
285 pub fn resolved_rule(
287 &self,
288 index: usize,
289 mut state_name: impl FnMut(StateId) -> String,
290 mut symbol_name: impl FnMut(Symbol) -> String,
291 ) -> ResolvedRule {
292 let rule = self.rule(index);
293 ResolvedRule {
294 parent: state_name(rule.result),
295 parent_is_final: self.is_accepting(&rule.result),
296 symbol: symbol_name(rule.symbol),
297 children: rule.children.iter().copied().map(&mut state_name).collect(),
298 weight: rule.weight,
299 interpretation_terms: Vec::new(),
300 }
301 }
302}
303
304impl Irtg {
305 pub fn interpretation_info(&self) -> Vec<InterpretationInfo> {
307 let mut values: Vec<_> = self
308 .interpretations()
309 .map(|interpretation| InterpretationInfo {
310 name: interpretation.name().to_owned(),
311 class_name: interpretation.class_name().to_owned(),
312 input_capable: interpretation.is_inputable(),
313 non_null_filter_capable: interpretation.supports_non_null_filter(),
314 })
315 .collect();
316 values.sort_by(|a, b| a.name.cmp(&b.name));
317 values
318 }
319
320 pub fn resolved_grammar_rules(&self) -> Vec<ResolvedRule> {
322 let mut interpretations: Vec<_> = self.interpretations().collect();
323 interpretations.sort_by_key(|interpretation| interpretation.name());
324
325 self.grammar()
326 .rules()
327 .map(|rule| {
328 let interpretation_terms = interpretations
329 .iter()
330 .map(|interpretation| {
331 let text = interpretation
332 .homomorphism()
333 .get(rule.symbol)
334 .map(|root| {
335 format_hom_term(
336 interpretation.homomorphism().arena(),
337 root,
338 interpretation.algebra_signature(),
339 )
340 })
341 .unwrap_or_default();
342 (interpretation.name().to_owned(), text)
343 })
344 .collect();
345
346 ResolvedRule {
347 parent: self.states().resolve(rule.result).clone(),
348 parent_is_final: self.grammar().is_accepting(&rule.result),
349 symbol: self.grammar_signature().resolve(rule.symbol).to_owned(),
350 children: rule
351 .children
352 .iter()
353 .map(|&state| self.states().resolve(state).clone())
354 .collect(),
355 weight: rule.weight,
356 interpretation_terms,
357 }
358 })
359 .collect()
360 }
361
362 pub fn num_grammar_rules(&self) -> usize {
364 self.grammar().num_rules()
365 }
366
367 pub fn resolved_grammar_rule(&self, index: usize) -> ResolvedRule {
373 let mut interpretations: Vec<_> = self.interpretations().collect();
374 interpretations.sort_by_key(|interpretation| interpretation.name());
375
376 let rule = self.grammar().rule(index);
377 let interpretation_terms = interpretations
378 .iter()
379 .map(|interpretation| {
380 let text = interpretation
381 .homomorphism()
382 .get(rule.symbol)
383 .map(|root| {
384 format_hom_term(
385 interpretation.homomorphism().arena(),
386 root,
387 interpretation.algebra_signature(),
388 )
389 })
390 .unwrap_or_default();
391 (interpretation.name().to_owned(), text)
392 })
393 .collect();
394
395 ResolvedRule {
396 parent: self.states().resolve(rule.result).clone(),
397 parent_is_final: self.grammar().is_accepting(&rule.result),
398 symbol: self.grammar_signature().resolve(rule.symbol).to_owned(),
399 children: rule
400 .children
401 .iter()
402 .map(|&state| self.states().resolve(state).clone())
403 .collect(),
404 weight: rule.weight,
405 interpretation_terms,
406 }
407 }
408
409 pub fn resolve_derivation(&self, arena: &TreeArena<Symbol>, root: Tree) -> TreeValue {
411 let (arena, root) = self.grammar_signature().resolve_tree(arena, root);
412 TreeValue::new(arena, root)
413 }
414
415 pub fn render_derivation(
417 &self,
418 arena: &TreeArena<Symbol>,
419 root: Tree,
420 ) -> Result<Vec<RenderedInterpretation>, IrtgError> {
421 let mut interpretations: Vec<_> = self.interpretations().collect();
422 interpretations.sort_by_key(|interpretation| interpretation.name());
423
424 interpretations
425 .into_iter()
426 .map(|interpretation| {
427 let text = interpretation.interpret_to_string(arena, root)?;
428 let value = if interpretation.is_tree_valued() {
429 let mut tree_arena = TreeArena::new();
430 match parse_tree(&mut tree_arena, &text) {
431 Ok(tree_root) => RenderedValue::Tree(TreeValue::new(tree_arena, tree_root)),
432 Err(_) => RenderedValue::Text(text),
433 }
434 } else {
435 RenderedValue::Text(text)
436 };
437 Ok(RenderedInterpretation {
438 name: interpretation.name().to_owned(),
439 value,
440 })
441 })
442 .collect()
443 }
444
445 pub fn evaluate_derivation(
447 &self,
448 arena: &TreeArena<Symbol>,
449 root: Tree,
450 ) -> Result<Vec<EvaluatedInterpretation>, IrtgError> {
451 let mut interpretations: Vec<_> = self.interpretations().collect();
452 interpretations.sort_by_key(|interpretation| interpretation.name());
453 interpretations
454 .into_iter()
455 .map(|interpretation| {
456 Ok(EvaluatedInterpretation {
457 name: interpretation.name().to_owned(),
458 value: interpretation.evaluate_derivation(arena, root)?,
459 })
460 })
461 .collect()
462 }
463}
464
465fn format_hom_term(
466 arena: &TreeArena<crate::HomLabel>,
467 node: Tree,
468 signature: &crate::Signature,
469) -> String {
470 let label = match arena.get_label(node) {
471 crate::HomLabel::Symbol(symbol) => signature.resolve(*symbol).to_owned(),
472 crate::HomLabel::Var(index) => format!("?{}", index + 1),
473 };
474 let children = arena.get_children(node);
475 if children.is_empty() {
476 label
477 } else {
478 let children = children
479 .iter()
480 .map(|&child| format_hom_term(arena, child, signature))
481 .collect::<Vec<_>>()
482 .join(", ");
483 format!("{label}({children})")
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490 use crate::parse_irtg;
491
492 const GRAMMAR: &str = r#"
493interpretation string: de.up.ling.irtg.algebra.StringAlgebra
494interpretation tree: de.up.ling.irtg.algebra.TreeWithAritiesAlgebra
495
496S! -> r(NP, VP) [0.7]
497 [string] *(?1, ?2)
498 [tree] S_2(?1, ?2)
499
500NP -> john [1.0]
501 [string] john
502 [tree] John_0
503
504VP -> sleeps [1.0]
505 [string] sleeps
506 [tree] sleeps_0
507"#;
508
509 #[test]
510 fn resolves_single_explicit_rule() {
511 let irtg = parse_irtg(GRAMMAR.as_bytes()).unwrap();
512 let g = irtg.grammar();
513 let all = g.resolve_rules(
514 |s| format!("q{}", s.index()),
515 |sym| irtg.grammar_signature().resolve(sym).to_owned(),
516 );
517 for (i, expected) in all.iter().enumerate() {
518 let one = g.resolved_rule(
519 i,
520 |s| format!("q{}", s.index()),
521 |sym| irtg.grammar_signature().resolve(sym).to_owned(),
522 );
523 assert_eq!(&one, expected);
524 }
525 }
526
527 #[test]
528 fn resolves_application_records() {
529 let irtg = parse_irtg(GRAMMAR.as_bytes()).unwrap();
530 let summary = irtg.grammar().application_summary();
531 assert_eq!(summary.rule_count, 3);
532 assert_eq!(summary.maximum_rank, 2);
533 let rules = irtg.resolved_grammar_rules();
534 assert_eq!(rules[0].parent, "S");
535 assert!(rules[0].parent_is_final);
536 assert_eq!(rules[0].interpretation_terms[0].0, "string");
537 }
538
539 #[test]
540 fn strategy_conversion_is_stable() {
541 assert!(matches!(
542 ParseStrategy::IndexedCondensed.materialization_strategy(),
543 MaterializationStrategy::IndexedCondensed
544 ));
545 let strategy = ParseStrategy::AstarZero {
546 stop_at_first_goal: true,
547 beam: Some(0.2),
548 }
549 .materialization_strategy();
550 assert!(matches!(strategy, MaterializationStrategy::Astar { .. }));
551 }
552
553 #[test]
554 fn resolves_single_grammar_rule() {
555 let irtg = parse_irtg(GRAMMAR.as_bytes()).unwrap();
556 let all = irtg.resolved_grammar_rules();
557 assert_eq!(irtg.num_grammar_rules(), all.len());
558 for (i, expected) in all.iter().enumerate() {
559 assert_eq!(&irtg.resolved_grammar_rule(i), expected);
560 }
561 }
562
563 #[test]
564 fn preserves_tree_results() {
565 let irtg = parse_irtg(GRAMMAR.as_bytes()).unwrap();
566 let string = irtg
567 .interpretation_ref("string")
568 .unwrap()
569 .parse_object_erased("john sleeps")
570 .unwrap();
571 let chart = irtg
572 .parse([irtg
573 .interpretation_ref("string")
574 .unwrap()
575 .input_erased(string)])
576 .unwrap();
577 assert!(chart.state_names.iter().any(|name| name == "NP[0-1]"));
578 assert!(chart.state_names.iter().any(|name| name == "VP[1-2]"));
579 assert!(chart.state_names.iter().any(|name| name == "S[0-2]"));
580 let best = chart.automaton.viterbi().unwrap();
581 let rendered = irtg.render_derivation(best.arena(), best.root()).unwrap();
582 assert!(matches!(rendered[0].value, RenderedValue::Text(_)));
583 assert!(matches!(rendered[1].value, RenderedValue::Tree(_)));
584
585 let evaluated = irtg.evaluate_derivation(best.arena(), best.root()).unwrap();
586 assert!(matches!(
587 evaluated[0].value.visual(),
588 crate::VisualRepresentation::Text(text) if text == "john sleeps"
589 ));
590 assert!(matches!(
591 evaluated[1].value.visual(),
592 crate::VisualRepresentation::Tree(_)
593 ));
594 assert_eq!(evaluated[0].value.codecs()[0].name, "string");
595 assert_eq!(evaluated[0].value.encode("STRING").unwrap(), "john sleeps");
596 assert_eq!(evaluated[1].value.codecs()[0].name, "display");
597 }
598
599 #[test]
600 fn evaluates_feature_structures_without_flattening_them() {
601 let irtg = parse_irtg(
602 br#"
603 interpretation ft: de.up.ling.irtg.algebra.FeatureStructureAlgebra
604 S! -> value
605 [ft] "[left: #x [case: nom], right: #x]"
606 "# as &[u8],
607 )
608 .unwrap();
609 let best = irtg.grammar().viterbi().unwrap();
610 let evaluated = irtg.evaluate_derivation(best.arena(), best.root()).unwrap();
611 assert!(matches!(
612 evaluated[0].value.visual(),
613 crate::VisualRepresentation::FeatureStructure(_)
614 ));
615 assert_eq!(evaluated[0].value.codecs()[0].name, "display");
616 assert!(
617 evaluated[0]
618 .value
619 .encode("display")
620 .unwrap()
621 .contains("case")
622 );
623 }
624
625 #[test]
626 fn reports_opt_in_non_null_filter_capability() {
627 let irtg = parse_irtg(
628 br#"
629 interpretation ft: de.up.ling.irtg.algebra.FeatureStructureAlgebra
630 interpretation string: de.up.ling.irtg.algebra.StringAlgebra
631 S! -> value
632 [ft] "[case: nom]"
633 [string] word
634 "# as &[u8],
635 )
636 .unwrap();
637 let info = irtg.interpretation_info();
638 let ft = info.iter().find(|item| item.name == "ft").unwrap();
639 let string = info.iter().find(|item| item.name == "string").unwrap();
640 assert!(ft.non_null_filter_capable);
641 assert!(!string.non_null_filter_capable);
642 assert!(
643 irtg.interpretation_ref("ft")
644 .unwrap()
645 .supports_non_null_filter()
646 );
647 assert!(
648 !irtg
649 .interpretation_ref("string")
650 .unwrap()
651 .supports_non_null_filter()
652 );
653 assert!(matches!(
654 irtg.filter_non_null(irtg.grammar(), "string"),
655 Err(crate::IrtgError::NonNullFilterUnsupported { .. })
656 ));
657 }
658
659 #[test]
660 fn reports_finite_and_infinite_language_cardinality() {
661 let finite = parse_irtg(GRAMMAR.as_bytes()).unwrap();
662 assert_eq!(
663 finite.grammar().language_cardinality(),
664 LanguageCardinality::Finite(1)
665 );
666
667 let recursive = parse_irtg(
668 br#"
669 interpretation string: de.up.ling.irtg.algebra.StringAlgebra
670
671 S! -> wrap(S)
672 [string] *(x, ?1)
673 S -> leaf
674 [string] x
675 "# as &[u8],
676 )
677 .unwrap();
678 assert_eq!(
679 recursive.grammar().language_cardinality(),
680 LanguageCardinality::Infinite
681 );
682 }
683}