Skip to main content

vertep_core/
order.rs

1//! Document order over the chart IR.
2//!
3//! The IR keeps every ordered collection (children, transitions, targets, executable content
4//! blocks) in source order, so document order is a pre-order walk of the tree. The walk visits a
5//! state, then its `<initial>` transition, its `onentry` and `onexit` blocks, its transitions
6//! (each followed by its content), its invokes' `finalize` blocks, and finally its child states.
7//! Positions are the indices of the returned sequence.
8//!
9//! States alone keep the same relative order in the compiled chart, whose dense state indices are
10//! document positions restricted to states.
11
12use alloc::vec::Vec;
13
14use crate::ir::{Block, Chart, Exec, ExecKind, State, Transition};
15
16/// A node visited by the document-order walk.
17#[derive(Clone, Copy, Debug)]
18pub enum DocNode<'a> {
19    /// A state of any kind.
20    State(&'a State),
21    /// A transition declared on a state, with its index in the state's transition list.
22    Transition {
23        /// Identifier of the source state.
24        source: &'a str,
25        /// Index in `State::transitions`.
26        index: usize,
27        /// The transition.
28        transition: &'a Transition,
29    },
30    /// The transition of a state's `<initial>` element.
31    InitialTransition {
32        /// Identifier of the owning compound state.
33        state: &'a str,
34        /// The transition.
35        transition: &'a Transition,
36    },
37    /// One executable content element.
38    Exec(&'a Exec),
39}
40
41/// Every state, transition and executable content element of `chart` in document order; the
42/// position of a node is its index in the result.
43// @tg: implements:: [[openspec:chart-model#Document Order]]
44pub fn document_order(chart: &Chart) -> Vec<DocNode<'_>> {
45    let mut out = Vec::new();
46    for s in &chart.states {
47        walk_state(s, &mut out);
48    }
49    out
50}
51
52/// The document position of every state, in the same pre-order as [`document_order`]. The
53/// result lists state identifiers; a state's position is its index.
54pub fn state_order(chart: &Chart) -> Vec<&str> {
55    let mut out = Vec::new();
56    fn go<'a>(s: &'a State, out: &mut Vec<&'a str>) {
57        out.push(s.id.as_str());
58        for c in &s.children {
59            go(c, out);
60        }
61    }
62    for s in &chart.states {
63        go(s, &mut out);
64    }
65    out
66}
67
68fn walk_state<'a>(s: &'a State, out: &mut Vec<DocNode<'a>>) {
69    out.push(DocNode::State(s));
70    if let Some(init) = &s.initial_element {
71        out.push(DocNode::InitialTransition {
72            state: s.id.as_str(),
73            transition: &init.transition,
74        });
75        walk_block(&init.transition.body, out);
76    }
77    for b in &s.onentry {
78        walk_block(b, out);
79    }
80    for b in &s.onexit {
81        walk_block(b, out);
82    }
83    for (index, t) in s.transitions.iter().enumerate() {
84        out.push(DocNode::Transition {
85            source: s.id.as_str(),
86            index,
87            transition: t,
88        });
89        walk_block(&t.body, out);
90    }
91    for inv in &s.invokes {
92        walk_block(&inv.finalize, out);
93    }
94    for c in &s.children {
95        walk_state(c, out);
96    }
97}
98
99fn walk_block<'a>(b: &'a Block, out: &mut Vec<DocNode<'a>>) {
100    for e in b {
101        out.push(DocNode::Exec(e));
102        match &e.kind {
103            ExecKind::If { branches } => {
104                for br in branches {
105                    walk_block(&br.body, out);
106                }
107            }
108            ExecKind::Foreach { body, .. } => walk_block(body, out),
109            _ => {}
110        }
111    }
112}
113
114/// Structural chart equality: every node, attribute, expression text, extension node and
115/// document order is compared, source locations are not (a [`crate::ir::Span`] equals every
116/// other span). Two charts are equal exactly when every surface serializes them identically.
117// @tg: implements:: [[openspec:chart-model#Chart Equality]]
118pub fn same_chart(a: &Chart, b: &Chart) -> bool {
119    a == b
120}