Skip to main content

fandango_core/
graph.rs

1//! Graph operations for lifting FANDANGO grammars to a graph.
2
3use crate::lang::constraints::{
4    Atom, BaseSelection, Comparison, Conjunction, Constraint, Disjunction, Expr, Implies,
5    Inversion, Quantifier, QuantifierSpecification, RsPair, RsPairs, RsSlices, Selection, Selector,
6    SelectorLength,
7};
8use crate::lang::{
9    Alternative, Concatenation, FandangoNode, Nonterminal, Operator, Production, Program,
10    Statement, Symbol,
11};
12use alloc::collections::VecDeque;
13use alloc::vec::Vec;
14use core::iter;
15use core::ops::Deref;
16use hashbrown::HashMap;
17use pest::Span;
18use petgraph::graph::{DiGraph, NodeIndex};
19use petgraph::graphmap::NodeTrait;
20use petgraph::prelude::EdgeRef;
21use petgraph::visit::IntoNodeReferences;
22use petgraph::{Direction, graph};
23
24/// Traverse this type's children, potentially recursively, for use with grammar graph creation.
25#[allow(unused_variables)]
26pub trait GraphTraverse<'program>: Sized {
27    /// The node type for graph.
28    type Node: NodeTrait + GraphTraverse<'program, Node = Self::Node> + From<Self>;
29
30    /// Recurse the traversal! The order of calls to `consumer` are not guaranteed, but ultimately
31    /// will invoke [`GraphTraverse::traverse`] for each level.
32    fn recurse<F>(self, mut consumer: F)
33    where
34        F: FnMut(Self::Node, Self::Node, Span<'program>),
35    {
36        self.traverse(|n1, n2, w| {
37            consumer(n1, n2, w);
38            n2.traverse(&mut consumer);
39        });
40    }
41
42    /// Traverse a single level from this node. The `consumer` function should accept two nodes, the
43    /// parent and the child, as well as an unsigned integer denoting the index of the children.
44    fn traverse<F>(self, consumer: F)
45    where
46        F: FnMut(Self::Node, Self::Node, Span<'program>),
47    {
48    }
49}
50
51/// Call `consumer` for each `i, child` in `children.enumerate()` with `consumer(parent, child, i)`.
52pub fn traverse_children<'program, 'source, F, T>(
53    parent: T,
54    children: impl Iterator<Item = (T::Node, Span<'source>)>,
55    mut consumer: F,
56) where
57    F: FnMut(T::Node, T::Node, Span<'source>),
58    T: GraphTraverse<'program>,
59    'source: 'program,
60{
61    let node = parent.into();
62    for (child, weight) in children {
63        consumer(node, child, weight);
64    }
65}
66
67/// Internal macro for compiling iterator chains, for use in [`crate::impl_traverse`].
68#[macro_export]
69macro_rules! chain_field_iter {
70    ($node:ty $(=> $from:tt)?, $current:expr) => {
71        $current
72    };
73
74    ($node:ty $(=> $from:tt)?, $current:expr, $field:tt) => {
75        $current.chain($crate::field_iter!($node $(=> $from)?, $field))
76    };
77
78    ($node:ty $(=> $from:tt)?, $current:expr, $field:tt, $($fields:tt),+) => {{
79        let next = $crate::chain_field_iter!($node $(=> $from)?, $current, $field);
80        $crate::chain_field_iter!($node $(=> $from)?, next, $($fields),+)
81    }};
82}
83
84/// Internal macro for producing iterators over fields, for use in [`crate::impl_traverse`].
85#[macro_export]
86macro_rules! field_iter {
87    ($node:ty $(=> $from:tt)?) => {
88        ::core::iter::empty()
89    };
90
91    ($node:ty $(=> $from:tt)?, [ $field:tt ]) => {
92        $($from.)? $field.iter().map(::core::convert::From::from)
93    };
94
95    ($node:ty $(=> $from:tt)?, $field:tt) => {
96        ::core::iter::once(&$($from.)? $field).map(::core::convert::From::from)
97    };
98
99    ($node:ty $(=> $from:tt)?, $field:tt, $($fields:tt),+) => {{
100        let next = $crate::field_iter!($node $(=> $from)?, $field);
101        $crate::chain_field_iter!($node $(=> $from)?, next, $($fields),+)
102    }};
103}
104
105/// Internal macro for compiling iterator chains, for use in [`crate::impl_traverse`], over enums.
106#[macro_export]
107macro_rules! variant_traverse {
108    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { $($bindings:tt),+ } { $($iteration:tt)+ } { } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
109        $crate::variant_traverse!(
110            $from,
111            $consumer,
112            $node,
113            @($($($variants)+)?),
114            $($emitted)*
115            $variant($($bindings),+) => { $crate::graph::traverse_children($from, $($iteration)+, $consumer) }
116        )
117    };
118
119    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { $($bindings:tt),+ } { $($iteration:tt)+ } { _ $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
120        $crate::variant_traverse!(
121            $from,
122            $consumer,
123            $node,
124            @(
125                $variant
126                { $($bindings),+, _ }
127                { $($iteration)+ }
128                { $($($remaining),+)? }
129                $(, $($variants)+)?
130            ),
131            $($emitted)*
132        )
133    };
134
135    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { $($bindings:tt),+ } { $($iteration:tt)+ } { $next:tt $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
136        $crate::variant_traverse!(
137            $from,
138            $consumer,
139            $node,
140            @(
141                $variant
142                { $($bindings),+, $next }
143                { $($iteration)+.chain(::core::iter::once($next).map(::core::convert::From::from)) }
144                { $($($remaining),+)? }
145                $(, $($variants)+)?
146            ),
147            $($emitted)*
148        )
149    };
150
151    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { $($bindings:tt),+ } { $($iteration:tt)+ } { [ $next:tt ] $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
152        $crate::variant_traverse!(
153            $from,
154            $consumer,
155            $node,
156            @(
157                $variant
158                { $($bindings),+, $next }
159                { $($iteration)+.chain($next.iter().map(::core::convert::From::from)) }
160                { $($($remaining),+)? }
161                $(, $($variants)+)?
162            ),
163            $($emitted)*
164        )
165    };
166
167    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { } { } { $next:tt $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
168        $crate::variant_traverse!(
169            $from,
170            $consumer,
171            $node,
172            @(
173                $variant
174                { $next }
175                { ::core::iter::once($next).map(::core::convert::From::from) }
176                { $($($remaining),+)? }
177                $(, $($variants)+)?
178            ),
179            $($emitted)*
180        )
181    };
182
183    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { } { } { _ $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
184        $crate::variant_traverse!(
185            $from,
186            $consumer,
187            $node,
188            @(
189                $variant
190                { }
191                { }
192                { $($($remaining),+)? }
193                $(, $($variants)+)?
194            ),
195            $($emitted)*
196        )
197    };
198
199    ($from:tt, $consumer:tt, $node:ty, @($variant:tt { } { } { [ $next:tt ] $(, $($remaining:tt),+)? } $(, $($variants:tt)+)?), $($emitted:tt)*) => {
200        $crate::variant_traverse!(
201            $from,
202            $consumer,
203            $node,
204            @(
205                $variant
206                { $next }
207                { $next.iter().map(::core::convert::From::from) }
208                { $($($remaining),+)? }
209                $(, $($variants)+)?
210            ),
211            $($emitted)*
212        )
213    };
214
215    ($from:tt, $consumer:tt, $node:ty, @($variant:tt $(, $($variants:tt)+)?), $($emitted:tt)*) => {
216        $crate::variant_traverse!(
217            $from,
218            $consumer,
219            $node,
220            @($($($variants)+)?),
221            $($emitted)*
222            $variant => {  }
223        )
224    };
225
226    ($from:tt, $consumer:tt, $node:ty, @($variant:tt ( $($options:tt),+ ) $(, $($variants:tt)+)?), $($emitted:tt)*) => {
227        $crate::variant_traverse!(
228            $from,
229            $consumer,
230            $node,
231            @(
232                $variant
233                { }
234                { }
235                { $($options),+ }
236                $(, $($variants)+)?
237            ),
238            $($emitted)*
239        )
240    };
241
242    ($from:tt, $consumer:tt, $node:ty, { $($variants:tt)+ }) => {
243        $crate::variant_traverse!(
244            $from,
245            $consumer,
246            $node,
247            @($($variants)+),
248        )
249    };
250
251    ($from:tt, $consumer:tt, $node:ty, @(), $($emitted:tt)*) => {
252        match $from {
253            $($emitted)*
254        }
255    };
256}
257
258/// Macro which generates implementations of [`GraphTraverse`] over fields of the provided struct or
259/// variants of the provided enum. The lifetime `'source` is already within the lifetime list and
260/// corresponds to the lifetime of the source code.
261///
262/// The first four fields are, in order:
263/// 1. The type for which [`GraphTraverse`] is to be implemented.
264/// 2. The name of the raw type (e.g., if providing the type behind a reference).
265/// 3. The node type of the graph (e.g., [`FandangoNode`]).
266/// 4. The generics/lifetimes required for the implementation (optional).
267///
268/// The remaining argument(s) are a variadic list of fields or a list of match-like enum pattern
269/// bindings without the `=> { ... }` clause, surrounded by `match { }`. Fields or enum bindings
270/// which are surrounded by `[]` will be interpreted as iterables and those without will be
271/// considered as something which can be immediately [`Into::into`]'d into the corresponding node
272/// type. The order of variables will be preserved, and the enumeration will take place over the
273/// combined iterator.
274///
275/// This enum can simplify the implementation of traversal for structures with a variety of layouts.
276/// For example, `statements` here has an `iter` method, the items for which implement [`Into`] for
277/// the node type [`FandangoNode`].
278/// ```rust,ignore
279/// # use fandango::graph::FandangoNode;
280/// # use fandango::lang::Program;
281/// fandango::impl_traverse!(
282///     &'program Program<'source>,
283///     Program,
284///     FandangoNode<'program, 'source>,
285///     <'source: 'program>,
286///     [statements]
287/// );
288/// ```
289///
290/// It is also possible to do this for enums across multiple variants:
291/// ```rust,ignore
292/// # use fandango::graph::FandangoNode;
293/// # use fandango::lang::Statement;
294/// fandango::impl_traverse!(
295///     &'program Statement<'source>,
296///     Statement,
297///     FandangoNode<'program, 'source>,
298///     <'source: 'program>,
299///     match { Production(prod), Constraint, Python }
300/// );
301/// ```
302#[macro_export]
303macro_rules! impl_traverse {
304    ($target:ty, $name:ty, $node:ty, < $($generics:tt $(: $constraints:tt)?),* >, match { $($variants:tt)+ }) => {
305        impl<'program, $($generics $(: $constraints)?),*> $crate::graph::GraphTraverse<'program> for $target {
306            type Node = $node;
307
308            fn traverse<F>(self, consumer: F)
309            where
310                F: ::core::ops::FnMut(Self::Node, Self::Node, $crate::lang::Span<'program>),
311            {
312                #![allow(unused_imports)]
313                use $name::*;
314                $crate::variant_traverse!(self, consumer, $node, { $($variants)+ })
315            }
316        }
317    };
318
319    ($target:ty, $name:ty, $node:ty, < $($generics:tt $(: $constraints:tt)?),* >, $($fields:tt),*) => {
320        impl<'program, $($generics $(: $constraints)?),*> $crate::graph::GraphTraverse<'program> for $target {
321            type Node = $node;
322
323            fn traverse<F>(self, consumer: F)
324            where
325                F: ::core::ops::FnMut(Self::Node, Self::Node,  $crate::lang::Span<'program>),
326            {
327                $crate::graph::traverse_children(self, $crate::field_iter!($node => self, $($fields),*), consumer);
328            }
329        }
330    };
331
332    ($target:ty, $name:ty, $node:ty, match { $($variants:tt)+ }) => {
333        $crate::impl_traverse!($target, $name, $node, <>, { $($variants)+ })
334    };
335
336    ($target:ty, $name:ty, $node:ty, $($fields:tt),*) => {
337        $crate::impl_traverse!($target, $name, $node, <>, $($fields),*)
338    };
339}
340
341impl<'program, 'source> GraphTraverse<'program> for &'program Nonterminal<'source> {
342    type Node = FandangoNode<'program, 'source>;
343}
344
345impl<'program, 'source> GraphTraverse<'program> for FandangoNode<'program, 'source>
346where
347    'source: 'program,
348{
349    type Node = Self;
350
351    fn traverse<F>(self, consumer: F)
352    where
353        F: FnMut(Self::Node, Self::Node, Span<'program>),
354    {
355        match self {
356            FandangoNode::Program(s) => s.traverse(consumer),
357            FandangoNode::Statement(s) => s.traverse(consumer),
358            FandangoNode::Production(s) => s.traverse(consumer),
359            FandangoNode::Alternative(s) => s.traverse(consumer),
360            FandangoNode::Concatenation(s) => s.traverse(consumer),
361            FandangoNode::Operator(s) => s.traverse(consumer),
362            FandangoNode::Symbol(s) => s.traverse(consumer),
363            FandangoNode::Nonterminal(s) => s.traverse(consumer),
364            FandangoNode::Constraint(s) => s.traverse(consumer),
365            FandangoNode::Implies(s) => s.traverse(consumer),
366            FandangoNode::Quantifier(s) => s.traverse(consumer),
367            FandangoNode::QuantifierSpecification(s) => s.traverse(consumer),
368            FandangoNode::Disjunction(s) => s.traverse(consumer),
369            FandangoNode::Conjunction(s) => s.traverse(consumer),
370            FandangoNode::Atom(s) => s.traverse(consumer),
371            FandangoNode::Comparison(s) => s.traverse(consumer),
372            FandangoNode::Expr(s) => s.traverse(consumer),
373            FandangoNode::SelectorLength(s) => s.traverse(consumer),
374            FandangoNode::Selection(s) => s.traverse(consumer),
375            FandangoNode::Selector(s) => s.traverse(consumer),
376            FandangoNode::BaseSelection(s) => s.traverse(consumer),
377            FandangoNode::RsPairs(s) => s.traverse(consumer),
378            FandangoNode::RsPair(s) => s.traverse(consumer),
379            FandangoNode::RsSlices(s) => s.traverse(consumer),
380            FandangoNode::Inversion(s) => s.traverse(consumer),
381            // nothing to do in these cases; they are terminals
382            FandangoNode::String(_)
383            | FandangoNode::ConstraintOperator(_)
384            | FandangoNode::RsSlice(_) => {}
385        }
386    }
387}
388
389impl<'program, 'source: 'program> GraphTraverse<'program> for &'program Selector<'source> {
390    type Node = FandangoNode<'program, 'source>;
391
392    fn traverse<F>(self, consumer: F)
393    where
394        F: FnMut(Self::Node, Self::Node, Span<'program>),
395    {
396        #![allow(unused_imports)]
397        use Selector::{Basic, ChildSelector, PathSelector};
398        match self {
399            ChildSelector(basic, child) => traverse_children(
400                self,
401                iter::once(basic)
402                    .map(From::from)
403                    .chain(iter::once(&**child).map(From::from)),
404                consumer,
405            ),
406            PathSelector(basic, descendent) => traverse_children(
407                self,
408                iter::once(basic)
409                    .map(From::from)
410                    .chain(iter::once(&**descendent).map(From::from)),
411                consumer,
412            ),
413            Basic(basic) => traverse_children(self, iter::once(basic).map(From::from), consumer),
414        }
415    }
416}
417impl<'program, 'source: 'program> GraphTraverse<'program> for &'program BaseSelection<'source> {
418    type Node = FandangoNode<'program, 'source>;
419
420    fn traverse<F>(self, consumer: F)
421    where
422        F: FnMut(Self::Node, Self::Node, Span<'program>),
423    {
424        #![allow(unused_imports)]
425        use BaseSelection::{Nonterminal, Selector};
426        match self {
427            Nonterminal(nonterminal) => {
428                traverse_children(self, iter::once(nonterminal).map(From::from), consumer);
429            }
430            Selector(selector) => {
431                traverse_children(self, iter::once(&**selector).map(From::from), consumer);
432            }
433        }
434    }
435}
436
437impl<'program, 'source: 'program> GraphTraverse<'program>
438    for &'program QuantifierSpecification<'source>
439{
440    type Node = FandangoNode<'program, 'source>;
441
442    fn traverse<F>(self, consumer: F)
443    where
444        F: FnMut(Self::Node, Self::Node, Span<'program>),
445    {
446        traverse_children(
447            self,
448            {
449                let next = iter::once(&self.nonterminal).map(From::from);
450                {
451                    let next = next.chain(iter::once(&self.selector).map(From::from));
452                    next.chain(
453                        iter::once(&self.quantifier)
454                            .map(Deref::deref)
455                            .map(From::from),
456                    )
457                }
458            },
459            consumer,
460        );
461    }
462}
463
464impl<'program, 'source: 'program> GraphTraverse<'program> for &'program Implies<'source> {
465    type Node = FandangoNode<'program, 'source>;
466
467    fn traverse<F>(self, consumer: F)
468    where
469        F: FnMut(Self::Node, Self::Node, Span<'program>),
470    {
471        traverse_children(
472            self,
473            {
474                let next = iter::once(&self.quantifier).map(From::from);
475                next.chain(self.implies.iter().map(Deref::deref).map(From::from))
476            },
477            consumer,
478        );
479    }
480}
481
482macro_rules! impl_fandango_traverse {
483    ($target:tt, match { $($variants:tt)+ }) => {
484        impl_traverse!(
485            &'program $target<'source>,
486            $target,
487            FandangoNode<'program, 'source>,
488            <'source: 'program>,
489            match { $($variants)+ }
490        );
491    };
492
493    ($target:tt, $($fields:tt),*) => {
494        impl_traverse!(
495            &'program $target<'source>,
496            $target,
497            FandangoNode<'program, 'source>,
498            <'source: 'program>,
499            $($fields),*
500        );
501    };
502}
503
504impl_fandango_traverse!(Program, [statements]);
505impl_fandango_traverse!(Statement, match { Production(prod), Constraint(constraint), Python });
506impl_fandango_traverse!(Production, nonterminal, alternative);
507impl_fandango_traverse!(Alternative, [concatenations]);
508impl_fandango_traverse!(Concatenation, [operators]);
509impl_fandango_traverse!(Operator, match { Kleene(sym), Plus(sym), Option(sym), Repeat(sym, _, _), Symbol(sym) });
510impl_fandango_traverse!(Symbol, match { Nonterminal(nt), String(s), Alternative(alt) });
511
512impl_fandango_traverse!(Constraint, match { Fitness(fitness), Implies(implies) });
513impl_fandango_traverse!(Quantifier, match { Forall(forall), Exists(exists), Disjunction(disjunction) });
514impl_fandango_traverse!(Disjunction, [conjunctions]);
515impl_fandango_traverse!(Conjunction, [atoms]);
516impl_fandango_traverse!(Atom, match { Comparison(comp), Implies(implies), Expr(expr) });
517impl_fandango_traverse!(Comparison, left, right, operator);
518impl_fandango_traverse!(Expr, match { Selector(selector), Inversion(inversion) });
519impl_fandango_traverse!(SelectorLength, match { WithLength(selector), NoLength(selector) });
520impl_fandango_traverse!(Selection, match { OverSlices(basic, slices), OverPairs(basic, pairs), Basic(basic) });
521impl_fandango_traverse!(RsPairs, [pairs]);
522impl_fandango_traverse!(RsPair, nonterminal, [slice]);
523impl_fandango_traverse!(RsSlices, [slices]);
524impl_fandango_traverse!(Inversion, match { Selector(selector), Stringified(selector) });
525
526/// Convert a type which implements [`GraphTraverse`] into a [`DiGraph`].
527pub trait IntoGraph<'program>: GraphTraverse<'program> {
528    /// Perform the conversion.
529    fn into_graph(
530        self,
531    ) -> (
532        HashMap<Self::Node, NodeIndex>,
533        DiGraph<Self::Node, Span<'program>>,
534    );
535}
536
537impl<'program, 'source, T> IntoGraph<'program> for T
538where
539    T: GraphTraverse<'program, Node = FandangoNode<'program, 'source>>,
540    'source: 'program,
541{
542    fn into_graph(
543        self,
544    ) -> (
545        HashMap<FandangoNode<'program, 'source>, graph::NodeIndex>,
546        DiGraph<Self::Node, Span<'program>>,
547    ) {
548        let mut graph = DiGraph::new();
549        let mut work = VecDeque::new();
550        self.traverse(|n1, n2, w| work.push_back((n1, n2, w)));
551
552        let mut node_indices: HashMap<FandangoNode<'program, 'source>, graph::NodeIndex> =
553            HashMap::new();
554        let mut idx = |g: &mut DiGraph<FandangoNode<'program, 'source>, _>, n| {
555            *node_indices.entry(n).or_insert_with(|| g.add_node(n))
556        };
557
558        while let Some((n1, n2, w)) = work.pop_front() {
559            match n1 {
560                FandangoNode::Production(_) if matches!(n2, FandangoNode::Nonterminal(_)) => {
561                    let n1 = idx(&mut graph, n1);
562                    let n2 = idx(&mut graph, n2);
563                    graph.add_edge(n1, n2, w);
564                }
565                FandangoNode::Production(prod) => {
566                    work.push_back((prod.nonterminal().into(), n2, w));
567                }
568                FandangoNode::Alternative(alt) if alt.concatenations().len() == 1 => {
569                    n2.traverse(|n1, n2, w| work.push_back((n1, n2, w)));
570                }
571                FandangoNode::Concatenation(concats) if concats.operators().len() == 1 => {
572                    n2.traverse(|n1, n2, w| work.push_back((n1, n2, w)));
573                }
574                FandangoNode::Nonterminal(_)
575                | FandangoNode::Alternative(_)
576                | FandangoNode::Concatenation(_)
577                | FandangoNode::Operator(_) => match n2 {
578                    FandangoNode::Alternative(alt) if alt.concatenations().len() == 1 => {
579                        n2.traverse(|_, n2, w| work.push_back((n1, n2, w)));
580                    }
581                    FandangoNode::Concatenation(concats) if concats.operators().len() == 1 => {
582                        n2.traverse(|_, n2, w| work.push_back((n1, n2, w)));
583                    }
584                    FandangoNode::Alternative(_)
585                    | FandangoNode::Concatenation(_)
586                    | FandangoNode::Operator(_)
587                        if !matches!(n2, FandangoNode::Operator(Operator::Symbol(_))) =>
588                    {
589                        {
590                            let n1 = idx(&mut graph, n1);
591                            let n2 = idx(&mut graph, n2);
592                            graph.add_edge(n1, n2, w);
593                        }
594                        n2.traverse(|n1, n2, w| work.push_back((n1, n2, w)));
595                    }
596                    FandangoNode::Nonterminal(_) | FandangoNode::String(_) => {
597                        let n1 = idx(&mut graph, n1);
598                        let n2 = idx(&mut graph, n2);
599                        graph.add_edge(n1, n2, w);
600                    }
601                    _ => n2.traverse(|_, n2, w| work.push_back((n1, n2, w))),
602                },
603                _ => n2.traverse(|n1, n2, w| work.push_back((n1, n2, w))),
604            }
605        }
606
607        (node_indices, graph)
608    }
609}
610
611/// Transforms a full grammar tree into a node describing only the head.
612pub trait IntoNode {
613    /// The node type which this tree transforms to.
614    type Node;
615
616    /// Perform the conversion!
617    fn into_node(self) -> Self::Node;
618}
619
620/// Computes the shortest derivation trees available from each alternation, returning the variants
621/// with the minimum possible path.
622#[must_use]
623#[allow(clippy::missing_panics_doc)]
624pub fn shortest_path<'program, 'source>(
625    graph: &DiGraph<FandangoNode<'program, 'source>, Span<'source>>,
626) -> HashMap<FandangoNode<'program, 'source>, Vec<usize>> {
627    let mut depths = graph
628        .node_references()
629        .filter_map(|(idx, node)| matches!(node, FandangoNode::String(_)).then_some((idx, 0usize)))
630        .collect::<HashMap<_, _>>();
631    let mut queue = depths
632        .keys()
633        .copied()
634        .flat_map(|term| graph.edges_directed(term, Direction::Incoming))
635        .map(|e| e.source())
636        .collect::<VecDeque<_>>();
637    let mut alternatives = HashMap::new();
638
639    loop {
640        let mut unchanged = true;
641        let mut next_queue = VecDeque::new();
642
643        for next in queue {
644            if depths.contains_key(&next) {
645                continue;
646            }
647            let mut children = graph
648                .edges(next)
649                .map(|e| (e.weight().start(), depths.get(&e.target()).copied()))
650                .collect::<Vec<_>>();
651            children.sort_by_key(|(s, _)| *s);
652
653            let mut depth = None;
654            match graph.node_weight(next).unwrap() {
655                FandangoNode::Alternative(_) => {
656                    let (choices, alt_depth) = children.into_iter().enumerate().fold(
657                        (Vec::new(), usize::MAX),
658                        |(mut current, max_len), (idx, (_, len))| {
659                            if let Some(len) = len {
660                                if len < max_len {
661                                    current.clear();
662                                    current.push(idx);
663                                    return (current, len);
664                                } else if len == max_len {
665                                    current.push(idx);
666                                }
667                            }
668                            (current, max_len)
669                        },
670                    );
671                    alternatives.insert(next, choices);
672                    depth = Some(alt_depth);
673                }
674                _ => {
675                    if let Some(children) = children
676                        .into_iter()
677                        .map(|(_, c)| c)
678                        .collect::<Option<Vec<_>>>()
679                    {
680                        depth = children.into_iter().min();
681                    }
682                }
683            }
684            if let Some(depth) = depth {
685                next_queue.extend(
686                    graph
687                        .edges_directed(next, Direction::Incoming)
688                        .map(|e| e.source()),
689                );
690                unchanged &= depths.insert(next, depth) == Some(depth);
691            }
692        }
693
694        queue = next_queue;
695        if unchanged {
696            break;
697        }
698    }
699
700    alternatives
701        .into_iter()
702        .map(|(idx, paths)| (*graph.node_weight(idx).unwrap(), paths))
703        .collect()
704}
705
706#[cfg(test)]
707mod test {
708    use crate::graph::IntoGraph;
709    use crate::lang::Program;
710    use crate::lang::test::SIMPLE_GRAMMAR;
711
712    use alloc::format;
713
714    use petgraph::data::{Element, FromElements};
715
716    use petgraph::graph::DiGraph;
717
718    extern crate std;
719
720    // this doesn't really test anything, just produces a graph in GraphViz format
721    #[test]
722    fn test_graph() {
723        let program = Program::try_from(SIMPLE_GRAMMAR).unwrap();
724
725        let (_, graph) = (&program).into_graph();
726
727        let _renderable = DiGraph::<_, _>::from_elements(
728            graph
729                .raw_nodes()
730                .iter()
731                .map(|n| Element::Node { weight: n.weight })
732                .chain(graph.raw_edges().iter().map(|e| {
733                    let (start_line, start_col) = e.weight.start_pos().line_col();
734                    let (end_line, end_col) = e.weight.end_pos().line_col();
735                    let rendered = if start_line == end_line {
736                        format!("{start_line}:{start_col}-{end_col}")
737                    } else {
738                        format!("{start_line}:{start_col}-{end_line}:{end_col}")
739                    };
740                    Element::Edge {
741                        source: e.source().index(),
742                        target: e.target().index(),
743                        weight: rendered,
744                    }
745                })),
746        );
747    }
748}