minimalist_grammar_parser/parsing/rules/
printing.rs

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//TODO: Window function for derivation
26
27///A representation of a node in a derivation for export.
28#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
29pub enum MgNode<T, C: Eq + Display> {
30    ///A dummy node for the root.
31    Start,
32    ///A normal node in the derivation
33    Node {
34        ///Current features at this node.
35        features: Vec<Feature<C>>,
36    },
37    ///A lemma/leaf node.
38    Leaf {
39        ///The lemma displayed
40        lemma: Lemma<T>,
41        ///The full features of the lexical entry.
42        features: Vec<Feature<C>>,
43    },
44    ///A trace (note usually the target of movement rather than the origin as is typical).
45    Trace {
46        ///The node that is moved here will have the same [`TraceId`]
47        trace: TraceId,
48    },
49}
50
51///Representation of a lemma for display or printing
52#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
53pub enum Lemma<T> {
54    ///A normal lemma
55    Single(Option<T>),
56    ///A head created by affixing multiple heads.
57    Multi {
58        heads: Vec<Option<T>>,
59        original_head: usize,
60        stolen: bool,
61    },
62}
63
64impl<T> Lemma<T> {
65    ///Returns whether the lemma has been stolen by head-movement.
66    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    ///Get this derivation as a [`TreeWithMovement`]
102    #[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                //We loop down until we find the last feature before the movement.
133                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
315/// Reorganises <(a,b), (b,c), (c,d), (e, f)> to <<a,b,c,d>, <e,f>>
316/// Can't handle orders like <(a,b), (c,d), (b,c)> so we have to sort beforehand
317fn 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) //skip category where merge happened
420                .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    ///Converts a [`RulePool`] into a [`Derivation`] which allows the construction of [`Tree`]s
456    ///which can be used to plot syntactic trees throughout the derivation of the parse.
457    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    ///Converts a [`RulePool`] into a [`Derivation`] which allows the construction of [`Tree`]s
544    ///which can be used to plot syntactic trees throughout the derivation of the parse. This
545    ///version allows for semantic information in the derivation as well.
546    #[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///A representation of all of the steps in the derivation of a parse.
570///This can be used to generate a [`Tree`] of any step of the parse.
571#[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    ///Get all possible [`Tree`]s in bottom-up order of a parse.
587    #[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    ///Get a [`Tree`] representation of the final parse.
596    #[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    ///How many trees in the derivation
602    #[must_use] 
603    pub fn number_of_trees(&self) -> usize {
604        self.windows.len()
605    }
606
607    ///Get the tree at the nth derivation step
608    #[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        //Handles moving nodes up or down depending on the position in the derivation
637        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        //Marks a stolen head as /not/ stolen if the head's destination isn't in the tree here.
662        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            //"Mary::d -k -q",
719            "some::n= d -k -q",
720            "vase::n",
721            //:will"dance::V",
722            "see::d= +k V",
723            "break::d= V",
724            //"fall::d= v",
725            "::=>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}