1use crate::parsing::rules::serialization::TreeWithMovement;
2use std::collections::BTreeMap;
3use std::fmt::{Debug, Display};
4
5#[cfg(not(feature = "semantics"))]
6use std::marker::PhantomData;
7
8#[cfg(feature = "semantics")]
9use crate::lexicon::SemanticLexicon;
10#[cfg(feature = "semantics")]
11use crate::parsing::rules::semantics::SemanticHistory;
12
13use ahash::HashMap;
14use itertools::Itertools;
15use petgraph::graph::NodeIndex;
16use serde::Serialize;
17
18use super::serialization::Tree;
19use super::{Rule, RuleIndex};
20use crate::lexicon::{Feature, LexemeId};
21use crate::parsing::rules::{StolenInfo, TraceId};
22use crate::parsing::trees::GornIndex;
23use crate::{Lexicon, RulePool};
24
25#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
29pub enum MgNode<T, C: Eq + Display> {
30 Start,
32 Node {
34 features: Vec<Feature<C>>,
36 },
37 Leaf {
39 lemma: Lemma<T>,
41 features: Vec<Feature<C>>,
43 },
44 Trace {
46 trace: TraceId,
48 },
49}
50
51#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
53pub enum Lemma<T> {
54 Single(Option<T>),
56 Multi {
58 heads: Vec<Option<T>>,
59 original_head: usize,
60 stolen: bool,
61 },
62}
63
64impl<T> Lemma<T> {
65 pub fn is_stolen(&self) -> bool {
67 if let Lemma::Multi { stolen, .. } = self {
68 *stolen
69 } else {
70 false
71 }
72 }
73}
74
75impl<T: Display> Display for Lemma<T> {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 Lemma::Single(Some(x)) => write!(f, "{x}"),
79 Lemma::Single(None) => write!(f, "ε"),
80 Lemma::Multi { heads, .. } => write!(
81 f,
82 "{}",
83 heads
84 .iter()
85 .map(|x| x
86 .as_ref().map_or_else(|| "ε".to_string(), std::string::ToString::to_string))
87 .collect::<Vec<_>>()
88 .join("-")
89 ),
90 }
91 }
92}
93
94#[derive(Debug, Clone, Copy, Eq, PartialEq)]
95enum DepthRuleOrder {
96 Todo(RuleIndex),
97 Done(RuleIndex),
98}
99
100impl RulePool {
101 #[must_use]
103 pub fn to_tree<'src, T: Eq + Clone, C: Eq + Clone + Display>(
104 self,
105 lex: &Lexicon<T, C>,
106 ) -> TreeWithMovement<'src, T, C> {
107 let d = lex.derivation(self);
108 d.tree()
109 }
110
111 fn get_node<T: Eq + Clone, C: Eq + Display + Clone>(
112 &self,
113 lex: &Lexicon<T, C>,
114 rule: RuleIndex,
115 lemma_lookup: &LemmaLookup,
116 ) -> MgNode<T, C> {
117 match self.get(rule) {
118 Rule::Start { .. } => MgNode::Start,
119 Rule::UnmoveTrace(trace_id) => MgNode::Trace { trace: *trace_id },
120 Rule::Scan { lexeme, .. } => {
121 let features = lex.leaf_to_features(*lexeme).unwrap().collect();
122 let lemma = lemma_lookup.get_lemma(lex, &rule);
123 MgNode::Leaf { lemma, features }
124 }
125 Rule::Unmerge { child, .. } | Rule::UnmergeFromMover { child, .. } => MgNode::Node {
126 features: lex.node_to_features(*child).collect(),
127 },
128 Rule::Unmove { child_id, .. } | Rule::UnmoveFromMover { child_id, .. } => {
129 let mut child = *child_id;
130 let mut offset = 1;
131
132 loop {
134 match self.get(child) {
135 Rule::Start { .. } | Rule::UnmoveTrace(..) | Rule::Scan { .. } => {
136 panic!("Can't move out of a leaf node or the start node.")
137 }
138 Rule::Unmerge { child, .. } | Rule::UnmergeFromMover { child, .. } => {
139 return MgNode::Node {
140 features: lex.node_to_features(*child).skip(offset).collect(),
141 };
142 }
143 Rule::Unmove { child_id, .. } | Rule::UnmoveFromMover { child_id, .. } => {
144 child = *child_id;
145 offset += 1;
146 }
147 }
148 }
149 }
150 }
151 }
152}
153
154impl Rule {
155 fn node(&self, rules: &RulePool) -> Option<NodeIndex> {
156 match self {
157 Rule::UnmoveTrace(..) => None,
158 Rule::Unmove { child_id, .. } | Rule::UnmoveFromMover { child_id, .. } => {
159 rules.get(*child_id).node(rules)
160 }
161 Rule::Scan { lexeme, .. } => Some(lexeme.0),
162 Rule::Start { node: child, .. }
163 | Rule::UnmergeFromMover { child, .. }
164 | Rule::Unmerge { child, .. } => Some(*child),
165 }
166 }
167}
168
169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170enum KeyValue {
171 Normal(LexemeId),
172 Stealer(RuleIndex),
173 Stolen(RuleIndex, GornIndex),
174}
175
176#[derive(Debug, Default, Clone)]
177struct LemmaLookup {
178 h: HashMap<RuleIndex, KeyValue>,
179 v: HashMap<RuleIndex, Vec<(GornIndex, LexemeId, RuleIndex)>>,
180}
181
182impl LemmaLookup {
183 fn add(&mut self, rule_index: RuleIndex, stolen: &StolenInfo, lexeme: LexemeId) {
184 match stolen {
185 StolenInfo::Normal => {
186 self.h.insert(rule_index, KeyValue::Normal(lexeme));
187 }
188 StolenInfo::Stolen(target_index, gorn_index) => {
189 self.h
190 .insert(rule_index, KeyValue::Stolen(*target_index, *gorn_index));
191 self.v
192 .entry(*target_index)
193 .or_default()
194 .push((*gorn_index, lexeme, rule_index));
195 }
196 StolenInfo::Stealer => {
197 self.h.insert(rule_index, KeyValue::Stealer(rule_index));
198 self.v.entry(rule_index).or_default().push((
199 GornIndex::default(),
200 lexeme,
201 rule_index,
202 ));
203 }
204 }
205 }
206
207 fn organize(&mut self) {
208 self.v
209 .values_mut()
210 .for_each(|x| x.sort_by(|(x, _, _), (y, _, _)| GornIndex::infix_order(x, y)));
211 }
212
213 fn get_lemma<T: Eq + Clone, C: Eq>(
214 &self,
215 lex: &Lexicon<T, C>,
216 rule_index: &RuleIndex,
217 ) -> Lemma<T> {
218 match self.h.get(rule_index).expect("Missing rule index") {
219 KeyValue::Normal(lexeme_id) => Lemma::Single(
220 lex.leaf_to_lemma(*lexeme_id)
221 .expect("Missing word in lexicon!")
222 .clone(),
223 ),
224 KeyValue::Stealer(target_rule) => {
225 let mut original_head = 0;
226 let heads = self
227 .v
228 .get(target_rule)
229 .expect("Bad lemma lookup")
230 .iter()
231 .enumerate()
232 .map(|(i, (_, x, r))| {
233 if r == rule_index {
234 original_head = i;
235 }
236 lex.leaf_to_lemma(*x).unwrap().clone()
237 })
238 .collect();
239
240 Lemma::Multi {
241 heads,
242 original_head,
243 stolen: false,
244 }
245 }
246 KeyValue::Stolen(target_rule, gorn_index) => {
247 let mut original_head = 0;
248 let heads = self
249 .v
250 .get(target_rule)
251 .expect("Bad lemma lookup")
252 .iter()
253 .filter(|(g, _, _)| g == gorn_index || gorn_index.is_ancestor_of(*g))
254 .enumerate()
255 .map(|(i, (_, x, r))| {
256 if r == rule_index {
257 original_head = i;
258 }
259 lex.leaf_to_lemma(*x).unwrap().clone()
260 })
261 .collect();
262
263 Lemma::Multi {
264 heads,
265 original_head,
266 stolen: true,
267 }
268 }
269 }
270 }
271
272 fn new(rules: &RulePool) -> LemmaLookup {
273 let mut affixes = LemmaLookup::default();
274
275 rules
276 .iter()
277 .enumerate()
278 .filter_map(|(x, y)| match y {
279 Rule::Scan { lexeme, stolen } => Some((RuleIndex(x), stolen, *lexeme)),
280 _ => None,
281 })
282 .for_each(|(rule_index, stolen, lexeme)| affixes.add(rule_index, stolen, lexeme));
283 affixes.organize();
284 affixes
285 }
286}
287
288#[derive(Debug, Copy, Clone, Eq, PartialEq)]
289struct TraceHistory {
290 source: RuleIndex,
291 destination: RuleIndex,
292}
293
294fn set_trace_destinations(trace_origins: &mut Vec<TraceHistory>, i: RuleIndex, rules: &RulePool) {
295 match rules.get(i) {
296 Rule::UnmergeFromMover {
297 stored_id,
298 destination_id,
299 ..
300 }
301 | Rule::UnmoveFromMover {
302 stored_id,
303 destination_id,
304 ..
305 } => {
306 trace_origins.push(TraceHistory {
307 source: *stored_id,
308 destination: *destination_id,
309 });
310 }
311 _ => (),
312 }
313}
314
315fn organise_movements(mut v: Vec<TraceHistory>) -> Vec<Vec<RuleIndex>> {
318 v.sort_by_key(|x| std::cmp::Reverse((x.source.0, x.destination.0)));
319 let mut threads: Vec<Vec<RuleIndex>> = vec![];
320 for TraceHistory {
321 source,
322 destination,
323 } in v
324 {
325 if let Some(x) = threads
326 .iter_mut()
327 .find(|x| x.last().is_some_and(|x| *x == source))
328 {
329 x.push(destination);
330 } else {
331 threads.push(vec![source, destination]);
332 }
333 }
334 threads
335}
336
337#[derive(Debug, Clone, Eq, PartialEq)]
338pub(super) struct MovementHelper<C> {
339 pub(crate) trace_origins: Vec<Vec<RuleIndex>>,
340 movement_features: Vec<Vec<C>>,
341 movement_ids: HashMap<RuleIndex, (usize, usize)>,
342}
343
344impl<C> MovementHelper<C> {
345 fn movements(&self) -> impl Iterator<Item = (RuleIndex, RuleIndex)> {
346 self.trace_origins
347 .iter()
348 .flat_map(|movement| movement.iter().copied().tuple_windows::<(_, _)>())
349 }
350}
351
352#[derive(Debug, Clone, PartialEq, Eq, Hash)]
353pub struct Storage<C> {
354 h: BTreeMap<usize, Vec<C>>,
355}
356
357impl<C: Display + Eq> Display for Storage<C> {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 write!(
360 f,
361 "{{{}}}",
362 self.values()
363 .map(|x| x.iter().map(Feature::Licensee).join(" "))
364 .join(", ")
365 )
366 }
367}
368
369impl<C> Default for Storage<C> {
370 fn default() -> Self {
371 Self {
372 h: Default::default(),
373 }
374 }
375}
376
377impl<C: Clone> Storage<C> {
378 fn update_storage(&mut self, r: RuleIndex, m: &MovementHelper<C>) {
379 if let Some((s_id, f_id)) = m.movement_ids.get(&r) {
380 let features = &m.movement_features[*s_id];
381 let value = features.iter().skip(*f_id).cloned().collect::<Vec<_>>();
382 if value.is_empty() {
383 self.h.remove(s_id);
384 } else {
385 self.h.insert(*s_id, value);
386 }
387 }
388 }
389
390 fn add_from(&mut self, other: &Storage<C>) {
391 self.h.extend(other.h.iter().map(|(a, b)| (*a, b.clone())));
392 }
393}
394
395impl<C> Storage<C> {
396 pub fn values(&self) -> std::collections::btree_map::Values<'_, usize, Vec<C>> {
397 self.h.values()
398 }
399
400 pub fn len(&self) -> usize {
401 self.h.len()
402 }
403
404 pub fn is_empty(&self) -> bool {
405 self.h.is_empty()
406 }
407}
408
409fn movement_helpers<T: Eq, C: Eq + Clone>(
410 trace_origins: Vec<TraceHistory>,
411 rules: &RulePool,
412 lex: &Lexicon<T, C>,
413) -> MovementHelper<C> {
414 let trace_origins = organise_movements(trace_origins);
415 let movement_features = trace_origins
416 .iter()
417 .map(|x| {
418 lex.node_to_features(rules.get(*x.first().unwrap()).node(rules).unwrap())
419 .skip(1) .map(crate::lexicon::Feature::into_inner)
421 .collect::<Vec<_>>()
422 })
423 .collect::<Vec<_>>();
424
425 let mut movement_ids = HashMap::default();
426 rules.iter().enumerate().for_each(|(i, r)| match r {
427 Rule::Start { .. } | Rule::UnmoveTrace(_) | Rule::Scan { .. } | Rule::Unmerge { .. } => (),
428 Rule::UnmergeFromMover { stored_id, .. }
429 | Rule::Unmove { stored_id, .. }
430 | Rule::UnmoveFromMover { stored_id, .. } => {
431 let i = RuleIndex(i);
432 movement_ids.insert(
433 i,
434 trace_origins
435 .iter()
436 .enumerate()
437 .find_map(|(movement_id, v)| {
438 v.iter()
439 .position(|x| x == stored_id)
440 .map(|feature_id| (movement_id, feature_id))
441 })
442 .unwrap(),
443 );
444 }
445 });
446
447 MovementHelper {
448 trace_origins,
449 movement_features,
450 movement_ids,
451 }
452}
453
454impl<T: Eq + Clone, C: Eq + Clone + Display> Lexicon<T, C> {
455 pub fn derivation(&self, rules: RulePool) -> Derivation<'static, T, C> {
458 let mut stack = vec![DepthRuleOrder::Todo(RuleIndex(0))];
459 let mut order = vec![];
460 let lemma_lookup = LemmaLookup::new(&rules);
461 let mut trace_origins = Vec::default();
462
463 let nodes: Vec<_> = (0..rules.0.len())
464 .map(|i| {
465 let i = RuleIndex(i);
466 set_trace_destinations(&mut trace_origins, i, &rules);
467 rules.get_node(self, i, &lemma_lookup)
468 })
469 .collect();
470
471 let movement = movement_helpers(trace_origins, &rules, self);
472
473 let mut h: HashMap<_, Vec<_>> = HashMap::default();
474 for (target, info) in rules.iter().enumerate().filter_map(|(i, x)| match x {
475 Rule::Scan { stolen, .. } => match stolen {
476 StolenInfo::Normal => None,
477 StolenInfo::Stealer => Some((RuleIndex(i), (GornIndex::default(), RuleIndex(i)))),
478 StolenInfo::Stolen(rule_index, gorn_index) => {
479 Some((*rule_index, (*gorn_index, RuleIndex(i))))
480 }
481 },
482 _ => None,
483 }) {
484 h.entry(target).or_default().push(info);
485 }
486
487 let mut head_movement: BTreeMap<RuleIndex, RuleIndex> = BTreeMap::default();
488 for (target, info) in h {
489 for (gorn, x) in info.iter().filter(|(_, x)| x != &target) {
490 let parent = info.iter().find_map(|(tg_gorn, tgt)| {
491 if tg_gorn.is_parent_of(*gorn) {
492 Some(*tgt)
493 } else {
494 None
495 }
496 });
497 head_movement.insert(*x, parent.unwrap());
498 }
499 }
500
501 let mut windows = vec![];
502
503 while let Some(x) = stack.pop() {
504 match x {
505 DepthRuleOrder::Todo(rule_index) => {
506 stack.push(DepthRuleOrder::Done(rule_index));
507 stack.extend(rules.complement_last(rule_index).map(DepthRuleOrder::Todo));
508 }
509 DepthRuleOrder::Done(rule_index) => {
510 if !matches!(
511 rules.get(rule_index),
512 Rule::Start { .. } | Rule::Scan { .. } | Rule::UnmoveTrace(_)
513 ) | windows.is_empty()
514 {
515 windows.push(order.len());
516 }
517
518 order.push(rule_index);
519 }
520 }
521 }
522 let Some(RuleIndex(0)) = order.pop() else {
523 panic!("Malformed rules!")
524 };
525
526 Derivation {
527 order,
528 nodes,
529 rules,
530 movement,
531 head_movement,
532 windows,
533 #[cfg(feature = "semantics")]
534 semantics: None,
535 #[cfg(not(feature = "semantics"))]
536 semantics: PhantomData,
537 }
538 }
539}
540
541#[cfg(feature = "semantics")]
542impl<'src, T: Eq + Debug + Clone, C: Eq + Debug + Clone + Display> SemanticLexicon<'src, T, C> {
543 #[must_use]
547 pub fn derivation(&self, rules: RulePool, h: SemanticHistory<'src>) -> Derivation<'src, T, C> {
548 let Derivation {
549 order,
550 rules,
551 nodes,
552 movement,
553 head_movement,
554 windows,
555 semantics: _,
556 } = self.lexicon().derivation(rules);
557 Derivation {
558 order,
559 rules,
560 nodes,
561 movement,
562 head_movement,
563 windows,
564 semantics: Some(h),
565 }
566 }
567}
568
569#[derive(Debug, Clone, Eq, PartialEq)]
572pub struct Derivation<'src, T, C: Eq + Display> {
573 order: Vec<RuleIndex>,
574 rules: RulePool,
575 nodes: Vec<MgNode<T, C>>,
576 head_movement: BTreeMap<RuleIndex, RuleIndex>,
577 windows: Vec<usize>,
578 pub(super) movement: MovementHelper<C>,
579 #[cfg(feature = "semantics")]
580 semantics: Option<SemanticHistory<'src>>,
581 #[cfg(not(feature = "semantics"))]
582 semantics: PhantomData<&'src ()>,
583}
584
585impl<'src, T: Clone, C: Clone + Eq + Display> Derivation<'src, T, C> {
586 #[must_use]
588 pub fn trees(&self) -> impl DoubleEndedIterator<Item = TreeWithMovement<'src, T, C>> {
589 (0..self.windows.len()).map(|x| {
590 let o = self.windows[x];
591 self.tree_and_movement(self.order[o], o)
592 })
593 }
594
595 #[must_use]
597 pub fn tree(&self) -> TreeWithMovement<'src, T, C> {
598 self.tree_and_movement(*self.order.last().unwrap(), self.order.len() - 1)
599 }
600
601 #[must_use]
603 pub fn number_of_trees(&self) -> usize {
604 self.windows.len()
605 }
606
607 #[must_use]
609 pub fn nth_tree(&self, n: usize) -> TreeWithMovement<'src, T, C> {
610 let o = self.windows[n];
611 self.tree_and_movement(self.order[o], o)
612 }
613
614 fn tree_and_movement(&self, rule: RuleIndex, max_order: usize) -> TreeWithMovement<'src, T, C> {
615 let valid_rules = &self.order[..max_order];
616 let head_movement = self.head_movement.iter().filter_map(|(x, y)| {
617 if valid_rules.contains(x) && valid_rules.contains(y) {
618 Some((*x, *y))
619 } else {
620 None
621 }
622 });
623 TreeWithMovement::new(
624 self.tree_at(rule, max_order),
625 head_movement,
626 self.movement
627 .movements()
628 .filter(|(a, b)| valid_rules.contains(a) && valid_rules.contains(b)),
629 )
630 }
631
632 fn tree_at(&self, mut rule: RuleIndex, max_order: usize) -> Tree<'src, T, C> {
633 let og_rule = rule;
634 let valid_rules = &self.order[..max_order];
635
636 if let Some(movement) = self
638 .movement
639 .trace_origins
640 .iter()
641 .find(|x| x.contains(&rule))
642 {
643 let n = movement
644 .iter()
645 .enumerate()
646 .find_map(|(i, rule)| {
647 if valid_rules.contains(rule) {
648 None
649 } else {
650 Some(i)
651 }
652 })
653 .unwrap_or(movement.len());
654
655 if n != 0 {
656 let pos = (movement.iter().position(|x| *x == rule).unwrap() + 1) % n;
657 rule = movement[pos];
658 }
659 }
660
661 let mut node = self.nodes[rule.0].clone();
663 if let Some(tgt) = self.head_movement.get(&rule)
664 && !valid_rules.contains(tgt)
665 {
666 let MgNode::Leaf {
667 lemma: Lemma::Multi { stolen, .. },
668 ..
669 } = &mut node
670 else {
671 panic!("self.head_movement must only contain multi-headed lemma leaf nodes");
672 };
673 *stolen = false;
674 }
675
676 let mut children = vec![];
677 let mut storage = Storage::default();
678 for child in self
679 .rules
680 .get(rule)
681 .children_directed()
682 .map(|rule| self.tree_at(rule, max_order))
683 {
684 storage.add_from(child.storage());
685 children.push(child);
686 }
687 storage.update_storage(rule, &self.movement);
688
689 #[cfg(feature = "semantics")]
690 if let Some(semantics) = &self.semantics {
691 Tree::new_with_semantics(
692 node,
693 semantics.semantic_node(rule),
694 storage,
695 children,
696 og_rule,
697 )
698 } else {
699 Tree::new(node, storage, children, og_rule)
700 }
701 #[cfg(not(feature = "semantics"))]
702 Tree::new(node, storage, children, og_rule)
703 }
704}
705
706#[cfg(test)]
707mod test {
708 use crate::{Lexicon, ParsingConfig};
709 use logprob::LogProb;
710
711 use crate::PhonContent;
712 use crate::grammars::{COPY_LANGUAGE, STABLER2011};
713
714 #[test]
715 fn complicated_head_movement() -> anyhow::Result<()> {
716 let grammar = [
717 "John::d -k -q",
718 "some::n= d -k -q",
720 "vase::n",
721 "see::d= +k V",
723 "break::d= V",
724 "::=>V v",
726 "::v<= =d +k voice",
727 "s::=>voice +q t",
728 "::=>V +q agrO",
729 "::=>V +k +q agrO",
730 "::=>agrO v",
731 "::=>v +k voice",
732 ]
733 .join("\n");
734
735 let lex =
736 Lexicon::from_string(grammar.as_str()).map_err(|e| anyhow::anyhow!(e.to_string()))?;
737 let (_, _, r) = lex
738 .parse(
739 &[
740 PhonContent::Normal("John"),
741 PhonContent::Affixed(vec!["break", "s"]),
742 PhonContent::Normal("some"),
743 PhonContent::Normal("vase"),
744 ],
745 "t",
746 &ParsingConfig::default(),
747 )
748 .unwrap()
749 .next()
750 .unwrap();
751 let d = lex.derivation(r);
752 let tree = d.nth_tree(2);
753 let s = serde_json::to_string(&tree)?;
754 println!("{s}");
755 assert_eq!(
756 s,
757 "{\"tree\":[{\"Node\":{\"features\":[\"V\"],\"movement\":[[\"-k\",\"-q\"]]}},{\"Leaf\":{\"features\":[\"d=\",\"V\"],\"lemma\":{\"Multi\":{\"heads\":[\"break\"],\"original_head\":0,\"stolen\":false}}}},[{\"Node\":{\"features\":[\"d\",\"-k\",\"-q\"],\"movement\":[]}},{\"Leaf\":{\"features\":[\"n=\",\"d\",\"-k\",\"-q\"],\"lemma\":{\"Single\":\"some\"}}},{\"Leaf\":{\"features\":[\"n\"],\"lemma\":{\"Single\":\"vase\"}}}]],\"head_movement\":[],\"phrasal_movement\":[]}"
758 );
759
760 let tree = d.tree();
761 let s = serde_json::to_string(&tree)?;
762 println!("{s}");
763 assert_eq!(
764 s,
765 "{\"tree\":[{\"Node\":{\"features\":[\"t\"],\"movement\":[]}},{\"Leaf\":{\"features\":[\"d\",\"-k\",\"-q\"],\"lemma\":{\"Single\":\"John\"}}},[{\"Node\":{\"features\":[\"+q\",\"t\"],\"movement\":[[\"-q\"]]}},{\"Leaf\":{\"features\":[\"=>voice\",\"+q\",\"t\"],\"lemma\":{\"Multi\":{\"heads\":[null,\"break\",null,null,\"s\"],\"original_head\":4,\"stolen\":false}}}},[{\"Node\":{\"features\":[\"voice\"],\"movement\":[[\"-q\"]]}},{\"Trace\":{\"trace\":0}},[{\"Node\":{\"features\":[\"+k\",\"voice\"],\"movement\":[[\"-k\",\"-q\"]]}},{\"Trace\":{\"trace\":1}},[{\"Node\":{\"features\":[\"=d\",\"+k\",\"voice\"],\"movement\":[]}},{\"Leaf\":{\"features\":[\"v<=\",\"=d\",\"+k\",\"voice\"],\"lemma\":{\"Multi\":{\"heads\":[null,\"break\",null,null],\"original_head\":0,\"stolen\":true}}}},[{\"Node\":{\"features\":[\"v\"],\"movement\":[]}},{\"Leaf\":{\"features\":[\"=>agrO\",\"v\"],\"lemma\":{\"Multi\":{\"heads\":[\"break\",null,null],\"original_head\":2,\"stolen\":true}}}},[{\"Node\":{\"features\":[\"agrO\"],\"movement\":[]}},[{\"Node\":{\"features\":[\"d\",\"-k\",\"-q\"],\"movement\":[]}},{\"Leaf\":{\"features\":[\"n=\",\"d\",\"-k\",\"-q\"],\"lemma\":{\"Single\":\"some\"}}},{\"Leaf\":{\"features\":[\"n\"],\"lemma\":{\"Single\":\"vase\"}}}],[{\"Node\":{\"features\":[\"+q\",\"agrO\"],\"movement\":[[\"-q\"]]}},{\"Trace\":{\"trace\":2}},[{\"Node\":{\"features\":[\"+k\",\"+q\",\"agrO\"],\"movement\":[[\"-k\",\"-q\"]]}},{\"Leaf\":{\"features\":[\"=>V\",\"+k\",\"+q\",\"agrO\"],\"lemma\":{\"Multi\":{\"heads\":[\"break\",null],\"original_head\":1,\"stolen\":true}}}},[{\"Node\":{\"features\":[\"V\"],\"movement\":[[\"-k\",\"-q\"]]}},{\"Leaf\":{\"features\":[\"d=\",\"V\"],\"lemma\":{\"Multi\":{\"heads\":[\"break\"],\"original_head\":0,\"stolen\":true}}}},{\"Trace\":{\"trace\":3}}]]]]]]]]]],\"head_movement\":[[\"11110\",\"10\"],[\"111110\",\"11110\"],[\"111111110\",\"111110\"],[\"1111111110\",\"111111110\"]],\"phrasal_movement\":[[\"1111111111\",\"11111110\"],[\"11111110\",\"1111110\"],[\"1110\",\"110\"],[\"110\",\"0\"]]}"
766 );
767 Ok(())
768 }
769
770 #[test]
771 fn to_graph() -> anyhow::Result<()> {
772 let lex = Lexicon::from_string(STABLER2011)?;
773 let config = ParsingConfig::new(
774 LogProb::new(-256.0).unwrap(),
775 LogProb::from_raw_prob(0.5).unwrap(),
776 100,
777 1000,
778 );
779 for sentence in vec!["which wine the queen prefers"].into_iter() {
780 let (_, _, rules) = lex
781 .parse(
782 &sentence
783 .split(' ')
784 .map(PhonContent::Normal)
785 .collect::<Vec<_>>(),
786 "C",
787 &config,
788 )?
789 .next()
790 .unwrap();
791 let latex = lex.derivation(rules).tree().latex();
792
793 println!("{}", latex);
794 assert_eq!(
795 latex,
796 "\\begin{forest}[\\der{C} [\\der{D -W} [\\plainlex{N= D -W}{which}] [\\plainlex{N}{wine}]] [\\der{+W C} [\\plainlex{V= +W C}{$\\epsilon$}] [\\der{V} [\\der{D} [\\plainlex{N= D}{the}] [\\plainlex{N}{queen}]] [\\der{=D V} [\\plainlex{D= =D V}{prefers}] [$t_0$]]]]]\\end{forest}"
797 );
798 }
799 Ok(())
800 }
801
802 #[test]
803 fn double_movement_graph() -> anyhow::Result<()> {
804 let lex = Lexicon::from_string(COPY_LANGUAGE)?;
805 let config = ParsingConfig::new(
806 LogProb::new(-256.0).unwrap(),
807 LogProb::from_raw_prob(0.5).unwrap(),
808 100,
809 1000,
810 );
811 for sentence in vec!["a b a b"].into_iter() {
812 let (_, _, rules) = lex
813 .parse(
814 &sentence
815 .split(' ')
816 .map(PhonContent::Normal)
817 .collect::<Vec<_>>(),
818 "T",
819 &config,
820 )?
821 .next()
822 .unwrap();
823 let latex = lex.derivation(rules).tree().latex();
824
825 println!("{}", latex);
826 assert_eq!(
827 latex,
828 "\\begin{forest}[\\der{T} [\\der{T -l} [\\der{T -l} [\\plainlex{T -r -l}{$\\epsilon$}] [\\der{+l T -l} [$t_3$] [\\plainlex{=A +l T -l}{a}]]] [\\der{+l T -l} [$t_1$] [\\plainlex{=B +l T -l}{b}]]] [\\der{+l T} [\\der{B -r} [\\der{A -r} [$t_4$] [\\der{+r A -r} [$t_5$] [\\plainlex{=T +r A -r}{a}]]] [\\der{+r B -r} [$t_2$] [\\plainlex{=T +r B -r}{b}]]] [\\der{+r +l T} [$t_0$] [\\plainlex{=T +r +l T}{$\\epsilon$}]]]]\\end{forest}"
829 );
830 }
831 Ok(())
832 }
833}