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()
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    ///Get this derivation as a [`TreeWithMovement`]
104    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                //We loop down until we find the last feature before the movement.
134                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
316/// Reorganises <(a,b), (b,c), (c,d), (e, f)> to <<a,b,c,d>, <e,f>>
317/// Can't handle orders like <(a,b), (c,d), (b,c)> so we have to sort beforehand
318fn 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) //skip category where merge happened
421                .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    ///Converts a [`RulePool`] into a [`Derivation`] which allows the construction of [`Tree`]s
457    ///which can be used to plot syntactic trees throughout the derivation of the parse.
458    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    ///Converts a [`RulePool`] into a [`Derivation`] which allows the construction of [`Tree`]s
545    ///which can be used to plot syntactic trees throughout the derivation of the parse. This
546    ///version allows for semantic information in the derivation as well.
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    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    ///Get a [`Tree`] representation of the final parse.
595    pub fn tree(&self) -> TreeWithMovement<'src, T, C> {
596        self.tree_and_movement(*self.order.last().unwrap(), self.order.len() - 1)
597    }
598
599    ///How many trees in the derivation
600    pub fn number_of_trees(&self) -> usize {
601        self.windows.len()
602    }
603
604    ///Get the tree at the nth derivation step
605    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        //Handles moving nodes up or down depending on the position in the derivation
633        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        //Marks a stolen head as /not/ stolen if the head's destination isn't in the tree here.
658        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}