Skip to main content

rstm_core/programs/impls/
imp_program_base.rs

1/*
2    Appellation: impl_program <module>
3    Created At: 2026.01.11:12:33:32
4    Contrib: @FL03
5*/
6use crate::programs::{ProgramBase, RawRuleset, Ruleset};
7use crate::rules::{Head, Instruction, Tail};
8use rstm_state::{IntoState, RawState, State};
9
10impl<R, I, Q, A> ProgramBase<R, Q, A, I>
11where
12    Q: RawState,
13    R: RawRuleset<Q, A, Rule = I>,
14    I: Instruction<Q, A>,
15{
16    /// initialize a new program from the given rule set
17    pub const fn from_rules(rules: R) -> Self {
18        Self {
19            rules,
20            initial_state: None,
21            _marker: core::marker::PhantomData,
22        }
23    }
24    #[cfg(all(feature = "json", feature = "std"))]
25    /// load a program from a `.json` file at the given path
26    pub fn load_from_json<P: AsRef<std::path::Path>>(path: P) -> crate::Result<Self>
27    where
28        Self: serde::de::DeserializeOwned,
29    {
30        // open the file
31        let file = std::fs::File::open(path)?;
32        // create a buffered reader
33        let reader = std::io::BufReader::new(file);
34        // deserialize the program
35        let p = serde_json::from_reader(reader)?;
36        Ok(p)
37    }
38    /// returns a reference to the ruleset
39    pub const fn rules(&self) -> &R {
40        &self.rules
41    }
42    #[allow(dead_code)]
43    /// returns a mutable reference to the ruleset
44    pub(crate) const fn rules_mut(&mut self) -> &mut R {
45        &mut self.rules
46    }
47    /// returns reference to the (optional) initial state
48    pub fn initial_state(&self) -> Option<&State<Q>> {
49        self.initial_state.as_ref()
50    }
51    /// configure the initial state
52    pub fn set_initial_state(&mut self, initial_state: Q) {
53        self.initial_state = Some(State(initial_state));
54    }
55    #[inline]
56    /// consumes the instance to create another with the given initial state
57    pub fn with_default_state<U>(self, initial_state: U) -> Self
58    where
59        U: IntoState<Q>,
60    {
61        Self {
62            initial_state: Some(initial_state.into_state()),
63            ..self
64        }
65    }
66    #[cfg(feature = "serde_json")]
67    /// serializes the current instance into a JSON string
68    pub fn to_json(&self) -> serde_json::Value
69    where
70        Self: serde::Serialize,
71    {
72        serde_json::to_value(self).expect("Failed to serialize the Program instance")
73    }
74    #[cfg(all(feature = "json", feature = "std"))]
75    /// export the program to a `.json` file at the given path
76    ///
77    /// **note**: there are no checks to see if the file already exists; it will automatically
78    /// be overwritten.
79    pub fn export_json<P>(&self, path: P) -> std::io::Result<()>
80    where
81        P: AsRef<std::path::Path>,
82        Self: serde::Serialize,
83    {
84        let path = path.as_ref();
85        // ensure the filename ends with `.json`
86        if path.extension().map(|os| os.to_str()).flatten() != Some("json") {
87            #[cfg(feature = "tracing")]
88            tracing::error!(
89                "the provided path does not end with `.json`; consider changing the file extension"
90            );
91            return Err(std::io::Error::new(
92                std::io::ErrorKind::InvalidInput,
93                "the provided path does not end with `.json`",
94            ));
95        }
96        let serialized = serde_json::to_string_pretty(self).unwrap();
97        std::fs::write(path, serialized)?;
98        #[cfg(feature = "tracing")]
99        tracing::info!("Program exported as JSON");
100        Ok(())
101    }
102
103    pub fn get_head(&self, head: &Head<Q, A>) -> Option<&Tail<Q, A>>
104    where
105        R: Ruleset<Q, A>,
106        R::Rule: Instruction<Q, A, Head = Head<Q, A>, Tail = Tail<Q, A>>,
107    {
108        self.rules().get(head)
109    }
110    /// given a state and symbol, returns the corresponding tail if it exists within the 
111    /// ruleset
112    pub fn find_tail(&self, state: State<&Q>, sym: &A) -> Option<&Tail<Q, A>>
113    where
114        R: Ruleset<Q, A>,
115        R::Rule: Instruction<Q, A, Head = Head<Q, A>, Tail = Tail<Q, A>>,
116    {
117        self.rules().find_tail(state, sym)
118    }
119    /// returns the number of rules within the ruleset
120    pub fn len(&self) -> usize
121    {
122        self.rules().len()
123    }
124    /// returns true if the ruleset is considered empty (i.e. contains no rules), 
125    /// otherwise false.
126    pub fn is_empty(&self) -> bool
127    {
128        self.rules().is_empty()
129    }
130}