1use std::cell::RefCell;
20use std::collections::HashMap;
21use std::io;
22use std::rc::Rc;
23
24use async_channel::{Receiver, Sender, TryRecvError};
25use async_trait::async_trait;
26use endbasic_core::{
27 CallError, Callable, CallableMetadata, Compiler, CompilerError, GlobalDef, Image, LineCol,
28 StopReason, SymbolKey, Vm,
29};
30
31pub mod arrays;
33pub mod console;
34pub mod data;
35pub mod exec;
36pub mod gfx;
37pub mod gpio;
38pub mod help;
39pub mod numerics;
40pub mod program;
41pub mod spi;
42pub mod storage;
43pub mod strings;
44pub mod testutils;
45
46#[derive(Debug, thiserror::Error)]
48pub enum Error {
49 #[error("{0}")]
51 CallError(#[from] CallError),
52
53 #[error("{0}")]
55 CompilerError(#[from] CompilerError),
56
57 #[error("{0}")]
59 IoError(#[from] io::Error),
60
61 #[error("{0}: {1}")]
63 RuntimeError(LineCol, String),
64
65 #[error("Break")]
67 Break,
68}
69
70pub type Result<T> = std::result::Result<T, Error>;
72
73pub trait Clearable {
75 fn reset_state(&self);
77}
78
79#[derive(Clone, Debug, Eq, PartialEq)]
85pub enum MachineAction {
86 Clear,
88
89 Run(String),
91}
92
93#[derive(Clone, Debug, Eq, PartialEq)]
95pub enum Signal {
96 Break,
98}
99
100#[async_trait(?Send)]
102pub trait Yielder {
103 async fn yield_now(&mut self);
105}
106
107pub struct Machine {
109 compiler: Compiler,
110 image: Image,
111 vm: Vm,
112 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
113 clearables: Vec<Box<dyn Clearable>>,
114 actions: Rc<RefCell<Vec<MachineAction>>>,
115 global_defs: Vec<GlobalDef>,
116 console: Rc<RefCell<dyn console::Console>>,
117 yielder: Option<Box<dyn Yielder>>,
118 signals_chan: (Sender<Signal>, Receiver<Signal>),
119}
120
121impl Machine {
122 pub fn clear(&mut self) {
128 for clearable in self.clearables.as_slice() {
129 clearable.reset_state();
130 }
131 self.vm.reset();
132 self.compiler = Compiler::new(&self.callables, &self.global_defs)
133 .expect("Compiler creation succeeded during Machine init; must also succeed here");
134 self.image = Image::default();
135 }
136
137 fn run(&mut self, program: String) -> Result<()> {
138 self.clear();
139 self.compile(&mut program.as_bytes())
140 }
141
142 fn clear_actions(&mut self) {
144 self.actions.borrow_mut().clear();
145 }
146
147 fn drain_actions(&mut self) -> Result<bool> {
151 let actions: Vec<MachineAction> = self.actions.borrow_mut().drain(..).collect();
152 let mut running_stored_program = false;
153 for action in actions {
154 match action {
155 MachineAction::Clear => self.clear(),
156 MachineAction::Run(program) => {
157 self.run(program)?;
158 running_stored_program = true;
159 }
160 }
161 }
162 Ok(running_stored_program)
163 }
164
165 pub fn drain_signals(&mut self) {
167 while self.signals_chan.1.try_recv().is_ok() {
168 }
170 }
171
172 fn should_stop(&mut self) -> bool {
174 match self.signals_chan.1.try_recv() {
175 Ok(Signal::Break) => true,
176 Err(TryRecvError::Empty) => false,
177 Err(TryRecvError::Closed) => false,
178 }
179 }
180
181 async fn should_stop_after_yield(&mut self) -> bool {
183 if let Some(yielder) = self.yielder.as_mut() {
184 yielder.yield_now().await;
185 }
186 self.should_stop()
187 }
188
189 pub fn compile(&mut self, input: &mut dyn io::Read) -> Result<()> {
191 self.compiler.compile_more(&mut self.image, input)?;
192 Ok(())
193 }
194
195 pub async fn exec(&mut self) -> Result<Option<i32>> {
197 let mut running_stored_program = false;
198 let result = loop {
199 match self.vm.exec(&self.image) {
200 StopReason::Eof => {
201 break Ok(None);
202 }
203
204 StopReason::End(code) => {
205 if !running_stored_program {
206 break Ok(Some(code.to_i32()));
207 }
208
209 if !code.is_success() {
210 self.console
211 .borrow_mut()
212 .print(&format!("Program exited with code {}", code.to_i32()))?;
213 }
214
215 break Ok(None);
216 }
217
218 StopReason::Exception(pos, msg) => {
219 break Err(Error::RuntimeError(pos, msg));
220 }
221
222 StopReason::UpcallAsync(handler) => {
223 let upcall_result = handler.invoke().await;
224
225 if self.should_stop() {
230 self.clear_actions();
231 self.vm.interrupt(&self.image);
232 break Err(Error::Break);
233 }
234
235 if let Err(e) = upcall_result {
236 self.clear_actions();
237 let (pos, message) = e.parts();
238 break Err(Error::RuntimeError(pos, message));
239 }
240
241 if self.drain_actions()? {
242 running_stored_program = true;
243 }
244 }
245
246 StopReason::Yield => {
247 if self.should_stop_after_yield().await {
248 self.vm.interrupt(&self.image);
249 break Err(Error::Break);
250 }
251 }
252 }
253 };
254 if running_stored_program {
255 self.vm.clear_error_handler();
256 }
257 result
258 }
259}
260
261#[derive(Default)]
265pub struct MachineBuilder {
266 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
267 callables_metadata: Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>>,
268 clearables: Vec<Box<dyn Clearable>>,
269 console: Option<Rc<RefCell<dyn console::Console>>>,
270 gpio_pins: Option<Rc<RefCell<dyn gpio::Pins>>>,
271 sleep_fn: Option<exec::SleepFn>,
272 actions: Rc<RefCell<Vec<MachineAction>>>,
273 yielder: Option<Box<dyn Yielder>>,
274 signals_chan: Option<(Sender<Signal>, Receiver<Signal>)>,
275 global_defs: Vec<GlobalDef>,
276}
277
278impl MachineBuilder {
279 pub fn actions(&self) -> Rc<RefCell<Vec<MachineAction>>> {
283 self.actions.clone()
284 }
285
286 pub fn add_callable(&mut self, callable: Rc<dyn Callable>) {
288 let metadata = callable.metadata();
289 let key = SymbolKey::from(metadata.name());
290
291 let previous = self.callables.insert(key.clone(), callable);
292 debug_assert!(previous.is_none(), "Cannot insert a callable twice");
293
294 let previous = self.callables_metadata.borrow_mut().insert(key, metadata);
295 debug_assert!(previous.is_none(), "Cannot insert callable metadata twice");
296 }
297
298 pub fn callables_metadata(&self) -> Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>> {
300 self.callables_metadata.clone()
301 }
302
303 pub fn add_clearable(&mut self, clearable: Box<dyn Clearable>) {
310 self.clearables.push(clearable);
311 }
312
313 pub fn with_console(mut self, console: Rc<RefCell<dyn console::Console>>) -> Self {
315 self.console = Some(console);
316 self
317 }
318
319 pub fn with_globals(mut self, defs: Vec<GlobalDef>) -> Self {
321 self.global_defs.extend(defs);
322 self
323 }
324
325 pub fn with_gpio_pins(mut self, pins: Rc<RefCell<dyn gpio::Pins>>) -> Self {
327 self.gpio_pins = Some(pins);
328 self
329 }
330
331 pub fn with_sleep_fn(mut self, sleep_fn: exec::SleepFn) -> Self {
333 self.sleep_fn = Some(sleep_fn);
334 self
335 }
336
337 pub fn with_yielder(mut self, yielder: Box<dyn Yielder>) -> Self {
339 self.yielder = Some(yielder);
340 self
341 }
342
343 pub fn with_signals_chan(mut self, chan: (Sender<Signal>, Receiver<Signal>)) -> Self {
345 self.signals_chan = Some(chan);
346 self
347 }
348
349 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
351 if self.console.is_none() {
352 self.console = Some(Rc::from(RefCell::from(console::TrivialConsole::default())));
353 }
354 self.console.clone().unwrap()
355 }
356
357 fn get_gpio_pins(&mut self) -> Rc<RefCell<dyn gpio::Pins>> {
359 if self.gpio_pins.is_none() {
360 self.gpio_pins = Some(Rc::from(RefCell::from(gpio::NoopPins::default())))
361 }
362 self.gpio_pins.as_ref().expect("Must have been initialized above").clone()
363 }
364
365 pub fn build(mut self) -> Machine {
367 let console = self.get_console();
368 let gpio_pins = self.get_gpio_pins();
369
370 let signals_chan = match self.signals_chan.take() {
371 Some(pair) => pair,
372 None => async_channel::unbounded(),
373 };
374
375 arrays::add_all(&mut self);
376 console::add_all(&mut self, console.clone());
377 gfx::add_all(&mut self, console.clone());
378 data::add_all(&mut self);
379 gpio::add_all(&mut self, gpio_pins);
380 let sleep_fn = self.sleep_fn.take();
381 exec::add_scripting(&mut self, sleep_fn);
382 numerics::add_all(&mut self);
383 strings::add_all(&mut self);
384
385 Machine {
386 compiler: Compiler::new(&self.callables, &self.global_defs)
387 .expect("Injected globals must be valid"),
388 image: Image::default(),
389 vm: Vm::new(self.callables.clone()),
390 callables: self.callables,
391 clearables: self.clearables,
392 actions: self.actions.clone(),
393 global_defs: self.global_defs.clone(),
394 console,
395 yielder: self.yielder.take(),
396 signals_chan,
397 }
398 }
399
400 pub fn make_interactive(self) -> InteractiveMachineBuilder {
402 InteractiveMachineBuilder::from(self)
403 }
404}
405
406pub struct InteractiveMachineBuilder {
413 builder: MachineBuilder,
414 program: Option<Rc<RefCell<dyn program::Program>>>,
415 storage: Option<Rc<RefCell<storage::Storage>>>,
416}
417
418impl InteractiveMachineBuilder {
419 fn from(builder: MachineBuilder) -> Self {
421 InteractiveMachineBuilder { builder, program: None, storage: None }
422 }
423
424 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
426 self.builder.get_console()
427 }
428
429 pub fn get_program(&mut self) -> Rc<RefCell<dyn program::Program>> {
431 if self.program.is_none() {
432 self.program = Some(Rc::from(RefCell::from(program::ImmutableProgram::default())));
433 }
434 self.program.clone().unwrap()
435 }
436
437 pub fn get_storage(&mut self) -> Rc<RefCell<storage::Storage>> {
439 if self.storage.is_none() {
440 self.storage = Some(Rc::from(RefCell::from(storage::Storage::default())));
441 }
442 self.storage.clone().unwrap()
443 }
444
445 pub fn with_program(mut self, program: Rc<RefCell<dyn program::Program>>) -> Self {
447 self.program = Some(program);
448 self
449 }
450
451 pub fn with_storage(mut self, storage: Rc<RefCell<storage::Storage>>) -> Self {
453 self.storage = Some(storage);
454 self
455 }
456
457 pub fn build(mut self) -> Machine {
459 let console = self.builder.get_console();
460 let program = self.get_program();
461 let storage = self.get_storage();
462
463 exec::add_interactive(&mut self.builder);
464 program::add_all(&mut self.builder, program, console.clone(), storage.clone());
465 storage::add_all(&mut self.builder, console.clone(), storage);
466 help::add_all(&mut self.builder, console);
467
468 self.builder.build()
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475
476 #[test]
477 fn test_should_stop_with_closed_channel() {
478 let (signals_tx, signals_rx) = async_channel::unbounded();
479 let mut machine =
480 MachineBuilder::default().with_signals_chan((signals_tx, signals_rx)).build();
481
482 machine.signals_chan.0.close();
483 assert!(!machine.should_stop());
484 }
485}