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