bubbles/runtime/runner/
mod.rs1pub(super) mod evaluation;
4pub(super) mod execute;
5pub(super) mod node_body;
6
7use std::collections::{HashMap, HashSet, VecDeque};
8use std::sync::Arc;
9
10use crate::compiler::Program;
11use crate::compiler::ast::StmtList;
12use crate::error::{DialogueError, Result};
13use crate::library::FunctionLibrary;
14use crate::runtime::event::DialogueEvent;
15use crate::runtime::provider::{LineProvider, PassthroughProvider};
16use crate::saliency::{FirstAvailable, SaliencyStrategy};
17use crate::value::VariableStorage;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum RunnerPhase {
22 Idle,
24 Running,
26 AwaitingOption,
29 Done,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub(super) enum State {
36 Idle,
37 Running,
38 AwaitingOption,
39 Done,
40}
41
42#[derive(Debug, Clone)]
49pub(super) struct Frame {
50 pub(super) node: Arc<str>,
51 pub(super) body: StmtList,
52 pub(super) ip: usize,
53}
54
55impl Frame {
56 pub(super) const fn new(node: Arc<str>, body: StmtList) -> Self {
57 Self { node, body, ip: 0 }
58 }
59}
60
61type OptionBodies = Vec<StmtList>;
63
64pub struct Runner<S: VariableStorage> {
71 pub(super) program: Program,
72 pub(super) storage: S,
73 pub(super) state: State,
74 pub(super) stack: Vec<Frame>,
75 pub(super) pending: VecDeque<DialogueEvent>,
76 pub(super) option_bodies: OptionBodies,
77 pub(super) library: FunctionLibrary,
78 pub(super) visits: HashMap<String, u32>,
79 pub(super) once_seen: HashSet<String>,
80 pub(super) saliency: Box<dyn SaliencyStrategy>,
81 pub(super) provider: Box<dyn LineProvider>,
82}
83
84impl<S: VariableStorage> Runner<S> {
85 fn clear_event_queues(&mut self) {
86 self.pending.clear();
87 self.option_bodies.clear();
88 }
89
90 #[must_use]
92 pub const fn program(&self) -> &Program {
93 &self.program
94 }
95
96 #[must_use]
98 pub const fn phase(&self) -> RunnerPhase {
99 match self.state {
100 State::Idle => RunnerPhase::Idle,
101 State::Running => RunnerPhase::Running,
102 State::AwaitingOption => RunnerPhase::AwaitingOption,
103 State::Done => RunnerPhase::Done,
104 }
105 }
106
107 #[must_use]
109 pub fn new(program: Program, storage: S) -> Self {
110 Self {
111 program,
112 storage,
113 state: State::Idle,
114 stack: Vec::new(),
115 pending: VecDeque::new(),
116 option_bodies: Vec::new(),
117 library: FunctionLibrary::new(),
118 visits: HashMap::new(),
119 once_seen: HashSet::new(),
120 saliency: Box::new(FirstAvailable),
121 provider: Box::new(PassthroughProvider),
122 }
123 }
124
125 pub fn start(&mut self, node: &str) -> Result<()> {
134 if !self.program.node_exists(node) {
135 return Err(DialogueError::UnknownNode(node.to_owned()));
136 }
137 let body = self.pick_node_body(node)?;
138 self.clear_event_queues();
139 let title: Arc<str> = Arc::from(node);
140 self.stack.clear();
141 self.stack.push(Frame::new(title, body));
142 self.state = State::Running;
143 *self.visits.entry(node.to_owned()).or_insert(0) += 1;
144 self.pending
145 .push_back(DialogueEvent::NodeStarted(node.to_owned()));
146 Ok(())
147 }
148
149 pub fn next_event(&mut self) -> Result<Option<DialogueEvent>> {
154 if let Some(ev) = self.pending.pop_front() {
155 return Ok(Some(ev));
156 }
157 match self.state {
158 State::Idle | State::Done => Ok(None),
159 State::AwaitingOption => Err(DialogueError::ProtocolViolation(
160 "call select_option() before next_event()".into(),
161 )),
162 State::Running => loop {
163 if let Some(ev) = self.pending.pop_front() {
164 return Ok(Some(ev));
165 }
166 if self.state != State::Running {
167 return Ok(None);
168 }
169 if let Some(ev) = self.step()? {
170 return Ok(Some(ev));
171 }
172 },
173 }
174 }
175
176 pub fn select_option(&mut self, index: usize) -> Result<()> {
183 if self.state != State::AwaitingOption {
184 return Err(DialogueError::ProtocolViolation(
185 "select_option() called when not awaiting an option".into(),
186 ));
187 }
188 let body = self.option_bodies.get(index).cloned().ok_or_else(|| {
189 DialogueError::ProtocolViolation(format!(
190 "option index {index} out of range ({})",
191 self.option_bodies.len()
192 ))
193 })?;
194 self.option_bodies.clear();
195 self.state = State::Running;
196 self.push_inline_frame(body);
197 Ok(())
198 }
199
200 pub(super) fn push_inline_frame(&mut self, body: StmtList) {
205 if body.is_empty() {
206 return;
207 }
208 let title = self
209 .stack
210 .last()
211 .map_or_else(|| Arc::from(""), |f| Arc::clone(&f.node));
212 self.stack.push(Frame::new(title, body));
213 }
214
215 pub(super) fn push_node_frame(&mut self, node: &str, body: StmtList) {
221 self.stack.push(Frame::new(Arc::from(node), body));
222 }
223
224 #[must_use]
226 pub const fn storage(&self) -> &S {
227 &self.storage
228 }
229
230 pub const fn storage_mut(&mut self) -> &mut S {
232 &mut self.storage
233 }
234
235 pub const fn library_mut(&mut self) -> &mut FunctionLibrary {
237 &mut self.library
238 }
239
240 pub fn set_saliency(&mut self, strategy: impl SaliencyStrategy) {
242 self.saliency = Box::new(strategy);
243 }
244
245 pub fn set_provider(&mut self, provider: impl LineProvider) {
247 self.provider = Box::new(provider);
248 }
249
250 pub(crate) fn set_saliency_box(&mut self, strategy: Box<dyn SaliencyStrategy>) {
254 self.saliency = strategy;
255 }
256
257 pub(crate) fn set_provider_box(&mut self, provider: Box<dyn LineProvider>) {
261 self.provider = provider;
262 }
263
264 #[cfg(feature = "serde")]
277 #[must_use]
278 pub fn snapshot(&self) -> crate::runtime::RunnerSnapshot {
279 crate::runtime::RunnerSnapshot {
280 current_node: self.stack.last().map(|f| f.node.as_ref().to_owned()),
281 visits: self.visits.clone(),
282 once_seen: self.once_seen.clone(),
283 }
284 }
285
286 #[cfg(feature = "serde")]
297 pub fn restore(&mut self, snapshot: crate::runtime::RunnerSnapshot) -> Result<()> {
298 self.visits = snapshot.visits;
299 self.once_seen = snapshot.once_seen;
300 self.stack.clear();
301 self.clear_event_queues();
302 self.state = State::Idle;
303 if let Some(node) = snapshot.current_node {
304 self.start(&node)?;
305 }
306 Ok(())
307 }
308}