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 datetime;
36pub mod exec;
37pub mod gfx;
38pub mod gpio;
39pub mod help;
40pub mod numerics;
41pub mod program;
42pub mod sound;
43pub mod spi;
44pub mod storage;
45pub mod strings;
46pub mod testutils;
47
48#[derive(Debug, thiserror::Error)]
50pub enum Error {
51 #[error("{0}")]
53 CallError(#[from] CallError),
54
55 #[error("{0}")]
57 CompilerError(#[from] CompilerError),
58
59 #[error("{0}")]
61 IoError(#[from] io::Error),
62
63 #[error("{0}: {1}")]
65 RuntimeError(LineCol, String),
66
67 #[error("Break")]
69 Break,
70}
71
72pub type Result<T> = std::result::Result<T, Error>;
74
75pub trait Clearable {
77 fn reset_state(&self);
79}
80
81#[derive(Clone, Debug, Eq, PartialEq)]
87pub enum MachineAction {
88 Clear,
90
91 Run(String),
93}
94
95#[derive(Clone, Debug, Eq, PartialEq)]
97pub enum Signal {
98 Break,
100}
101
102#[async_trait(?Send)]
104pub trait Yielder {
105 async fn yield_now(&mut self);
107}
108
109pub struct Machine {
111 compiler: Compiler,
112 image: Image,
113 vm: Vm,
114 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
115 clearables: Vec<Box<dyn Clearable>>,
116 actions: Rc<RefCell<Vec<MachineAction>>>,
117 global_defs: Vec<GlobalDef>,
118 console: Rc<RefCell<dyn console::Console>>,
119 yielder: Option<Rc<RefCell<dyn Yielder>>>,
120 signals_chan: (Sender<Signal>, Receiver<Signal>),
121}
122
123impl Machine {
124 pub fn clear(&mut self) {
130 for clearable in self.clearables.as_slice() {
131 clearable.reset_state();
132 }
133 self.vm.reset();
134 self.compiler = Compiler::new(&self.callables, &self.global_defs)
135 .expect("Compiler creation succeeded during Machine init; must also succeed here");
136 self.image = Image::default();
137 }
138
139 fn run(&mut self, program: String) -> Result<()> {
140 self.clear();
141 self.compile(&mut program.as_bytes())
142 }
143
144 fn clear_actions(&mut self) {
146 self.actions.borrow_mut().clear();
147 }
148
149 fn drain_actions(&mut self) -> Result<bool> {
153 let actions: Vec<MachineAction> = self.actions.borrow_mut().drain(..).collect();
154 let mut running_stored_program = false;
155 for action in actions {
156 match action {
157 MachineAction::Clear => self.clear(),
158 MachineAction::Run(program) => {
159 self.run(program)?;
160 running_stored_program = true;
161 }
162 }
163 }
164 Ok(running_stored_program)
165 }
166
167 pub fn drain_signals(&mut self) {
169 while self.signals_chan.1.try_recv().is_ok() {
170 }
172 }
173
174 fn should_stop(&mut self) -> bool {
176 match self.signals_chan.1.try_recv() {
177 Ok(Signal::Break) => true,
178 Err(TryRecvError::Empty) => false,
179 Err(TryRecvError::Closed) => false,
180 }
181 }
182
183 async fn should_stop_after_yield(&mut self) -> bool {
185 if let Some(yielder) = self.yielder.as_ref() {
186 let mut yielder = yielder.borrow_mut();
187 yielder.yield_now().await;
188 }
189 self.should_stop()
190 }
191
192 pub fn compile(&mut self, input: &mut dyn io::Read) -> Result<()> {
194 self.compiler.compile_more(&mut self.image, input)?;
195 Ok(())
196 }
197
198 pub async fn exec(&mut self) -> Result<Option<i32>> {
200 let mut running_stored_program = false;
201 let result = loop {
202 match self.vm.exec(&self.image) {
203 StopReason::Eof => {
204 break Ok(None);
205 }
206
207 StopReason::End(code) => {
208 if !running_stored_program {
209 break Ok(Some(code.to_i32()));
210 }
211
212 if !code.is_success() {
213 self.console
214 .borrow_mut()
215 .print(&format!("Program exited with code {}", code.to_i32()))?;
216 }
217
218 break Ok(None);
219 }
220
221 StopReason::Exception(pos, msg) => {
222 break Err(Error::RuntimeError(pos, msg));
223 }
224
225 StopReason::UpcallAsync(handler) => {
226 let upcall_result = handler.invoke().await;
227
228 if self.should_stop() {
233 self.clear_actions();
234 self.vm.interrupt(&self.image);
235 break Err(Error::Break);
236 }
237
238 if let Err(e) = upcall_result {
239 self.clear_actions();
240 let (pos, message) = e.parts();
241 break Err(Error::RuntimeError(pos, message));
242 }
243
244 if self.drain_actions()? {
245 running_stored_program = true;
246 }
247 }
248
249 StopReason::Yield => {
250 if self.should_stop_after_yield().await {
251 self.vm.interrupt(&self.image);
252 break Err(Error::Break);
253 }
254 }
255 }
256 };
257 if running_stored_program {
258 self.vm.clear_error_handler();
259 }
260 result
261 }
262}
263
264#[derive(Default)]
268pub struct MachineBuilder {
269 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
270 callables_metadata: Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>>,
271 clearables: Vec<Box<dyn Clearable>>,
272 console: Option<Rc<RefCell<dyn console::Console>>>,
273 datetime: Option<Rc<dyn datetime::DateTime>>,
274 gpio_pins: Option<Rc<RefCell<dyn gpio::Pins>>>,
275 actions: Rc<RefCell<Vec<MachineAction>>>,
276 yielder: Option<Rc<RefCell<dyn Yielder>>>,
277 signals_chan: Option<(Sender<Signal>, Receiver<Signal>)>,
278 global_defs: Vec<GlobalDef>,
279}
280
281impl MachineBuilder {
282 pub fn actions(&self) -> Rc<RefCell<Vec<MachineAction>>> {
286 self.actions.clone()
287 }
288
289 pub fn add_callable(&mut self, callable: Rc<dyn Callable>) {
291 let metadata = callable.metadata();
292 let key = SymbolKey::from(metadata.name());
293
294 let previous = self.callables.insert(key.clone(), callable);
295 debug_assert!(previous.is_none(), "Cannot insert a callable twice");
296
297 let previous = self.callables_metadata.borrow_mut().insert(key, metadata);
298 debug_assert!(previous.is_none(), "Cannot insert callable metadata twice");
299 }
300
301 pub fn callables_metadata(&self) -> Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>> {
303 self.callables_metadata.clone()
304 }
305
306 pub fn add_clearable(&mut self, clearable: Box<dyn Clearable>) {
313 self.clearables.push(clearable);
314 }
315
316 pub fn yielder(&self) -> Option<Rc<RefCell<dyn Yielder>>> {
318 self.yielder.clone()
319 }
320
321 pub fn with_console(mut self, console: Rc<RefCell<dyn console::Console>>) -> Self {
323 self.console = Some(console);
324 self
325 }
326
327 pub fn with_datetime(mut self, datetime: Rc<dyn datetime::DateTime>) -> Self {
329 self.datetime = Some(datetime);
330 self
331 }
332
333 pub fn with_globals(mut self, defs: Vec<GlobalDef>) -> Self {
335 self.global_defs.extend(defs);
336 self
337 }
338
339 pub fn with_gpio_pins(mut self, pins: Rc<RefCell<dyn gpio::Pins>>) -> Self {
341 self.gpio_pins = Some(pins);
342 self
343 }
344
345 pub fn with_yielder(mut self, yielder: Rc<RefCell<dyn Yielder>>) -> Self {
347 self.yielder = Some(yielder);
348 self
349 }
350
351 pub fn with_signals_chan(mut self, chan: (Sender<Signal>, Receiver<Signal>)) -> Self {
353 self.signals_chan = Some(chan);
354 self
355 }
356
357 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
359 if self.console.is_none() {
360 self.console = Some(Rc::from(RefCell::from(console::TrivialConsole::default())));
361 }
362 self.console.clone().unwrap()
363 }
364
365 pub fn get_datetime(&mut self) -> Rc<dyn datetime::DateTime> {
367 if self.datetime.is_none() {
368 self.datetime = Some(Rc::from(datetime::SystemDateTime::default()));
369 }
370 self.datetime.clone().unwrap()
371 }
372
373 fn get_gpio_pins(&mut self) -> Rc<RefCell<dyn gpio::Pins>> {
375 if self.gpio_pins.is_none() {
376 self.gpio_pins = Some(Rc::from(RefCell::from(gpio::NoopPins::default())))
377 }
378 self.gpio_pins.as_ref().expect("Must have been initialized above").clone()
379 }
380
381 pub fn build(mut self) -> Machine {
383 let console = self.get_console();
384 let datetime = self.get_datetime();
385 let gpio_pins = self.get_gpio_pins();
386
387 let signals_chan = match self.signals_chan.take() {
388 Some(pair) => pair,
389 None => async_channel::unbounded(),
390 };
391
392 arrays::add_all(&mut self);
393 console::add_all(&mut self, console.clone());
394 gfx::add_all(&mut self, console.clone());
395 data::add_all(&mut self);
396 datetime::add_all(&mut self, datetime);
397 gpio::add_all(&mut self, gpio_pins);
398 exec::add_scripting(&mut self);
399 numerics::add_all(&mut self);
400 sound::cmds::add_all(&mut self, console.clone());
401 strings::add_all(&mut self);
402
403 Machine {
404 compiler: Compiler::new(&self.callables, &self.global_defs)
405 .expect("Injected globals must be valid"),
406 image: Image::default(),
407 vm: Vm::new(self.callables.clone()),
408 callables: self.callables,
409 clearables: self.clearables,
410 actions: self.actions.clone(),
411 global_defs: self.global_defs.clone(),
412 console,
413 yielder: self.yielder.take(),
414 signals_chan,
415 }
416 }
417
418 pub fn make_interactive(self) -> InteractiveMachineBuilder {
420 InteractiveMachineBuilder::from(self)
421 }
422}
423
424pub struct InteractiveMachineBuilder {
431 builder: MachineBuilder,
432 program: Option<Rc<RefCell<dyn program::Program>>>,
433 storage: Option<Rc<RefCell<storage::Storage>>>,
434}
435
436impl InteractiveMachineBuilder {
437 fn from(builder: MachineBuilder) -> Self {
439 InteractiveMachineBuilder { builder, program: None, storage: None }
440 }
441
442 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
444 self.builder.get_console()
445 }
446
447 pub fn get_program(&mut self) -> Rc<RefCell<dyn program::Program>> {
449 if self.program.is_none() {
450 self.program = Some(Rc::from(RefCell::from(program::ImmutableProgram::default())));
451 }
452 self.program.clone().unwrap()
453 }
454
455 pub fn get_storage(&mut self) -> Rc<RefCell<storage::Storage>> {
457 if self.storage.is_none() {
458 self.storage = Some(Rc::from(RefCell::from(storage::Storage::default())));
459 }
460 self.storage.clone().unwrap()
461 }
462
463 pub fn with_program(mut self, program: Rc<RefCell<dyn program::Program>>) -> Self {
465 self.program = Some(program);
466 self
467 }
468
469 pub fn with_storage(mut self, storage: Rc<RefCell<storage::Storage>>) -> Self {
471 self.storage = Some(storage);
472 self
473 }
474
475 pub fn build(mut self) -> Machine {
477 let console = self.builder.get_console();
478 let program = self.get_program();
479 let storage = self.get_storage();
480
481 exec::add_interactive(&mut self.builder);
482 let yielder = self.builder.yielder();
483
484 program::add_all(
485 &mut self.builder,
486 program,
487 console.clone(),
488 storage.clone(),
489 yielder.clone(),
490 );
491 storage::add_all(&mut self.builder, console.clone(), storage, yielder.clone());
492 help::add_all(&mut self.builder, console, yielder);
493
494 self.builder.build()
495 }
496}
497
498#[cfg(test)]
499mod tests {
500 use super::*;
501
502 #[test]
503 fn test_should_stop_with_closed_channel() {
504 let (signals_tx, signals_rx) = async_channel::unbounded();
505 let mut machine =
506 MachineBuilder::default().with_signals_chan((signals_tx, signals_rx)).build();
507
508 machine.signals_chan.0.close();
509 assert!(!machine.should_stop());
510 }
511}