1use crate::{
4 APPEND_SYMBOL, Algebra, BinarizedTagTreeDecompositionAutomaton, Binarizing, BottomUpTa,
5 CodecMetadata, CondensedTa, DisplayCodec, Explicit, ExplicitBuildError, ExplicitBuilder,
6 FeatureStructure, FeatureStructureAlgebra, FxHashMap, FxHashSet, Homomorphism,
7 HomomorphismError, IndexedCondensedIntersectionStats, Interner, InvHom, MinHeuristic,
8 ObligatoryLeafTables, OutputCodec, OutputCodecError, OutputCodecRegistry, OutsideHeuristic,
9 ParseControl, ScoredZeroHeuristic, Signature, SignatureError, SpaceJoinCodec, StateId,
10 StateUniverse, StringAlgebra, Symbol, TagStringAlgebra, TagStringDecompositionAutomaton,
11 TagStringValue, TagTreeAlgebra, TagTreeDecompositionAutomaton, TopDownTa, TreeAlgebra,
12 UniversalSxHeuristic, VisualRepresentation, ViterbiTree, WeightScorer, ZeroHeuristic,
13 alto_ast::{AstHomTerm, AstIrtg, AstState, LexError, Tok, lex},
14 alto_grammar,
15 astar::{
16 AstarOptions, AstarStats, PreparedAstarGrammar, astar_one_best_with_stats,
17 astar_string_one_best_with_stats, astar_string_one_best_with_stats_prepared,
18 materialize_astar_intersection_with_pairs_controlled,
19 materialize_astar_string_intersection_with_controlled,
20 },
21 materialize::{
22 materialize_indexed_condensed_intersection_with_pairs_controlled,
23 materialize_topdown_condensed_intersection,
24 materialize_topdown_condensed_intersection_with_pairs_controlled,
25 },
26};
27use lalrpop_util::ParseError;
28use packed_term_arena::tree::{Tree, TreeArena};
29use std::{
30 any::Any,
31 fmt,
32 hash::Hash,
33 io::Read,
34 marker::PhantomData,
35 sync::{Arc, Mutex},
36};
37use thiserror::Error;
38
39fn product_state_names<S: Clone + Eq + Hash + fmt::Display>(
40 left_names: &[String],
41 right_states: &Interner<S>,
42 pairs: &[(StateId, StateId)],
43) -> Vec<String> {
44 pairs
45 .iter()
46 .map(|&(left, right)| {
47 let left = left_names
48 .get(left.index())
49 .cloned()
50 .unwrap_or_else(|| format!("q{}", left.0));
51 format!("{left} × {}", right_states.resolve(right))
52 })
53 .collect()
54}
55
56fn product_state_parts<S: Clone + Eq + Hash + fmt::Display>(
57 left_parts: &[Vec<String>],
58 right_states: &Interner<S>,
59 pairs: &[(StateId, StateId)],
60) -> Vec<Vec<String>> {
61 pairs
62 .iter()
63 .map(|&(left, right)| {
64 let mut parts = left_parts
65 .get(left.index())
66 .cloned()
67 .unwrap_or_else(|| vec![format!("q{}", left.0)]);
68 parts.push(right_states.resolve(right).to_string());
69 parts
70 })
71 .collect()
72}
73
74fn string_product_state_names(
75 left_names: &[String],
76 right_states: &Interner<crate::algebras::Span>,
77 pairs: &[(StateId, StateId)],
78) -> Vec<String> {
79 pairs
80 .iter()
81 .map(|&(left, right)| {
82 let left = left_names
83 .get(left.index())
84 .cloned()
85 .unwrap_or_else(|| format!("q{}", left.0));
86 format!("{left}{}", right_states.resolve(right))
87 })
88 .collect()
89}
90
91fn filter_feature_chart(
92 chart: &Explicit,
93 algebra: &FeatureStructureAlgebra,
94 homomorphism: &Homomorphism,
95 control: &ParseControl,
96) -> Result<NonNullFilteredChart, IrtgError> {
97 control.check().map_err(|_| IrtgError::Cancelled)?;
98 let filter = InvHom::new(algebra.filter(), homomorphism);
99 let rules: Vec<_> = chart
100 .rules()
101 .map(|rule| {
102 (
103 rule.symbol,
104 rule.children.to_vec(),
105 rule.result,
106 rule.weight,
107 )
108 })
109 .collect();
110 let mut builder = ExplicitBuilder::new();
111 let mut pair_ids = FxHashMap::<(StateId, FeatureStructure), StateId>::default();
112 let mut value_ids = FxHashMap::<FeatureStructure, usize>::default();
113 let mut state_origins = Vec::<(StateId, usize)>::new();
114 let mut variants = vec![Vec::<(FeatureStructure, StateId)>::new(); chart.num_states() as usize];
115 let mut emitted = FxHashSet::<(Symbol, Vec<StateId>, StateId)>::default();
116
117 let intern = |left: StateId,
118 value: FeatureStructure,
119 builder: &mut ExplicitBuilder,
120 pair_ids: &mut FxHashMap<(StateId, FeatureStructure), StateId>,
121 value_ids: &mut FxHashMap<FeatureStructure, usize>,
122 state_origins: &mut Vec<(StateId, usize)>,
123 variants: &mut [Vec<(FeatureStructure, StateId)>]| {
124 if let Some(&id) = pair_ids.get(&(left, value.clone())) {
125 return (id, false);
126 }
127 let next_value_id = value_ids.len();
128 let value_id = *value_ids.entry(value.clone()).or_insert(next_value_id);
129 let id = builder.new_state();
130 if chart.is_accepting(&left) {
131 builder.add_accepting(id);
132 }
133 pair_ids.insert((left, value.clone()), id);
134 state_origins.push((left, value_id));
135 variants[left.index()].push((value, id));
136 (id, true)
137 };
138
139 let mut changed = true;
140 while changed {
141 control.check().map_err(|_| IrtgError::Cancelled)?;
142 changed = false;
143 for (symbol, children, result, weight) in &rules {
144 control.check().map_err(|_| IrtgError::Cancelled)?;
145 if children.is_empty() {
146 filter.step(*symbol, &[], &mut |value| {
147 if control.is_cancelled() {
148 return;
149 }
150 let (parent, is_new) = intern(
151 *result,
152 value,
153 &mut builder,
154 &mut pair_ids,
155 &mut value_ids,
156 &mut state_origins,
157 &mut variants,
158 );
159 changed |= is_new;
160 if emitted.insert((*symbol, Vec::new(), parent)) {
161 builder.add_weighted_rule(*symbol, Vec::new(), parent, *weight);
162 }
163 });
164 continue;
165 }
166 if children
167 .iter()
168 .any(|child| variants[child.index()].is_empty())
169 {
170 continue;
171 }
172 let pool_storage: Vec<_> = children
173 .iter()
174 .map(|child| variants[child.index()].clone())
175 .collect();
176 let pools: Vec<_> = pool_storage.iter().map(Vec::as_slice).collect();
177 crate::run::cartesian_product(&pools, |combination| {
178 if control.is_cancelled() {
179 return;
180 }
181 let child_values: Vec<_> =
182 combination.iter().map(|(value, _)| value.clone()).collect();
183 let child_ids: Vec<_> = combination.iter().map(|(_, id)| *id).collect();
184 filter.step(*symbol, &child_values, &mut |value| {
185 if control.is_cancelled() {
186 return;
187 }
188 let (parent, is_new) = intern(
189 *result,
190 value,
191 &mut builder,
192 &mut pair_ids,
193 &mut value_ids,
194 &mut state_origins,
195 &mut variants,
196 );
197 changed |= is_new;
198 if emitted.insert((*symbol, child_ids.clone(), parent)) {
199 builder.add_weighted_rule(*symbol, child_ids.clone(), parent, *weight);
200 }
201 });
202 });
203 control.check().map_err(|_| IrtgError::Cancelled)?;
204 }
205 }
206 Ok(NonNullFilteredChart {
207 automaton: builder.build(),
208 state_origins,
209 })
210}
211
212#[derive(Debug)]
214pub struct NonNullFilteredChart {
215 pub automaton: Explicit,
217 pub state_origins: Vec<(StateId, usize)>,
219}
220
221const STRING_ALGEBRA: &str = "de.up.ling.irtg.algebra.StringAlgebra";
222const TAG_STRING_ALGEBRA: &str = "de.up.ling.irtg.algebra.TagStringAlgebra";
223const TAG_TREE_ALGEBRA: &str = "de.up.ling.irtg.algebra.TagTreeAlgebra";
224const TAG_TREE_WITH_ARITIES_ALGEBRA: &str = "de.up.ling.irtg.algebra.TagTreeWithAritiesAlgebra";
225const BINARIZING_TAG_TREE_ALGEBRA: &str = "de.up.ling.irtg.algebra.BinarizingTagTreeAlgebra";
226const BINARIZING_TAG_TREE_WITH_ARITIES_ALGEBRA: &str =
227 "de.up.ling.irtg.algebra.BinarizingTagTreeWithAritiesAlgebra";
228const FEATURE_STRUCTURE_ALGEBRA: &str = "de.up.ling.irtg.algebra.FeatureStructureAlgebra";
229const TREE_WITH_ARITIES_ALGEBRA: &str = "de.up.ling.irtg.algebra.TreeWithAritiesAlgebra";
230const BINARIZING_TREE_WITH_ARITIES_ALGEBRA: &str =
231 "de.up.ling.irtg.algebra.BinarizingTreeWithAritiesAlgebra";
232
233pub enum MaterializationStrategy<'h> {
257 TopDownCondensed,
266 IndexedCondensed,
276 Astar {
287 heuristic: AstarHeuristic<'h>,
289 options: AstarOptions,
291 },
292}
293
294pub enum AstarHeuristic<'h> {
296 Zero,
299 Outside(&'h OutsideHeuristic),
302 UniversalSx {
306 table: &'h UniversalSxHeuristic,
308 n: usize,
310 },
311 UniversalSxF {
317 table: &'h UniversalSxHeuristic,
319 oblig: &'h ObligatoryLeafTables,
321 n: usize,
323 },
324}
325
326#[derive(Debug)]
328pub struct Irtg {
329 grammar: Explicit,
330 states: Interner<String>,
331 grammar_signature: Signature,
332 interpretations: FxHashMap<String, Interpretation>,
333}
334
335impl Irtg {
336 pub fn grammar(&self) -> &Explicit {
338 &self.grammar
339 }
340
341 pub fn grammar_signature(&self) -> &Signature {
343 &self.grammar_signature
344 }
345
346 pub fn states(&self) -> &Interner<String> {
348 &self.states
349 }
350
351 pub fn string_interpretation_names(&self) -> Vec<&str> {
353 let mut names: Vec<_> = self
354 .interpretations
355 .values()
356 .filter(|interpretation| interpretation.kind == InterpretationKind::String)
357 .map(|interpretation| interpretation.name.as_str())
358 .collect();
359 names.sort_unstable();
360 names
361 }
362
363 pub fn input_interpretation_names(&self) -> Vec<&str> {
365 let mut names: Vec<_> = self
366 .interpretations
367 .values()
368 .filter(|interpretation| interpretation.is_inputable())
369 .map(|interpretation| interpretation.name.as_str())
370 .collect();
371 names.sort_unstable();
372 names
373 }
374
375 pub fn filter_non_null(
378 &self,
379 chart: &Explicit,
380 interpretation_name: &str,
381 ) -> Result<Explicit, IrtgError> {
382 Ok(self
383 .filter_non_null_with_state_origins(chart, interpretation_name)?
384 .automaton)
385 }
386
387 pub fn filter_non_null_with_state_origins(
389 &self,
390 chart: &Explicit,
391 interpretation_name: &str,
392 ) -> Result<NonNullFilteredChart, IrtgError> {
393 self.filter_non_null_with_state_origins_controlled(
394 chart,
395 interpretation_name,
396 &ParseControl::new(),
397 )
398 }
399
400 pub fn filter_non_null_with_state_origins_controlled(
410 &self,
411 chart: &Explicit,
412 interpretation_name: &str,
413 control: &ParseControl,
414 ) -> Result<NonNullFilteredChart, IrtgError> {
415 let interpretation = self
416 .interpretations
417 .get(interpretation_name)
418 .ok_or_else(|| IrtgError::UnknownInterpretation(interpretation_name.to_owned()))?;
419 if !interpretation.supports_non_null_filter() {
420 return Err(IrtgError::NonNullFilterUnsupported {
421 interpretation: interpretation_name.to_owned(),
422 class_name: interpretation.class_name.clone(),
423 });
424 }
425 let algebra = interpretation.algebra.lock().unwrap();
426 let algebra = algebra
427 .as_ref()
428 .downcast_ref::<FeatureStructureAlgebra>()
429 .expect("non-null filter capability must match its algebra");
430 filter_feature_chart(chart, algebra, &interpretation.homomorphism, control)
431 }
432
433 pub fn interpretations(&self) -> impl Iterator<Item = &Interpretation> {
435 self.interpretations.values()
436 }
437
438 pub fn interpretation_ref(&self, name: &str) -> Option<&Interpretation> {
440 self.interpretations.get(name)
441 }
442
443 pub fn interpretation<A>(&self, name: &str) -> Result<TypedInterpretation<'_, A>, IrtgError>
445 where
446 A: Algebra + 'static,
447 {
448 let interpretation = self
449 .interpretations
450 .get(name)
451 .ok_or_else(|| IrtgError::UnknownInterpretation(name.to_owned()))?;
452 if interpretation.algebra.lock().unwrap().as_ref().is::<A>() {
453 Ok(TypedInterpretation {
454 interpretation,
455 _algebra: PhantomData,
456 })
457 } else {
458 Err(IrtgError::WrongAlgebraType {
459 interpretation: name.to_owned(),
460 requested: std::any::type_name::<A>(),
461 actual: interpretation.class_name.clone(),
462 })
463 }
464 }
465
466 pub fn parse<'a>(
469 &self,
470 inputs: impl IntoIterator<Item = ParseInput<'a>>,
471 ) -> Result<ParseChart, IrtgError> {
472 self.parse_with(inputs, &MaterializationStrategy::TopDownCondensed)
473 }
474
475 pub fn parse_with<'a>(
482 &self,
483 inputs: impl IntoIterator<Item = ParseInput<'a>>,
484 strategy: &MaterializationStrategy<'_>,
485 ) -> Result<ParseChart, IrtgError> {
486 self.parse_with_control(inputs, strategy, &ParseControl::new())
487 }
488
489 pub fn parse_with_control<'a>(
499 &self,
500 inputs: impl IntoIterator<Item = ParseInput<'a>>,
501 strategy: &MaterializationStrategy<'_>,
502 control: &ParseControl,
503 ) -> Result<ParseChart, IrtgError> {
504 control.check().map_err(|_| IrtgError::Cancelled)?;
505 if matches!(strategy, MaterializationStrategy::Astar { .. }) {
507 self.check_astar_weight_precondition()?;
508 }
509
510 let mut chart = self.grammar.clone();
511 let mut state_names = (0..chart.num_states())
512 .map(|state| self.states.resolve(StateId(state)).clone())
513 .collect::<Vec<_>>();
514 let mut state_parts = state_names
515 .iter()
516 .cloned()
517 .map(|name| vec![name])
518 .collect::<Vec<_>>();
519 let mut stats = Vec::new();
520
521 for input in inputs {
522 control.check().map_err(|_| IrtgError::Cancelled)?;
523 let interpretation = input.interpretation;
524 match interpretation.kind {
525 InterpretationKind::String => {
526 let value = *input.value.downcast::<Vec<Symbol>>().map_err(|_| {
527 IrtgError::WrongInputType {
528 interpretation: interpretation.name.clone(),
529 }
530 })?;
531 let decomp = interpretation.decompose_string(value)?;
532 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
533 let next_chart = match strategy {
534 MaterializationStrategy::TopDownCondensed => {
535 let (c, right_states, pairs, stat) =
536 materialize_topdown_condensed_intersection_with_pairs_controlled(
537 &chart, &invhom, control,
538 )
539 .map_err(|_| IrtgError::Cancelled)?;
540 state_names =
541 string_product_state_names(&state_names, &right_states, &pairs);
542 state_parts = product_state_parts(&state_parts, &right_states, &pairs);
543 stats.push(stat);
544 c
545 }
546 MaterializationStrategy::IndexedCondensed => {
547 let (c, right_states, pairs, stat) =
548 materialize_indexed_condensed_intersection_with_pairs_controlled(
549 &chart, &invhom, control,
550 )
551 .map_err(|_| IrtgError::Cancelled)?;
552 state_names =
553 string_product_state_names(&state_names, &right_states, &pairs);
554 state_parts = product_state_parts(&state_parts, &right_states, &pairs);
555 stats.push(stat);
556 c
557 }
558 MaterializationStrategy::Astar {
559 heuristic,
560 options: _,
561 } => {
562 let (c, right_states, pairs) = self
566 .run_astar_chart(&chart, &invhom, heuristic, strategy, control)?;
567 state_names =
568 string_product_state_names(&state_names, &right_states, &pairs);
569 state_parts = product_state_parts(&state_parts, &right_states, &pairs);
570 c
572 }
573 };
574 chart = next_chart;
575 }
576 InterpretationKind::TagString => {
577 let value =
578 *input
579 .value
580 .downcast::<TagStringValue<Symbol>>()
581 .map_err(|_| IrtgError::WrongInputType {
582 interpretation: interpretation.name.clone(),
583 })?;
584 let decomp = interpretation.decompose_tag_string(value)?;
585 let (next_chart, next_names, next_parts, stat) = self.run_generic_chart(
586 &chart,
587 &state_names,
588 &state_parts,
589 decomp,
590 &interpretation.homomorphism,
591 strategy,
592 &interpretation.name,
593 control,
594 )?;
595 if let Some(stat) = stat {
596 stats.push(stat);
597 }
598 chart = next_chart;
599 state_names = next_names;
600 state_parts = next_parts;
601 }
602 InterpretationKind::TagTree => {
603 let value =
604 *input
605 .value
606 .downcast::<Tree>()
607 .map_err(|_| IrtgError::WrongInputType {
608 interpretation: interpretation.name.clone(),
609 })?;
610 let decomp = interpretation.decompose_tag_tree(value)?;
611 let (next_chart, next_names, next_parts, stat) = self.run_generic_chart(
612 &chart,
613 &state_names,
614 &state_parts,
615 decomp,
616 &interpretation.homomorphism,
617 strategy,
618 &interpretation.name,
619 control,
620 )?;
621 if let Some(stat) = stat {
622 stats.push(stat);
623 }
624 chart = next_chart;
625 state_names = next_names;
626 state_parts = next_parts;
627 }
628 InterpretationKind::BinarizedTagTree => {
629 let value =
630 *input
631 .value
632 .downcast::<Tree>()
633 .map_err(|_| IrtgError::WrongInputType {
634 interpretation: interpretation.name.clone(),
635 })?;
636 let decomp = interpretation.decompose_binarized_tag_tree(value)?;
637 let (next_chart, next_names, next_parts, stat) = self.run_generic_chart(
638 &chart,
639 &state_names,
640 &state_parts,
641 decomp,
642 &interpretation.homomorphism,
643 strategy,
644 &interpretation.name,
645 control,
646 )?;
647 if let Some(stat) = stat {
648 stats.push(stat);
649 }
650 chart = next_chart;
651 state_names = next_names;
652 state_parts = next_parts;
653 }
654 InterpretationKind::OutputOnly => {
655 return Err(IrtgError::NotInputable {
656 interpretation: interpretation.name.clone(),
657 });
658 }
659 }
660 }
661
662 Ok(ParseChart {
663 automaton: chart,
664 state_names,
665 state_parts,
666 stats,
667 })
668 }
669
670 pub fn best_with<'a>(
677 &self,
678 inputs: impl IntoIterator<Item = ParseInput<'a>>,
679 strategy: &MaterializationStrategy<'_>,
680 ) -> Result<Option<ViterbiTree>, IrtgError> {
681 self.best_with_scorer(inputs, strategy, &crate::ProbabilityScorer)
682 }
683
684 pub fn best_with_scorer<'a, S: WeightScorer>(
686 &self,
687 inputs: impl IntoIterator<Item = ParseInput<'a>>,
688 strategy: &MaterializationStrategy<'_>,
689 scorer: &S,
690 ) -> Result<Option<ViterbiTree>, IrtgError> {
691 self.best_with_scorer_and_stats(inputs, strategy, scorer)
692 .map(|(tree, _)| tree)
693 }
694
695 pub fn best_with_scorer_and_stats<'a, S: WeightScorer>(
697 &self,
698 inputs: impl IntoIterator<Item = ParseInput<'a>>,
699 strategy: &MaterializationStrategy<'_>,
700 scorer: &S,
701 ) -> Result<(Option<ViterbiTree>, Option<AstarStats>), IrtgError> {
702 self.best_with_scorer_and_stats_impl(inputs, strategy, scorer, None)
703 }
704
705 pub fn best_with_scorer_and_stats_prepared<'a, S: WeightScorer>(
708 &self,
709 inputs: impl IntoIterator<Item = ParseInput<'a>>,
710 strategy: &MaterializationStrategy<'_>,
711 scorer: &S,
712 prepared: &PreparedAstarGrammar,
713 ) -> Result<(Option<ViterbiTree>, Option<AstarStats>), IrtgError> {
714 self.best_with_scorer_and_stats_impl(inputs, strategy, scorer, Some(prepared))
715 }
716
717 fn best_with_scorer_and_stats_impl<'a, S: WeightScorer>(
718 &self,
719 inputs: impl IntoIterator<Item = ParseInput<'a>>,
720 strategy: &MaterializationStrategy<'_>,
721 scorer: &S,
722 prepared: Option<&PreparedAstarGrammar>,
723 ) -> Result<(Option<ViterbiTree>, Option<AstarStats>), IrtgError> {
724 if let MaterializationStrategy::Astar { heuristic, .. } = strategy {
726 self.check_astar_weight_precondition()?;
728
729 let inputs: Vec<_> = inputs.into_iter().collect();
735 if inputs.is_empty() {
736 return Ok((self.grammar.viterbi_with(scorer), None));
737 }
738
739 let (last, rest) = inputs.split_last().unwrap();
741 if !rest.is_empty() {
743 return self.best_with_multi(inputs, heuristic, strategy, scorer);
755 }
756
757 let interpretation = last.interpretation;
759 match interpretation.kind {
760 InterpretationKind::String => {
761 let value = last
762 .value
763 .downcast_ref::<Vec<Symbol>>()
764 .ok_or_else(|| IrtgError::WrongInputType {
765 interpretation: interpretation.name.clone(),
766 })?
767 .clone();
768 let decomp = interpretation.decompose_string(value)?;
769 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
770 let (tree, stats) = self.run_astar_one_best_with_scorer(
771 &self.grammar,
772 &invhom,
773 heuristic,
774 scorer,
775 prepared,
776 );
777 Ok((tree, Some(stats)))
778 }
779 InterpretationKind::TagString => {
780 let value = last
781 .value
782 .downcast_ref::<TagStringValue<Symbol>>()
783 .ok_or_else(|| IrtgError::WrongInputType {
784 interpretation: interpretation.name.clone(),
785 })?
786 .clone();
787 let decomp = interpretation.decompose_tag_string(value)?;
788 let (tree, stats) = self.run_generic_one_best(
789 &self.grammar,
790 decomp,
791 &interpretation.homomorphism,
792 heuristic,
793 scorer,
794 &interpretation.name,
795 )?;
796 Ok((tree, Some(stats)))
797 }
798 InterpretationKind::TagTree => {
799 let value = *last.value.downcast_ref::<Tree>().ok_or_else(|| {
800 IrtgError::WrongInputType {
801 interpretation: interpretation.name.clone(),
802 }
803 })?;
804 let decomp = interpretation.decompose_tag_tree(value)?;
805 let (tree, stats) = self.run_generic_one_best(
806 &self.grammar,
807 decomp,
808 &interpretation.homomorphism,
809 heuristic,
810 scorer,
811 &interpretation.name,
812 )?;
813 Ok((tree, Some(stats)))
814 }
815 InterpretationKind::BinarizedTagTree => {
816 let value = *last.value.downcast_ref::<Tree>().ok_or_else(|| {
817 IrtgError::WrongInputType {
818 interpretation: interpretation.name.clone(),
819 }
820 })?;
821 let decomp = interpretation.decompose_binarized_tag_tree(value)?;
822 let (tree, stats) = self.run_generic_one_best(
823 &self.grammar,
824 decomp,
825 &interpretation.homomorphism,
826 heuristic,
827 scorer,
828 &interpretation.name,
829 )?;
830 Ok((tree, Some(stats)))
831 }
832 InterpretationKind::OutputOnly => Err(IrtgError::NotInputable {
833 interpretation: interpretation.name.clone(),
834 }),
835 }
836 } else {
837 let chart = self.parse_with(inputs, strategy)?;
839 Ok((chart.automaton.viterbi_with(scorer), None))
840 }
841 }
842
843 #[allow(clippy::too_many_arguments)]
848 fn run_generic_chart<R>(
849 &self,
850 chart: &Explicit,
851 left_state_names: &[String],
852 left_state_parts: &[Vec<String>],
853 decomp: R,
854 homomorphism: &Homomorphism,
855 strategy: &MaterializationStrategy<'_>,
856 interpretation: &str,
857 control: &ParseControl,
858 ) -> Result<
859 (
860 Explicit,
861 Vec<String>,
862 Vec<Vec<String>>,
863 Option<IndexedCondensedIntersectionStats>,
864 ),
865 IrtgError,
866 >
867 where
868 R: CondensedTa + StateUniverse + TopDownTa,
869 R::State: Clone + Eq + Hash + fmt::Display,
870 {
871 let invhom = InvHom::new(decomp, homomorphism);
872 match strategy {
873 MaterializationStrategy::TopDownCondensed => {
874 let (chart, right_states, pairs, stats) =
875 materialize_topdown_condensed_intersection_with_pairs_controlled(
876 chart, &invhom, control,
877 )
878 .map_err(|_| IrtgError::Cancelled)?;
879 let names = product_state_names(left_state_names, &right_states, &pairs);
880 let parts = product_state_parts(left_state_parts, &right_states, &pairs);
881 Ok((chart, names, parts, Some(stats)))
882 }
883 MaterializationStrategy::IndexedCondensed => {
884 let (chart, right_states, pairs, stats) =
885 materialize_indexed_condensed_intersection_with_pairs_controlled(
886 chart, &invhom, control,
887 )
888 .map_err(|_| IrtgError::Cancelled)?;
889 let names = product_state_names(left_state_names, &right_states, &pairs);
890 let parts = product_state_parts(left_state_parts, &right_states, &pairs);
891 Ok((chart, names, parts, Some(stats)))
892 }
893 MaterializationStrategy::Astar { heuristic, options } => {
894 let options = AstarOptions {
895 stop_at_first_goal: options.stop_at_first_goal,
896 beam: options.beam,
897 };
898 let (chart, right_states, pairs) = match heuristic {
899 AstarHeuristic::Zero => {
900 let heuristic = ZeroHeuristic;
901 let (chart, states, pairs, _) =
902 materialize_astar_intersection_with_pairs_controlled(
903 chart,
904 &invhom,
905 &heuristic,
906 options,
907 &crate::ProbabilityScorer,
908 control,
909 );
910 control.check().map_err(|_| IrtgError::Cancelled)?;
911 (chart, states, pairs)
912 }
913 AstarHeuristic::Outside(heuristic) => {
914 let (chart, states, pairs, _) =
915 materialize_astar_intersection_with_pairs_controlled(
916 chart,
917 &invhom,
918 *heuristic,
919 options,
920 &crate::ProbabilityScorer,
921 control,
922 );
923 control.check().map_err(|_| IrtgError::Cancelled)?;
924 (chart, states, pairs)
925 }
926 AstarHeuristic::UniversalSx { .. } | AstarHeuristic::UniversalSxF { .. } => {
927 return Err(IrtgError::IncompatibleHeuristic {
928 interpretation: interpretation.to_owned(),
929 message: "SX and SXF heuristics require a StringAlgebra interpretation"
930 .to_owned(),
931 });
932 }
933 };
934 let names = product_state_names(left_state_names, &right_states, &pairs);
935 let parts = product_state_parts(left_state_parts, &right_states, &pairs);
936 Ok((chart, names, parts, None))
937 }
938 }
939 }
940
941 fn run_generic_one_best<R, S>(
942 &self,
943 chart: &Explicit,
944 decomp: R,
945 homomorphism: &Homomorphism,
946 heuristic: &AstarHeuristic<'_>,
947 scorer: &S,
948 interpretation: &str,
949 ) -> Result<(Option<ViterbiTree>, AstarStats), IrtgError>
950 where
951 R: CondensedTa + StateUniverse,
952 R::State: Clone + Eq + Hash,
953 S: WeightScorer,
954 {
955 let invhom = InvHom::new(decomp, homomorphism);
956 match heuristic {
957 AstarHeuristic::Zero => {
958 let heuristic = ScoredZeroHeuristic::new(scorer);
959 Ok(astar_one_best_with_stats(
960 chart, &invhom, &heuristic, scorer,
961 ))
962 }
963 AstarHeuristic::Outside(heuristic) => Ok(astar_one_best_with_stats(
964 chart, &invhom, *heuristic, scorer,
965 )),
966 AstarHeuristic::UniversalSx { .. } | AstarHeuristic::UniversalSxF { .. } => {
967 Err(IrtgError::IncompatibleHeuristic {
968 interpretation: interpretation.to_owned(),
969 message: "SX and SXF heuristics require a StringAlgebra interpretation"
970 .to_owned(),
971 })
972 }
973 }
974 }
975
976 fn check_astar_weight_precondition(&self) -> Result<(), IrtgError> {
978 for rule in self.grammar.rules() {
979 if rule.weight > 1.0 {
980 let symbol = if (rule.symbol.0 as usize) < self.grammar_signature.len() {
983 self.grammar_signature.resolve(rule.symbol).to_owned()
984 } else {
985 format!("{:?}", rule.symbol)
986 };
987 return Err(IrtgError::AstarWeightPrecondition {
988 weight: rule.weight,
989 symbol,
990 });
991 }
992 }
993 Ok(())
994 }
995
996 fn run_astar_chart(
998 &self,
999 chart: &Explicit,
1000 invhom: &InvHom<'_, crate::algebras::StringDecompositionAutomaton>,
1001 heuristic: &AstarHeuristic<'_>,
1002 strategy: &MaterializationStrategy<'_>,
1003 control: &ParseControl,
1004 ) -> Result<
1005 (
1006 Explicit,
1007 Interner<crate::algebras::Span>,
1008 Vec<(StateId, StateId)>,
1009 ),
1010 IrtgError,
1011 > {
1012 let options = match strategy {
1013 MaterializationStrategy::Astar { options, .. } => AstarOptions {
1014 stop_at_first_goal: options.stop_at_first_goal,
1015 beam: options.beam,
1016 },
1017 _ => unreachable!(),
1018 };
1019 let (new_chart, right_states, pairs, _) = match heuristic {
1020 AstarHeuristic::Zero => {
1021 let h = ZeroHeuristic;
1022 materialize_astar_string_intersection_with_controlled(
1023 chart,
1024 invhom,
1025 &h,
1026 options,
1027 &crate::ProbabilityScorer,
1028 control,
1029 )
1030 }
1031 AstarHeuristic::Outside(h) => materialize_astar_string_intersection_with_controlled(
1032 chart,
1033 invhom,
1034 *h,
1035 options,
1036 &crate::ProbabilityScorer,
1037 control,
1038 ),
1039 AstarHeuristic::UniversalSx { table, n } => {
1040 let h = table.for_sentence(*n);
1041 materialize_astar_string_intersection_with_controlled(
1042 chart,
1043 invhom,
1044 &h,
1045 options,
1046 &crate::ProbabilityScorer,
1047 control,
1048 )
1049 }
1050 AstarHeuristic::UniversalSxF { table, oblig, n } => {
1051 let sx = table.for_sentence(*n);
1052 let f = oblig.for_sentence(invhom.inner().sentence(), &crate::ProbabilityScorer);
1053 let h = MinHeuristic::new(sx, f);
1054 materialize_astar_string_intersection_with_controlled(
1055 chart,
1056 invhom,
1057 &h,
1058 options,
1059 &crate::ProbabilityScorer,
1060 control,
1061 )
1062 }
1063 };
1064 control.check().map_err(|_| IrtgError::Cancelled)?;
1065 Ok((new_chart, right_states, pairs))
1066 }
1067
1068 fn run_astar_one_best_with_scorer<S: WeightScorer>(
1070 &self,
1071 chart: &Explicit,
1072 invhom: &InvHom<'_, crate::algebras::StringDecompositionAutomaton>,
1073 heuristic: &AstarHeuristic<'_>,
1074 scorer: &S,
1075 prepared: Option<&PreparedAstarGrammar>,
1076 ) -> (Option<ViterbiTree>, AstarStats) {
1077 macro_rules! run {
1078 ($h:expr) => {
1079 if let Some(prepared) = prepared {
1080 astar_string_one_best_with_stats_prepared(chart, prepared, invhom, $h, scorer)
1081 } else {
1082 astar_string_one_best_with_stats(chart, invhom, $h, scorer)
1083 }
1084 };
1085 }
1086 match heuristic {
1087 AstarHeuristic::Zero => {
1088 let h = ScoredZeroHeuristic::new(scorer);
1089 run!(&h)
1090 }
1091 AstarHeuristic::Outside(h) => run!(*h),
1092 AstarHeuristic::UniversalSx { table, n } => {
1093 let h = table.for_sentence(*n);
1094 run!(&h)
1095 }
1096 AstarHeuristic::UniversalSxF { table, oblig, n } => {
1097 let sx = table.for_sentence(*n);
1098 let f = oblig.for_sentence(invhom.inner().sentence(), scorer);
1099 let h = MinHeuristic::new(sx, f);
1100 run!(&h)
1101 }
1102 }
1103 }
1104
1105 fn best_with_multi<'a, S: WeightScorer>(
1108 &self,
1109 inputs: Vec<ParseInput<'a>>,
1110 heuristic: &AstarHeuristic<'_>,
1111 _strategy: &MaterializationStrategy<'_>,
1112 scorer: &S,
1113 ) -> Result<(Option<ViterbiTree>, Option<AstarStats>), IrtgError> {
1114 let n = inputs.len();
1116 let mut chart = self.grammar.clone();
1117
1118 for (i, input) in inputs.into_iter().enumerate() {
1119 let interpretation = input.interpretation;
1120 let is_last = i == n - 1;
1121 match interpretation.kind {
1122 InterpretationKind::String => {
1123 let value = *input.value.downcast::<Vec<Symbol>>().map_err(|_| {
1124 IrtgError::WrongInputType {
1125 interpretation: interpretation.name.clone(),
1126 }
1127 })?;
1128 let decomp = interpretation.decompose_string(value)?;
1129 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
1130 if is_last {
1131 let (tree, stats) = self.run_astar_one_best_with_scorer(
1132 &chart, &invhom, heuristic, scorer, None,
1133 );
1134 return Ok((tree, Some(stats)));
1135 } else {
1136 let (next, _, _) =
1137 materialize_topdown_condensed_intersection(&chart, &invhom);
1138 chart = next;
1139 }
1140 }
1141 InterpretationKind::TagString => {
1142 let value =
1143 *input
1144 .value
1145 .downcast::<TagStringValue<Symbol>>()
1146 .map_err(|_| IrtgError::WrongInputType {
1147 interpretation: interpretation.name.clone(),
1148 })?;
1149 let decomp = interpretation.decompose_tag_string(value)?;
1150 if is_last {
1151 let (tree, stats) = self.run_generic_one_best(
1152 &chart,
1153 decomp,
1154 &interpretation.homomorphism,
1155 heuristic,
1156 scorer,
1157 &interpretation.name,
1158 )?;
1159 return Ok((tree, Some(stats)));
1160 }
1161 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
1162 chart = materialize_topdown_condensed_intersection(&chart, &invhom).0;
1163 }
1164 InterpretationKind::TagTree => {
1165 let value =
1166 *input
1167 .value
1168 .downcast::<Tree>()
1169 .map_err(|_| IrtgError::WrongInputType {
1170 interpretation: interpretation.name.clone(),
1171 })?;
1172 let decomp = interpretation.decompose_tag_tree(value)?;
1173 if is_last {
1174 let (tree, stats) = self.run_generic_one_best(
1175 &chart,
1176 decomp,
1177 &interpretation.homomorphism,
1178 heuristic,
1179 scorer,
1180 &interpretation.name,
1181 )?;
1182 return Ok((tree, Some(stats)));
1183 }
1184 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
1185 chart = materialize_topdown_condensed_intersection(&chart, &invhom).0;
1186 }
1187 InterpretationKind::BinarizedTagTree => {
1188 let value =
1189 *input
1190 .value
1191 .downcast::<Tree>()
1192 .map_err(|_| IrtgError::WrongInputType {
1193 interpretation: interpretation.name.clone(),
1194 })?;
1195 let decomp = interpretation.decompose_binarized_tag_tree(value)?;
1196 if is_last {
1197 let (tree, stats) = self.run_generic_one_best(
1198 &chart,
1199 decomp,
1200 &interpretation.homomorphism,
1201 heuristic,
1202 scorer,
1203 &interpretation.name,
1204 )?;
1205 return Ok((tree, Some(stats)));
1206 }
1207 let invhom = InvHom::new(decomp, &interpretation.homomorphism);
1208 chart = materialize_topdown_condensed_intersection(&chart, &invhom).0;
1209 }
1210 InterpretationKind::OutputOnly => {
1211 return Err(IrtgError::NotInputable {
1212 interpretation: interpretation.name.clone(),
1213 });
1214 }
1215 }
1216 }
1217
1218 Ok((chart.viterbi_with(scorer), None))
1219 }
1220}
1221
1222trait DerivationRenderer: fmt::Debug + Send + Sync {
1228 fn render(
1229 &self,
1230 algebra: &dyn Any,
1231 homomorphism: &Homomorphism,
1232 arena: &TreeArena<Symbol>,
1233 root: Tree,
1234 name: &str,
1235 ) -> Result<String, IrtgError>;
1236
1237 fn evaluate(
1238 &self,
1239 algebra: &dyn Any,
1240 homomorphism: &Homomorphism,
1241 arena: &TreeArena<Symbol>,
1242 root: Tree,
1243 name: &str,
1244 ) -> Result<EvaluatedAlgebraValue, IrtgError>;
1245}
1246
1247struct TypedDerivationCodec<A, C> {
1249 codec: C,
1250 output_codecs: Arc<OutputCodecRegistry>,
1251 _algebra: PhantomData<fn() -> A>,
1252}
1253
1254impl<A, C> TypedDerivationCodec<A, C> {
1255 fn new(codec: C) -> Self {
1256 Self {
1257 codec,
1258 output_codecs: Arc::new(OutputCodecRegistry::standard()),
1259 _algebra: PhantomData,
1260 }
1261 }
1262}
1263
1264impl<A, C: fmt::Debug> fmt::Debug for TypedDerivationCodec<A, C> {
1266 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1267 f.debug_struct("TypedDerivationCodec")
1268 .field("codec", &self.codec)
1269 .finish()
1270 }
1271}
1272
1273impl<A, C> DerivationRenderer for TypedDerivationCodec<A, C>
1274where
1275 A: Algebra + 'static,
1276 A: Send,
1277 A::Value: Send + Sync + 'static,
1278 C: OutputCodec<A::Value, Output = String> + fmt::Debug + Send + Sync,
1279{
1280 fn render(
1281 &self,
1282 algebra: &dyn Any,
1283 homomorphism: &Homomorphism,
1284 arena: &TreeArena<Symbol>,
1285 root: Tree,
1286 name: &str,
1287 ) -> Result<String, IrtgError> {
1288 let algebra = algebra
1289 .downcast_ref::<A>()
1290 .ok_or_else(|| IrtgError::WrongInputType {
1291 interpretation: name.to_owned(),
1292 })?;
1293 let mut image = TreeArena::new();
1295 let image_root = homomorphism.apply(arena, root, &mut image)?;
1296 let value =
1297 algebra
1298 .evaluate_term(&image, image_root)
1299 .ok_or_else(|| IrtgError::ObjectParse {
1300 interpretation: name.to_owned(),
1301 message: "derivation tree did not evaluate in the algebra".to_owned(),
1302 })?;
1303 Ok(self.codec.encode(&value))
1304 }
1305
1306 fn evaluate(
1307 &self,
1308 algebra: &dyn Any,
1309 homomorphism: &Homomorphism,
1310 arena: &TreeArena<Symbol>,
1311 root: Tree,
1312 name: &str,
1313 ) -> Result<EvaluatedAlgebraValue, IrtgError> {
1314 let algebra = algebra
1315 .downcast_ref::<A>()
1316 .ok_or_else(|| IrtgError::WrongInputType {
1317 interpretation: name.to_owned(),
1318 })?;
1319 let mut image = TreeArena::new();
1320 let image_root = homomorphism.apply(arena, root, &mut image)?;
1321 let value =
1322 algebra
1323 .evaluate_term(&image, image_root)
1324 .ok_or_else(|| IrtgError::ObjectParse {
1325 interpretation: name.to_owned(),
1326 message: "derivation tree did not evaluate in the algebra".to_owned(),
1327 })?;
1328 let visual = algebra.visualize(&value);
1329 Ok(EvaluatedAlgebraValue {
1330 inner: Box::new(TypedEvaluatedAlgebraValue {
1331 value,
1332 visual,
1333 output_codecs: self.output_codecs.clone(),
1334 }),
1335 })
1336 }
1337}
1338
1339trait ErasedEvaluatedAlgebraValue: fmt::Debug + Send + Sync {
1340 fn visual(&self) -> &VisualRepresentation;
1341 fn codecs(&self) -> Vec<CodecMetadata>;
1342 fn encode(&self, codec_name: &str) -> Result<String, OutputCodecError>;
1343}
1344
1345struct TypedEvaluatedAlgebraValue<V> {
1346 value: V,
1347 visual: VisualRepresentation,
1348 output_codecs: Arc<OutputCodecRegistry>,
1349}
1350
1351impl<V> fmt::Debug for TypedEvaluatedAlgebraValue<V> {
1352 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1353 f.debug_struct("TypedEvaluatedAlgebraValue")
1354 .field("visual", &self.visual)
1355 .finish_non_exhaustive()
1356 }
1357}
1358
1359impl<V: Send + Sync + 'static> ErasedEvaluatedAlgebraValue for TypedEvaluatedAlgebraValue<V> {
1360 fn visual(&self) -> &VisualRepresentation {
1361 &self.visual
1362 }
1363
1364 fn codecs(&self) -> Vec<CodecMetadata> {
1365 self.output_codecs
1366 .codecs_for::<V>()
1367 .iter()
1368 .map(|codec| *codec.metadata())
1369 .collect()
1370 }
1371
1372 fn encode(&self, codec_name: &str) -> Result<String, OutputCodecError> {
1373 Ok(self
1374 .output_codecs
1375 .codec_for_name::<V>(codec_name)?
1376 .encode(&self.value))
1377 }
1378}
1379
1380#[derive(Debug)]
1382pub struct EvaluatedAlgebraValue {
1383 inner: Box<dyn ErasedEvaluatedAlgebraValue>,
1384}
1385
1386impl EvaluatedAlgebraValue {
1387 pub fn visual(&self) -> &VisualRepresentation {
1389 self.inner.visual()
1390 }
1391
1392 pub fn codecs(&self) -> Vec<CodecMetadata> {
1394 self.inner.codecs()
1395 }
1396
1397 pub fn encode(&self, codec_name: &str) -> Result<String, OutputCodecError> {
1399 self.inner.encode(codec_name)
1400 }
1401}
1402
1403#[derive(Debug)]
1405pub struct Interpretation {
1406 name: String,
1407 class_name: String,
1408 kind: InterpretationKind,
1409 algebra: Mutex<Box<dyn Any + Send>>,
1410 algebra_signature: Signature,
1411 homomorphism: Homomorphism,
1412 renderer: Box<dyn DerivationRenderer>,
1413}
1414
1415impl Interpretation {
1416 fn decompose_string(
1417 &self,
1418 value: Vec<Symbol>,
1419 ) -> Result<crate::StringDecompositionAutomaton, IrtgError> {
1420 let algebra = self.algebra.lock().unwrap();
1421 let algebra = algebra
1422 .as_ref()
1423 .downcast_ref::<StringAlgebra>()
1424 .ok_or_else(|| IrtgError::WrongInputType {
1425 interpretation: self.name.clone(),
1426 })?;
1427 Ok(algebra.decompose(value))
1428 }
1429
1430 fn decompose_tag_string(
1431 &self,
1432 value: TagStringValue<Symbol>,
1433 ) -> Result<TagStringDecompositionAutomaton, IrtgError> {
1434 let algebra = self.algebra.lock().unwrap();
1435 let algebra = algebra
1436 .as_ref()
1437 .downcast_ref::<TagStringAlgebra>()
1438 .ok_or_else(|| IrtgError::WrongInputType {
1439 interpretation: self.name.clone(),
1440 })?;
1441 algebra
1442 .decompose(value)
1443 .ok_or_else(|| IrtgError::ObjectParse {
1444 interpretation: self.name.clone(),
1445 message: "TAG decomposition inputs must be contiguous strings".to_owned(),
1446 })
1447 }
1448
1449 fn decompose_tag_tree(&self, value: Tree) -> Result<TagTreeDecompositionAutomaton, IrtgError> {
1450 let algebra = self.algebra.lock().unwrap();
1451 let algebra = algebra
1452 .as_ref()
1453 .downcast_ref::<TagTreeAlgebra>()
1454 .ok_or_else(|| IrtgError::WrongInputType {
1455 interpretation: self.name.clone(),
1456 })?;
1457 Ok(algebra.decompose(value))
1458 }
1459
1460 fn decompose_binarized_tag_tree(
1461 &self,
1462 value: Tree,
1463 ) -> Result<BinarizedTagTreeDecompositionAutomaton, IrtgError> {
1464 let algebra = self.algebra.lock().unwrap();
1465 let algebra = algebra
1466 .as_ref()
1467 .downcast_ref::<Binarizing<TagTreeAlgebra>>()
1468 .ok_or_else(|| IrtgError::WrongInputType {
1469 interpretation: self.name.clone(),
1470 })?;
1471 let append = algebra
1472 .append_symbol()
1473 .ok_or_else(|| IrtgError::ObjectParse {
1474 interpretation: self.name.clone(),
1475 message: "binarizing TAG tree algebra has no append symbol".to_owned(),
1476 })?;
1477 Ok(BinarizedTagTreeDecompositionAutomaton::new(
1478 algebra.inner().decompose_binarized(value),
1479 append,
1480 ))
1481 }
1482
1483 pub fn name(&self) -> &str {
1485 &self.name
1486 }
1487
1488 pub fn class_name(&self) -> &str {
1490 &self.class_name
1491 }
1492
1493 pub fn algebra_signature(&self) -> &Signature {
1495 &self.algebra_signature
1496 }
1497
1498 pub fn homomorphism(&self) -> &Homomorphism {
1500 &self.homomorphism
1501 }
1502
1503 pub fn is_inputable(&self) -> bool {
1506 !matches!(self.kind, InterpretationKind::OutputOnly)
1507 }
1508
1509 pub fn supports_non_null_filter(&self) -> bool {
1511 self.algebra
1512 .lock()
1513 .unwrap()
1514 .as_ref()
1515 .is::<FeatureStructureAlgebra>()
1516 }
1517
1518 pub fn is_tree_valued(&self) -> bool {
1520 matches!(
1521 self.kind,
1522 InterpretationKind::TagTree | InterpretationKind::BinarizedTagTree
1523 ) || (self.kind == InterpretationKind::OutputOnly
1524 && self.class_name != FEATURE_STRUCTURE_ALGEBRA)
1525 }
1526
1527 pub fn parse_object_erased(&self, input: &str) -> Result<Box<dyn Any + Send>, IrtgError> {
1530 match self.kind {
1531 InterpretationKind::String => {
1532 let mut algebra = self.algebra.lock().unwrap();
1533 let algebra = algebra
1534 .as_mut()
1535 .downcast_mut::<StringAlgebra>()
1536 .ok_or_else(|| IrtgError::WrongInputType {
1537 interpretation: self.name.clone(),
1538 })?;
1539 let value = algebra
1540 .parse_object(input)
1541 .map_err(|err| IrtgError::ObjectParse {
1542 interpretation: self.name.clone(),
1543 message: err.to_string(),
1544 })?;
1545 Ok(Box::new(value))
1546 }
1547 InterpretationKind::TagString => {
1548 let mut algebra = self.algebra.lock().unwrap();
1549 let algebra = algebra
1550 .as_mut()
1551 .downcast_mut::<TagStringAlgebra>()
1552 .ok_or_else(|| IrtgError::WrongInputType {
1553 interpretation: self.name.clone(),
1554 })?;
1555 let value = algebra
1556 .parse_object(input)
1557 .map_err(|err| IrtgError::ObjectParse {
1558 interpretation: self.name.clone(),
1559 message: err.to_string(),
1560 })?;
1561 Ok(Box::new(value))
1562 }
1563 InterpretationKind::TagTree => {
1564 let mut algebra = self.algebra.lock().unwrap();
1565 let algebra = algebra
1566 .as_mut()
1567 .downcast_mut::<TagTreeAlgebra>()
1568 .ok_or_else(|| IrtgError::WrongInputType {
1569 interpretation: self.name.clone(),
1570 })?;
1571 let value = algebra
1572 .parse_object(input)
1573 .map_err(|err| IrtgError::ObjectParse {
1574 interpretation: self.name.clone(),
1575 message: err.to_string(),
1576 })?;
1577 Ok(Box::new(value))
1578 }
1579 InterpretationKind::BinarizedTagTree => {
1580 let mut algebra = self.algebra.lock().unwrap();
1581 let algebra = algebra
1582 .as_mut()
1583 .downcast_mut::<Binarizing<TagTreeAlgebra>>()
1584 .ok_or_else(|| IrtgError::WrongInputType {
1585 interpretation: self.name.clone(),
1586 })?;
1587 let value = algebra
1588 .parse_object(input)
1589 .map_err(|err| IrtgError::ObjectParse {
1590 interpretation: self.name.clone(),
1591 message: err.to_string(),
1592 })?;
1593 Ok(Box::new(value))
1594 }
1595 InterpretationKind::OutputOnly => Err(IrtgError::ObjectParse {
1596 interpretation: self.name.clone(),
1597 message: "interpretation is output-only and cannot be parsed as a parse input"
1598 .to_owned(),
1599 }),
1600 }
1601 }
1602
1603 pub fn input_erased(&self, value: Box<dyn Any + Send>) -> ParseInput<'_> {
1606 ParseInput {
1607 interpretation: self,
1608 value,
1609 }
1610 }
1611
1612 pub fn interpret_to_string(
1615 &self,
1616 arena: &TreeArena<Symbol>,
1617 root: Tree,
1618 ) -> Result<String, IrtgError> {
1619 let algebra = self.algebra.lock().unwrap();
1620 self.renderer
1621 .render(&**algebra, &self.homomorphism, arena, root, &self.name)
1622 }
1623
1624 pub fn evaluate_derivation(
1626 &self,
1627 arena: &TreeArena<Symbol>,
1628 root: Tree,
1629 ) -> Result<EvaluatedAlgebraValue, IrtgError> {
1630 let algebra = self.algebra.lock().unwrap();
1631 self.renderer
1632 .evaluate(&**algebra, &self.homomorphism, arena, root, &self.name)
1633 }
1634}
1635
1636#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1637enum InterpretationKind {
1638 String,
1640 TagString,
1642 TagTree,
1644 BinarizedTagTree,
1646 OutputOnly,
1649}
1650
1651pub struct TypedInterpretation<'i, A> {
1653 interpretation: &'i Interpretation,
1654 _algebra: PhantomData<A>,
1655}
1656
1657impl<'i, A> TypedInterpretation<'i, A>
1658where
1659 A: Algebra + 'static,
1660 A::InternalValue: Send + 'static,
1661 A::ParseError: fmt::Display,
1662{
1663 pub fn name(&self) -> &str {
1665 self.interpretation.name()
1666 }
1667
1668 pub fn algebra_signature(&self) -> &Signature {
1670 self.interpretation.algebra_signature()
1671 }
1672
1673 pub fn homomorphism(&self) -> &Homomorphism {
1675 self.interpretation.homomorphism()
1676 }
1677
1678 pub fn parse_object(&self, input: &str) -> Result<A::InternalValue, IrtgError> {
1680 let mut algebra = self.interpretation.algebra.lock().unwrap();
1681 let algebra =
1682 algebra
1683 .as_mut()
1684 .downcast_mut::<A>()
1685 .ok_or_else(|| IrtgError::WrongInputType {
1686 interpretation: self.interpretation.name.clone(),
1687 })?;
1688 algebra
1689 .parse_object(input)
1690 .map_err(|err| IrtgError::ObjectParse {
1691 interpretation: self.interpretation.name.clone(),
1692 message: err.to_string(),
1693 })
1694 }
1695
1696 pub fn input(&self, value: A::InternalValue) -> ParseInput<'i> {
1698 ParseInput {
1699 interpretation: self.interpretation,
1700 value: Box::new(value),
1701 }
1702 }
1703
1704 pub fn interpret_derivation(
1706 &self,
1707 arena: &TreeArena<Symbol>,
1708 root: Tree,
1709 ) -> Result<A::Value, IrtgError> {
1710 let algebra = self.interpretation.algebra.lock().unwrap();
1711 let algebra =
1712 algebra
1713 .as_ref()
1714 .downcast_ref::<A>()
1715 .ok_or_else(|| IrtgError::WrongInputType {
1716 interpretation: self.interpretation.name.clone(),
1717 })?;
1718 let mut image = TreeArena::new();
1719 let image_root = self
1720 .interpretation
1721 .homomorphism
1722 .apply(arena, root, &mut image)?;
1723 algebra
1724 .evaluate_term(&image, image_root)
1725 .ok_or_else(|| IrtgError::ObjectParse {
1726 interpretation: self.interpretation.name.clone(),
1727 message: "derivation tree did not evaluate in the algebra".to_owned(),
1728 })
1729 }
1730}
1731
1732pub struct ParseInput<'i> {
1734 interpretation: &'i Interpretation,
1735 value: Box<dyn Any + Send>,
1736}
1737
1738#[derive(Debug)]
1740pub struct ParseChart {
1741 pub automaton: Explicit,
1743 pub state_names: Vec<String>,
1745 pub state_parts: Vec<Vec<String>>,
1759 pub stats: Vec<IndexedCondensedIntersectionStats>,
1761}
1762
1763#[derive(Debug, Error)]
1765pub enum IrtgError {
1766 #[error("parsing cancelled")]
1772 Cancelled,
1773 #[error("input is not valid UTF-8: {0}")]
1775 Utf8(#[from] std::string::FromUtf8Error),
1776 #[error("failed to read IRTG: {0}")]
1778 Io(#[from] std::io::Error),
1779 #[error("{0}")]
1781 Syntax(String),
1782 #[error("{0}")]
1784 Signature(#[from] SignatureError),
1785 #[error("{0}")]
1787 Homomorphism(#[from] HomomorphismError),
1788 #[error("{0}")]
1790 Automaton(#[from] ExplicitBuildError),
1791 #[error("unknown interpretation {0:?}")]
1793 UnknownInterpretation(String),
1794 #[error("interpretation {interpretation:?} has algebra {actual}, not {requested}")]
1796 WrongAlgebraType {
1797 interpretation: String,
1799 requested: &'static str,
1801 actual: String,
1803 },
1804 #[error("wrong input value type for interpretation {interpretation:?}")]
1806 WrongInputType {
1807 interpretation: String,
1809 },
1810 #[error("interpretation {interpretation:?} is output-only and cannot be a parse input")]
1812 NotInputable {
1813 interpretation: String,
1815 },
1816 #[error(
1818 "interpretation {interpretation:?} with algebra {class_name} does not support non-null filtering"
1819 )]
1820 NonNullFilterUnsupported {
1821 interpretation: String,
1823 class_name: String,
1825 },
1826 #[error("heuristic is incompatible with interpretation {interpretation:?}: {message}")]
1828 IncompatibleHeuristic {
1829 interpretation: String,
1831 message: String,
1833 },
1834 #[error("failed to parse object for interpretation {interpretation:?}: {message}")]
1836 ObjectParse {
1837 interpretation: String,
1839 message: String,
1841 },
1842 #[error("unsupported algebra {class_name} for interpretation {interpretation:?}")]
1844 UnsupportedAlgebra {
1845 interpretation: String,
1847 class_name: String,
1849 },
1850 #[error(
1852 "A* requires all grammar rule weights ≤ 1, but found weight {weight} for rule {symbol:?}"
1853 )]
1854 AstarWeightPrecondition {
1855 weight: f64,
1857 symbol: String,
1859 },
1860}
1861
1862pub fn parse_irtg<R: Read>(mut reader: R) -> Result<Irtg, IrtgError> {
1864 let mut bytes = Vec::new();
1865 reader.read_to_end(&mut bytes)?;
1866 let input = String::from_utf8(bytes)?;
1867 let tokens = lex(&input).map_err(irtg_lex_error)?;
1868 let ast = alto_grammar::IrtgParser::new()
1869 .parse(tokens.into_iter().map(Ok))
1870 .map_err(irtg_parse_error)?;
1871 build_irtg(ast)
1872}
1873
1874pub(crate) fn build_irtg(ast: AstIrtg) -> Result<Irtg, IrtgError> {
1875 let mut builder = ExplicitBuilder::new();
1876 let mut states = Interner::new();
1877 let mut state_ids = FxHashMap::default();
1878 let mut grammar_signature = Signature::new();
1879 let mut homs = FxHashMap::default();
1880 let mut algebra_signatures = FxHashMap::default();
1881 let interpretation_algebras = ast
1882 .interpretations
1883 .iter()
1884 .map(|decl| (decl.name.clone(), decl.algebra.clone()))
1885 .collect::<FxHashMap<_, _>>();
1886
1887 for decl in &ast.interpretations {
1888 homs.insert(decl.name.clone(), Homomorphism::new());
1889 algebra_signatures.insert(decl.name.clone(), Signature::new());
1890 }
1891
1892 for rule in ast.rules {
1893 let parent = state_id(&mut builder, &mut states, &mut state_ids, &rule.auto.parent);
1894 if rule.auto.parent.is_final {
1895 builder.add_accepting(parent);
1896 }
1897 let child_ids: Vec<_> = rule
1898 .auto
1899 .children
1900 .iter()
1901 .map(|child| {
1902 let id = state_id(&mut builder, &mut states, &mut state_ids, child);
1903 if child.is_final {
1904 builder.add_accepting(id);
1905 }
1906 id
1907 })
1908 .collect();
1909 let symbol = grammar_signature.intern(rule.auto.symbol.clone(), child_ids.len())?;
1910 builder.add_weighted_rule(symbol, child_ids, parent, rule.auto.weight.unwrap_or(1.0));
1911
1912 for hom_rule in rule.homs {
1913 let Some(hom) = homs.get_mut(&hom_rule.interpretation) else {
1914 return Err(IrtgError::UnknownInterpretation(hom_rule.interpretation));
1915 };
1916 let signature = algebra_signatures
1917 .get_mut(&hom_rule.interpretation)
1918 .expect("hom and signature maps are initialized together");
1919 let normalized;
1920 let term = if interpretation_algebras
1921 .get(&hom_rule.interpretation)
1922 .map(String::as_str)
1923 == Some(FEATURE_STRUCTURE_ALGEBRA)
1924 {
1925 normalized = normalize_legacy_feature_term(&hom_rule.term);
1926 &normalized
1927 } else {
1928 &hom_rule.term
1929 };
1930 let term = lower_hom_term(term, hom, signature)?;
1931 hom.add(symbol, rule.auto.children.len(), term)?;
1932 }
1933 }
1934
1935 let mut interpretations = FxHashMap::default();
1936 for decl in ast.interpretations {
1937 let (kind, algebra, algebra_signature, renderer): (
1938 InterpretationKind,
1939 Box<dyn Any + Send>,
1940 Signature,
1941 Box<dyn DerivationRenderer>,
1942 ) = if decl.algebra == STRING_ALGEBRA {
1943 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
1944 let algebra = StringAlgebra::with_signature(signature.clone());
1945 let renderer = Box::new(TypedDerivationCodec::<StringAlgebra, SpaceJoinCodec>::new(
1946 SpaceJoinCodec,
1947 ));
1948 (
1949 InterpretationKind::String,
1950 Box::new(algebra),
1951 signature,
1952 renderer,
1953 )
1954 } else if decl.algebra == TAG_STRING_ALGEBRA {
1955 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
1956 let algebra = TagStringAlgebra::with_signature(signature.clone());
1957 let renderer = Box::new(TypedDerivationCodec::<TagStringAlgebra, DisplayCodec>::new(
1958 DisplayCodec,
1959 ));
1960 (
1961 InterpretationKind::TagString,
1962 Box::new(algebra),
1963 signature,
1964 renderer,
1965 )
1966 } else if decl.algebra == TAG_TREE_ALGEBRA || decl.algebra == TAG_TREE_WITH_ARITIES_ALGEBRA
1967 {
1968 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
1969 let algebra = if decl.algebra == TAG_TREE_WITH_ARITIES_ALGEBRA {
1970 TagTreeAlgebra::with_arities(signature.clone())
1971 } else {
1972 TagTreeAlgebra::tree(signature.clone())
1973 };
1974 let renderer = Box::new(TypedDerivationCodec::<TagTreeAlgebra, DisplayCodec>::new(
1975 DisplayCodec,
1976 ));
1977 (
1978 InterpretationKind::TagTree,
1979 Box::new(algebra),
1980 signature,
1981 renderer,
1982 )
1983 } else if decl.algebra == BINARIZING_TAG_TREE_ALGEBRA
1984 || decl.algebra == BINARIZING_TAG_TREE_WITH_ARITIES_ALGEBRA
1985 {
1986 let mut signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
1987 let append = Some(signature.intern(APPEND_SYMBOL.to_owned(), 2)?);
1988 let inner = if decl.algebra == BINARIZING_TAG_TREE_WITH_ARITIES_ALGEBRA {
1989 TagTreeAlgebra::with_arities(signature.clone())
1990 } else {
1991 TagTreeAlgebra::tree(signature.clone())
1992 };
1993 let algebra = Binarizing::new(inner, append);
1994 let renderer = Box::new(TypedDerivationCodec::<
1995 Binarizing<TagTreeAlgebra>,
1996 DisplayCodec,
1997 >::new(DisplayCodec));
1998 (
1999 InterpretationKind::BinarizedTagTree,
2000 Box::new(algebra),
2001 signature,
2002 renderer,
2003 )
2004 } else if decl.algebra == FEATURE_STRUCTURE_ALGEBRA {
2005 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
2006 let algebra = FeatureStructureAlgebra::with_signature(signature.clone());
2007 let renderer = Box::new(
2008 TypedDerivationCodec::<FeatureStructureAlgebra, DisplayCodec>::new(DisplayCodec),
2009 );
2010 (
2011 InterpretationKind::OutputOnly,
2012 Box::new(algebra),
2013 signature,
2014 renderer,
2015 )
2016 } else if decl.algebra == TREE_WITH_ARITIES_ALGEBRA {
2017 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
2018 let algebra = TreeAlgebra::with_arities(signature.clone());
2019 let renderer = Box::new(TypedDerivationCodec::<TreeAlgebra, DisplayCodec>::new(
2020 DisplayCodec,
2021 ));
2022 (
2023 InterpretationKind::OutputOnly,
2024 Box::new(algebra),
2025 signature,
2026 renderer,
2027 )
2028 } else if decl.algebra == BINARIZING_TREE_WITH_ARITIES_ALGEBRA {
2029 let signature = algebra_signatures.remove(&decl.name).unwrap_or_default();
2030 let append = signature.get(APPEND_SYMBOL);
2031 let algebra = Binarizing::new(TreeAlgebra::with_arities(signature.clone()), append);
2032 let renderer = Box::new(
2033 TypedDerivationCodec::<Binarizing<TreeAlgebra>, DisplayCodec>::new(DisplayCodec),
2034 );
2035 (
2036 InterpretationKind::OutputOnly,
2037 Box::new(algebra),
2038 signature,
2039 renderer,
2040 )
2041 } else {
2042 return Err(IrtgError::UnsupportedAlgebra {
2043 interpretation: decl.name.clone(),
2044 class_name: decl.algebra.clone(),
2045 });
2046 };
2047 let homomorphism = homs.remove(&decl.name).unwrap_or_else(Homomorphism::new);
2048 interpretations.insert(
2049 decl.name.clone(),
2050 Interpretation {
2051 name: decl.name,
2052 class_name: decl.algebra,
2053 kind,
2054 algebra: Mutex::new(algebra),
2055 algebra_signature,
2056 homomorphism,
2057 renderer,
2058 },
2059 );
2060 }
2061
2062 Ok(Irtg {
2063 grammar: builder.try_build()?,
2064 states,
2065 grammar_signature,
2066 interpretations,
2067 })
2068}
2069
2070fn state_id(
2071 builder: &mut ExplicitBuilder,
2072 states: &mut Interner<String>,
2073 state_ids: &mut FxHashMap<String, StateId>,
2074 state: &AstState,
2075) -> StateId {
2076 if let Some(&id) = state_ids.get(&state.name) {
2077 return id;
2078 }
2079 let id = builder.new_state();
2080 let interned = states.intern(state.name.clone());
2081 debug_assert_eq!(id, interned);
2082 state_ids.insert(state.name.clone(), id);
2083 id
2084}
2085
2086fn normalize_legacy_feature_term(term: &AstHomTerm) -> AstHomTerm {
2087 match term {
2088 AstHomTerm::Variable(variable) => AstHomTerm::Variable(*variable),
2089 AstHomTerm::Symbol(label, children) => {
2090 let children = children.iter().map(normalize_legacy_feature_term).collect();
2091 if let Some(specification) = label.strip_prefix("emba_") {
2092 let mut attributes = specification.split('_');
2093 if let (Some(top), Some(bottom)) = (attributes.next(), attributes.next()) {
2094 return AstHomTerm::Symbol(format!("remap_root={top},foot={bottom}"), children);
2095 }
2096 }
2097 AstHomTerm::Symbol(label.clone(), children)
2098 }
2099 }
2100}
2101
2102fn lower_hom_term(
2103 term: &AstHomTerm,
2104 hom: &mut Homomorphism,
2105 signature: &mut Signature,
2106) -> Result<Tree, IrtgError> {
2107 match term {
2108 AstHomTerm::Variable(variable) => {
2109 if *variable == 0 {
2110 return Err(IrtgError::Syntax(
2111 "Alto homomorphism variables are one-based; ?0 is invalid".to_owned(),
2112 ));
2113 }
2114 Ok(hom.add_var(variable - 1))
2115 }
2116 AstHomTerm::Symbol(name, children) => {
2117 let children = children
2118 .iter()
2119 .map(|child| lower_hom_term(child, hom, signature))
2120 .collect::<Result<Vec<_>, _>>()?;
2121 let symbol = signature.intern(name.clone(), children.len())?;
2122 Ok(hom.add_symbol(symbol, children))
2123 }
2124 }
2125}
2126
2127fn irtg_lex_error(err: LexError) -> IrtgError {
2128 IrtgError::Syntax(err.to_string())
2129}
2130
2131fn irtg_parse_error(err: ParseError<usize, Tok, String>) -> IrtgError {
2132 IrtgError::Syntax(match err {
2133 ParseError::InvalidToken { location } => format!("invalid token at byte {location}"),
2134 ParseError::UnrecognizedEof { location, expected } => {
2135 format!(
2136 "unexpected EOF at byte {location}; expected {}",
2137 expected.join(", ")
2138 )
2139 }
2140 ParseError::UnrecognizedToken { token, expected } => format!(
2141 "unexpected token {:?} at byte {}; expected {}",
2142 token.1,
2143 token.0,
2144 expected.join(", ")
2145 ),
2146 ParseError::ExtraToken { token } => {
2147 format!("unexpected extra token {:?} at byte {}", token.1, token.0)
2148 }
2149 ParseError::User { error } => error,
2150 })
2151}
2152
2153#[cfg(test)]
2154mod tests {
2155 use super::*;
2156
2157 #[test]
2158 fn parses_tiny_string_irtg_and_accepts_compatible_input() {
2159 let irtg = parse_irtg(
2160 br#"
2161 interpretation english: de.up.ling.irtg.algebra.StringAlgebra
2162
2163 S! -> r(NP,VP) [1.0]
2164 [english] *(?1,?2)
2165
2166 NP -> john_rule
2167 [english] john
2168
2169 VP -> watches_rule
2170 [english] watches
2171 "# as &[u8],
2172 )
2173 .unwrap();
2174
2175 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2176 let value = english.parse_object("john watches").unwrap();
2177 let chart = irtg.parse([english.input(value)]).unwrap();
2178 assert!(!chart.automaton.is_empty());
2179
2180 let bad = english.parse_object("john sleeps").unwrap();
2181 let chart = irtg.parse([english.input(bad)]).unwrap();
2182 assert!(chart.automaton.is_empty());
2183 }
2184
2185 #[test]
2186 fn typed_interpretation_returns_tree_value_without_rendering() {
2187 let irtg = parse_irtg(
2188 br#"
2189 interpretation string: de.up.ling.irtg.algebra.StringAlgebra
2190 interpretation tree: de.up.ling.irtg.algebra.TreeWithAritiesAlgebra
2191
2192 S! -> r(A,B)
2193 [string] *(?1,?2)
2194 [tree] S_2(?1,?2)
2195
2196 A -> a
2197 [string] a
2198 [tree] NN_1(a_0)
2199
2200 B -> b
2201 [string] b
2202 [tree] VB_1(b_0)
2203 "# as &[u8],
2204 )
2205 .unwrap();
2206
2207 let string = irtg.interpretation::<StringAlgebra>("string").unwrap();
2208 let input = string.parse_object("a b").unwrap();
2209 let best = irtg
2210 .best_with(
2211 [string.input(input)],
2212 &MaterializationStrategy::TopDownCondensed,
2213 )
2214 .unwrap()
2215 .unwrap();
2216 let tree = irtg.interpretation::<TreeAlgebra>("tree").unwrap();
2217 let value = tree
2218 .interpret_derivation(best.arena(), best.root())
2219 .unwrap();
2220 assert_eq!(value.to_string(), "S(NN(a), VB(b))");
2221 assert_eq!(value.arena().get_label(value.root()), "S");
2222 }
2223
2224 #[test]
2225 fn parses_multi_interpretation_irtg_and_enforces_both_inputs() {
2226 let irtg = parse_irtg(
2227 br#"
2228 interpretation english: de.up.ling.irtg.algebra.StringAlgebra
2229 interpretation german: de.up.ling.irtg.algebra.StringAlgebra
2230
2231 S! -> r(A,B)
2232 [english] *(?1,?2)
2233 [german] *(?1,?2)
2234
2235 A -> a
2236 [english] john
2237 [german] hans
2238
2239 B -> b
2240 [english] watches
2241 [german] sieht
2242 "# as &[u8],
2243 )
2244 .unwrap();
2245
2246 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2247 let german = irtg.interpretation::<StringAlgebra>("german").unwrap();
2248 let english_value = english.parse_object("john watches").unwrap();
2249 let german_value = german.parse_object("hans sieht").unwrap();
2250 let chart = irtg
2251 .parse([english.input(english_value), german.input(german_value)])
2252 .unwrap();
2253 assert!(!chart.automaton.is_empty());
2254
2255 let english_value = english.parse_object("john watches").unwrap();
2256 let german_value = german.parse_object("hans schaut").unwrap();
2257 let chart = irtg
2258 .parse([english.input(english_value), german.input(german_value)])
2259 .unwrap();
2260 assert!(chart.automaton.is_empty());
2261 }
2262
2263 #[test]
2264 fn reads_actual_alto_format_cfg_fixture() {
2265 let irtg = parse_irtg(include_bytes!("../benchdata/irtg/cfg.irtg").as_slice()).unwrap();
2266 let interpretation = irtg.interpretation::<StringAlgebra>("i").unwrap();
2267 let value = interpretation
2268 .parse_object("john watches the woman")
2269 .unwrap();
2270 let chart = irtg.parse([interpretation.input(value)]).unwrap();
2271 assert!(!chart.automaton.is_empty());
2272 assert_eq!(irtg.grammar().rules().count(), 12);
2273 }
2274
2275 #[test]
2276 fn parses_features_comments_quoted_names_and_scientific_weights() {
2277 let irtg = parse_irtg(
2278 br#"
2279 interpretation 'surface': de.up.ling.irtg.algebra.StringAlgebra
2280 feature constructor: SomeFeature(A, B)
2281 /* block comment */
2282 'S root'! -> 'r root'('A one') [3.3921302578018993E-4]
2283 [surface] wrap(?1) // line comment
2284
2285 'A one' -> leaf()
2286 [surface] 'hello world'
2287 "# as &[u8],
2288 )
2289 .unwrap();
2290
2291 assert_eq!(irtg.grammar().rules().count(), 2);
2292 let parent = irtg.states().get(&"S root".to_owned()).unwrap();
2293 let symbol = irtg.grammar_signature().get("r root").unwrap();
2294 let rule = irtg
2295 .grammar()
2296 .rules()
2297 .find(|rule| rule.symbol == symbol)
2298 .unwrap();
2299 assert_eq!(rule.result, parent);
2300 assert!((rule.weight - 3.3921302578018993E-4).abs() < 1e-12);
2301 let surface = irtg.interpretation::<StringAlgebra>("surface").unwrap();
2302 assert!(surface.algebra_signature().get("wrap").is_some());
2303 assert!(surface.algebra_signature().get("hello world").is_some());
2304 }
2305
2306 #[test]
2307 fn rejects_unknown_hom_interpretation() {
2308 let err = parse_irtg(
2309 br#"
2310 interpretation i: de.up.ling.irtg.algebra.StringAlgebra
2311 S! -> r
2312 [missing] x
2313 "# as &[u8],
2314 )
2315 .unwrap_err();
2316 assert!(matches!(err, IrtgError::UnknownInterpretation(name) if name == "missing"));
2317 }
2318
2319 #[test]
2320 fn parse_irtg_rejects_invalid_utf8_reader() {
2321 let err = parse_irtg(&b"\xff"[..]).unwrap_err();
2322 assert!(matches!(err, IrtgError::Utf8(_)));
2323 }
2324
2325 #[test]
2326 fn rejects_zero_variable() {
2327 let err = parse_irtg(
2328 br#"
2329 interpretation i: de.up.ling.irtg.algebra.StringAlgebra
2330 S! -> r(A)
2331 [i] ?0
2332 "# as &[u8],
2333 )
2334 .unwrap_err();
2335 assert!(matches!(err, IrtgError::Syntax(message) if message.contains("?0")));
2336 }
2337
2338 #[test]
2339 fn rejects_duplicate_grammar_transitions() {
2340 let err = parse_irtg(
2341 br#"
2342 interpretation i: de.up.ling.irtg.algebra.StringAlgebra
2343 S! -> r [1.0]
2344 S! -> r [2.0]
2345 "# as &[u8],
2346 )
2347 .unwrap_err();
2348 assert!(matches!(err, IrtgError::Automaton(_)));
2349 }
2350
2351 fn tiny_irtg_bytes() -> &'static [u8] {
2356 br#"
2357 interpretation english: de.up.ling.irtg.algebra.StringAlgebra
2358
2359 S! -> r(NP,VP) [1.0]
2360 [english] *(?1,?2)
2361
2362 NP -> john_rule [1.0]
2363 [english] john
2364
2365 VP -> watches_rule [1.0]
2366 [english] watches
2367 "#
2368 }
2369
2370 #[test]
2371 fn parse_with_top_down_condensed_matches_parse() {
2372 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2373 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2374
2375 let value = english.parse_object("john watches").unwrap();
2376 let chart_default = irtg.parse([english.input(value.clone())]).unwrap();
2377
2378 let chart_explicit = irtg
2379 .parse_with(
2380 [english.input(value)],
2381 &MaterializationStrategy::TopDownCondensed,
2382 )
2383 .unwrap();
2384
2385 assert_eq!(
2387 chart_default.automaton.is_empty(),
2388 chart_explicit.automaton.is_empty()
2389 );
2390 assert!(!chart_explicit.automaton.is_empty());
2391 }
2392
2393 #[test]
2394 fn parse_with_indexed_condensed_works() {
2395 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2396 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2397
2398 let value = english.parse_object("john watches").unwrap();
2399 let chart = irtg
2400 .parse_with(
2401 [english.input(value)],
2402 &MaterializationStrategy::IndexedCondensed,
2403 )
2404 .unwrap();
2405
2406 assert!(!chart.automaton.is_empty());
2407
2408 let bad = english.parse_object("john sleeps").unwrap();
2410 let chart = irtg
2411 .parse_with(
2412 [english.input(bad)],
2413 &MaterializationStrategy::IndexedCondensed,
2414 )
2415 .unwrap();
2416 assert!(chart.automaton.is_empty());
2417 }
2418
2419 #[test]
2420 fn parse_with_astar_zero_returns_non_empty_chart_for_known_sentence() {
2421 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2422 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2423
2424 let value = english.parse_object("john watches").unwrap();
2425 let chart = irtg
2426 .parse_with(
2427 [english.input(value)],
2428 &MaterializationStrategy::Astar {
2429 heuristic: AstarHeuristic::Zero,
2430 options: AstarOptions {
2431 stop_at_first_goal: true,
2432 beam: None,
2433 },
2434 },
2435 )
2436 .unwrap();
2437
2438 assert!(!chart.automaton.is_empty());
2439 }
2440
2441 #[test]
2442 fn string_chart_state_names_are_consistent_across_strategies() {
2443 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2444 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2445 let strategies = [
2446 MaterializationStrategy::TopDownCondensed,
2447 MaterializationStrategy::IndexedCondensed,
2448 MaterializationStrategy::Astar {
2449 heuristic: AstarHeuristic::Zero,
2450 options: AstarOptions {
2451 stop_at_first_goal: false,
2452 beam: None,
2453 },
2454 },
2455 ];
2456
2457 for strategy in strategies {
2458 let value = english.parse_object("john watches").unwrap();
2459 let chart = irtg.parse_with([english.input(value)], &strategy).unwrap();
2460 assert!(chart.state_names.iter().any(|name| name == "NP[0-1]"));
2461 assert!(chart.state_names.iter().any(|name| name == "VP[1-2]"));
2462 assert!(chart.state_names.iter().any(|name| name == "S[0-2]"));
2463 assert!(chart.state_names.iter().all(|name| !name.starts_with('q')));
2464 }
2465 }
2466
2467 #[test]
2468 fn astar_strategy_rejects_grammar_with_weight_greater_than_one() {
2469 let irtg = parse_irtg(
2470 br#"
2471 interpretation i: de.up.ling.irtg.algebra.StringAlgebra
2472
2473 S! -> r [5.0]
2474 [i] hello
2475
2476 "# as &[u8],
2477 )
2478 .unwrap();
2479
2480 let interp = irtg.interpretation::<StringAlgebra>("i").unwrap();
2481 let value = interp.parse_object("hello").unwrap();
2482
2483 let err = irtg
2484 .parse_with(
2485 [interp.input(value)],
2486 &MaterializationStrategy::Astar {
2487 heuristic: AstarHeuristic::Zero,
2488 options: AstarOptions {
2489 stop_at_first_goal: true,
2490 beam: None,
2491 },
2492 },
2493 )
2494 .unwrap_err();
2495
2496 assert!(
2497 matches!(err, IrtgError::AstarWeightPrecondition { weight, .. } if weight > 1.0),
2498 "expected AstarWeightPrecondition, got {:?}",
2499 err
2500 );
2501 }
2502
2503 #[test]
2504 fn astar_weight_precondition_error_contains_weight_and_symbol() {
2505 let irtg = parse_irtg(
2506 br#"
2507 interpretation i: de.up.ling.irtg.algebra.StringAlgebra
2508
2509 S! -> my_rule [3.5]
2510 [i] hello
2511 "# as &[u8],
2512 )
2513 .unwrap();
2514
2515 let interp = irtg.interpretation::<StringAlgebra>("i").unwrap();
2516 let value = interp.parse_object("hello").unwrap();
2517
2518 let err = irtg
2519 .parse_with(
2520 [interp.input(value)],
2521 &MaterializationStrategy::Astar {
2522 heuristic: AstarHeuristic::Zero,
2523 options: AstarOptions {
2524 stop_at_first_goal: true,
2525 beam: None,
2526 },
2527 },
2528 )
2529 .unwrap_err();
2530
2531 if let IrtgError::AstarWeightPrecondition { weight, symbol } = &err {
2532 assert!((weight - 3.5).abs() < 1e-10);
2533 assert!(symbol.contains("my_rule"), "symbol = {symbol}");
2534 } else {
2535 panic!("expected AstarWeightPrecondition, got {:?}", err);
2536 }
2537
2538 let msg = err.to_string();
2540 assert!(msg.contains("3.5") || msg.contains("A*"), "message = {msg}");
2541 }
2542
2543 #[test]
2544 fn best_with_astar_returns_some_for_known_sentence() {
2545 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2546 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2547
2548 let value = english.parse_object("john watches").unwrap();
2549 let result = irtg
2550 .best_with(
2551 [english.input(value)],
2552 &MaterializationStrategy::Astar {
2553 heuristic: AstarHeuristic::Zero,
2554 options: AstarOptions {
2555 stop_at_first_goal: true,
2556 beam: None,
2557 },
2558 },
2559 )
2560 .unwrap();
2561
2562 assert!(result.is_some(), "expected a best tree for 'john watches'");
2563 }
2564
2565 #[test]
2566 fn best_with_chart_strategy_returns_some_for_known_sentence() {
2567 let irtg = parse_irtg(tiny_irtg_bytes()).unwrap();
2568 let english = irtg.interpretation::<StringAlgebra>("english").unwrap();
2569
2570 let value = english.parse_object("john watches").unwrap();
2571 let result = irtg
2572 .best_with(
2573 [english.input(value)],
2574 &MaterializationStrategy::TopDownCondensed,
2575 )
2576 .unwrap();
2577
2578 assert!(result.is_some(), "expected a best tree for 'john watches'");
2579 }
2580
2581 fn tiny_tag_irtg() -> &'static [u8] {
2582 br#"
2583 interpretation string: de.up.ling.irtg.algebra.TagStringAlgebra
2584 interpretation tree: de.up.ling.irtg.algebra.TagTreeAlgebra
2585
2586 S! -> r
2587 [string] *WRAP21*(*CONC12*(a, *EE*), b)
2588 [tree] @(*, f(a))
2589 "#
2590 }
2591
2592 #[test]
2593 fn tag_string_and_tree_are_both_parse_inputs() {
2594 let irtg = parse_irtg(tiny_tag_irtg()).unwrap();
2595 let string = irtg.interpretation::<TagStringAlgebra>("string").unwrap();
2596 let tree = irtg.interpretation::<TagTreeAlgebra>("tree").unwrap();
2597 let string_value = string.parse_object("a b").unwrap();
2598 let tree_value = tree.parse_object("f(a)").unwrap();
2599
2600 let chart = irtg
2601 .parse([string.input(string_value), tree.input(tree_value)])
2602 .unwrap();
2603 assert!(chart.automaton.viterbi().is_some());
2604 }
2605
2606 #[test]
2607 fn tag_inputs_support_generic_zero_astar() {
2608 let irtg = parse_irtg(tiny_tag_irtg()).unwrap();
2609 let string = irtg.interpretation::<TagStringAlgebra>("string").unwrap();
2610 let value = string.parse_object("a b").unwrap();
2611 let result = irtg
2612 .best_with(
2613 [string.input(value)],
2614 &MaterializationStrategy::Astar {
2615 heuristic: AstarHeuristic::Zero,
2616 options: AstarOptions {
2617 stop_at_first_goal: true,
2618 beam: None,
2619 },
2620 },
2621 )
2622 .unwrap();
2623 assert!(result.is_some());
2624 }
2625
2626 #[test]
2627 fn generic_algebra_state_names_are_consistent_across_strategies() {
2628 let irtg = parse_irtg(tiny_tag_irtg()).unwrap();
2629
2630 for interpretation in ["string", "tree"] {
2631 let mut expected_names = None;
2632 let strategies = [
2633 MaterializationStrategy::TopDownCondensed,
2634 MaterializationStrategy::IndexedCondensed,
2635 MaterializationStrategy::Astar {
2636 heuristic: AstarHeuristic::Zero,
2637 options: AstarOptions {
2638 stop_at_first_goal: false,
2639 beam: None,
2640 },
2641 },
2642 ];
2643
2644 for strategy in strategies {
2645 let input = irtg
2646 .interpretation_ref(interpretation)
2647 .unwrap()
2648 .parse_object_erased(if interpretation == "string" {
2649 "a b"
2650 } else {
2651 "f(a)"
2652 })
2653 .unwrap();
2654 let chart = irtg
2655 .parse_with(
2656 [irtg
2657 .interpretation_ref(interpretation)
2658 .unwrap()
2659 .input_erased(input)],
2660 &strategy,
2661 )
2662 .unwrap();
2663 let mut names = chart.state_names;
2664 names.sort();
2665 assert!(names.iter().all(|name| !name.starts_with('q')));
2666 assert!(names.iter().all(|name| name.contains(" × ")));
2667 assert!(chart.state_parts.iter().all(|parts| parts.len() == 2));
2668 if interpretation == "string" {
2669 assert!(
2670 chart
2671 .state_parts
2672 .iter()
2673 .any(|parts| { parts[1] == "[0-2]" || parts[1] == "[0-1, 1-2]" })
2674 );
2675 }
2676 if let Some(expected) = &expected_names {
2677 assert_eq!(&names, expected);
2678 } else {
2679 expected_names = Some(names);
2680 }
2681 }
2682 }
2683 }
2684
2685 #[test]
2686 fn binarized_tag_tree_is_inputable() {
2687 let irtg = parse_irtg(
2688 br#"
2689 interpretation tree: de.up.ling.irtg.algebra.BinarizingTagTreeAlgebra
2690
2691 S! -> r
2692 [tree] f(_@_(a, _@_(b, c)))
2693 "# as &[u8],
2694 )
2695 .unwrap();
2696 let tree = irtg
2697 .interpretation::<Binarizing<TagTreeAlgebra>>("tree")
2698 .unwrap();
2699 let value = tree.parse_object("f(a, b, c)").unwrap();
2700 let chart = irtg.parse([tree.input(value)]).unwrap();
2701 assert!(chart.automaton.viterbi().is_some());
2702 }
2703
2704 #[test]
2705 fn legacy_alto_aux_embedding_is_normalized_outside_the_algebra() {
2706 let irtg = parse_irtg(
2707 br#"
2708 interpretation ft: de.up.ling.irtg.algebra.FeatureStructureAlgebra
2709
2710 S! -> combine(A)
2711 [ft] emba_top_bottom(?1)
2712
2713 A -> value
2714 [ft] "[root: #x [], foot: #x]"
2715 "# as &[u8],
2716 )
2717 .unwrap();
2718 let ft = irtg
2719 .interpretation::<FeatureStructureAlgebra>("ft")
2720 .unwrap();
2721 assert!(ft.algebra_signature().get("emba_top_bottom").is_none());
2722 assert!(
2723 ft.algebra_signature()
2724 .get("remap_root=top,foot=bottom")
2725 .is_some()
2726 );
2727 }
2728}