bubbles/runtime/runner/
mod.rs1pub(super) mod evaluation;
4pub(super) mod execute;
5pub(super) mod node_body;
6mod session;
7
8use std::borrow::Cow;
9use std::collections::{HashMap, HashSet, VecDeque};
10use std::sync::Arc;
11
12use crate::compiler::Program;
13use crate::compiler::ast::StmtList;
14use crate::error::{DialogueError, Result};
15use crate::library::FunctionLibrary;
16use crate::runtime::event::DialogueEvent;
17use crate::runtime::provider::{LineProvider, PassthroughProvider};
18use crate::saliency::{FirstAvailable, SaliencyStrategy};
19use crate::value::{Value, VariableStorage};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum RunnerPhase {
24 Idle,
26 Running,
28 AwaitingOption,
31 Done,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub(super) enum State {
38 Idle,
39 Running,
40 AwaitingOption,
41 Done,
42}
43
44#[derive(Debug, Clone)]
51pub(super) struct Frame {
52 pub(super) node: Arc<str>,
53 pub(super) body: StmtList,
54 pub(super) ip: usize,
55}
56
57impl Frame {
58 pub(super) const fn new(node: Arc<str>, body: StmtList) -> Self {
59 Self { node, body, ip: 0 }
60 }
61}
62
63type OptionBodies = Vec<(bool, StmtList)>;
65
66pub struct Runner<S: VariableStorage> {
73 pub(super) program: Program,
74 pub(super) storage: S,
75 pub(super) state: State,
76 pub(super) stack: Vec<Frame>,
77 pub(super) pending: VecDeque<DialogueEvent>,
78 pub(super) option_bodies: OptionBodies,
79 pub(super) library: FunctionLibrary,
80 pub(super) visits: HashMap<String, u32>,
81 pub(super) once_seen: HashSet<String>,
82 pub(super) saliency: Box<dyn SaliencyStrategy>,
83 pub(super) provider: Box<dyn LineProvider>,
84}
85
86impl<S: VariableStorage> Runner<S> {
87 fn clear_event_queues(&mut self) {
88 self.pending.clear();
89 self.option_bodies.clear();
90 }
91
92 #[must_use]
94 pub const fn program(&self) -> &Program {
95 &self.program
96 }
97
98 #[must_use]
100 pub const fn phase(&self) -> RunnerPhase {
101 match self.state {
102 State::Idle => RunnerPhase::Idle,
103 State::Running => RunnerPhase::Running,
104 State::AwaitingOption => RunnerPhase::AwaitingOption,
105 State::Done => RunnerPhase::Done,
106 }
107 }
108
109 #[must_use]
111 pub fn new(program: Program, storage: S) -> Self {
112 Self {
113 program,
114 storage,
115 state: State::Idle,
116 stack: Vec::new(),
117 pending: VecDeque::new(),
118 option_bodies: Vec::new(),
119 library: FunctionLibrary::new(),
120 visits: HashMap::new(),
121 once_seen: HashSet::new(),
122 saliency: Box::new(FirstAvailable),
123 provider: Box::new(PassthroughProvider),
124 }
125 }
126
127 pub fn start(&mut self, node: &str) -> Result<()> {
136 if !self.program.node_exists(node) {
137 return Err(DialogueError::UnknownNode(node.to_owned()));
138 }
139 let body = self.pick_node_body(node)?;
140 self.clear_event_queues();
141 let title: Arc<str> = Arc::from(node);
142 self.stack.clear();
143 self.stack.push(Frame::new(title, body));
144 self.state = State::Running;
145 *self.visits.entry(node.to_owned()).or_insert(0) += 1;
146 self.pending
147 .push_back(DialogueEvent::NodeStarted(node.to_owned()));
148 Ok(())
149 }
150
151 pub fn next_event(&mut self) -> Result<Option<DialogueEvent>> {
156 if let Some(ev) = self.pending.pop_front() {
157 return Ok(Some(ev));
158 }
159 match self.state {
160 State::Idle | State::Done => Ok(None),
161 State::AwaitingOption => Err(DialogueError::ProtocolViolation(
162 "call select_option() before next_event()".into(),
163 )),
164 State::Running => loop {
165 if let Some(ev) = self.pending.pop_front() {
166 return Ok(Some(ev));
167 }
168 if self.state != State::Running {
169 return Ok(None);
170 }
171 if let Some(ev) = self.step()? {
172 return Ok(Some(ev));
173 }
174 },
175 }
176 }
177
178 pub fn select_option(&mut self, index: usize) -> Result<()> {
185 if self.state != State::AwaitingOption {
186 return Err(DialogueError::ProtocolViolation(
187 "select_option() called when not awaiting an option".into(),
188 ));
189 }
190 let (available, body) = self.option_bodies.get(index).cloned().ok_or_else(|| {
191 DialogueError::ProtocolViolation(format!(
192 "option index {index} out of range ({})",
193 self.option_bodies.len()
194 ))
195 })?;
196 if !available {
197 return Err(DialogueError::ProtocolViolation(format!(
198 "option index {index} is unavailable (guard not satisfied)"
199 )));
200 }
201 self.option_bodies.clear();
202 self.state = State::Running;
203 self.push_inline_frame(body);
204 Ok(())
205 }
206
207 pub(super) fn push_inline_frame(&mut self, body: StmtList) {
212 if body.is_empty() {
213 return;
214 }
215 let title = self
216 .stack
217 .last()
218 .map_or_else(|| Arc::from(""), |f| Arc::clone(&f.node));
219 self.stack.push(Frame::new(title, body));
220 }
221
222 pub(super) fn push_node_frame(&mut self, node: &str, body: StmtList) {
228 self.stack.push(Frame::new(Arc::from(node), body));
229 }
230
231 #[must_use]
233 pub const fn storage(&self) -> &S {
234 &self.storage
235 }
236
237 pub const fn storage_mut(&mut self) -> &mut S {
239 &mut self.storage
240 }
241
242 #[must_use]
248 pub fn all_variables(&self) -> Vec<(String, Value)> {
249 self.storage.all_variables()
250 }
251
252 #[must_use]
254 pub fn variable(&self, name: &str) -> Option<Value> {
255 self.storage.get(name)
256 }
257
258 #[must_use]
260 pub fn variable_ref(&self, name: &str) -> Option<Cow<'_, Value>> {
261 self.storage.get_ref(name)
262 }
263
264 pub const fn library_mut(&mut self) -> &mut FunctionLibrary {
266 &mut self.library
267 }
268
269 pub fn set_saliency(&mut self, strategy: impl SaliencyStrategy) {
271 self.saliency = Box::new(strategy);
272 }
273
274 pub fn set_provider(&mut self, provider: impl LineProvider) {
276 self.provider = Box::new(provider);
277 }
278
279 pub(crate) fn set_saliency_box(&mut self, strategy: Box<dyn SaliencyStrategy>) {
283 self.saliency = strategy;
284 }
285
286 pub(crate) fn set_provider_box(&mut self, provider: Box<dyn LineProvider>) {
290 self.provider = provider;
291 }
292}