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(CallError),
54
55 #[error("{0}")]
57 CompilerError(CompilerError),
58
59 #[error("{0}")]
61 IoError(io::Error),
62
63 #[error("{0}: {1}")]
65 RuntimeError(LineCol, String),
66
67 #[error("Break")]
69 Break,
70}
71
72impl From<CallError> for Error {
73 fn from(value: CallError) -> Self {
74 Self::CallError(value)
75 }
76}
77
78impl From<CompilerError> for Error {
79 fn from(value: CompilerError) -> Self {
80 Self::CompilerError(value)
81 }
82}
83
84impl From<io::Error> for Error {
85 fn from(value: io::Error) -> Self {
86 Self::IoError(value)
87 }
88}
89
90pub type Result<T> = std::result::Result<T, Error>;
92
93pub trait Clearable {
95 fn reset_state(&self);
97}
98
99#[derive(Clone, Debug, Eq, PartialEq)]
105pub enum MachineAction {
106 Clear,
108
109 Run(String),
111}
112
113#[derive(Clone, Debug, Eq, PartialEq)]
115pub enum Signal {
116 Break,
118}
119
120#[async_trait(?Send)]
122pub trait Yielder {
123 async fn yield_now(&mut self);
125}
126
127pub struct Machine {
129 compiler: Compiler,
130 image: Image,
131 vm: Vm,
132 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
133 clearables: Vec<Box<dyn Clearable>>,
134 actions: Rc<RefCell<Vec<MachineAction>>>,
135 global_defs: Vec<GlobalDef>,
136 console: Rc<RefCell<dyn console::Console>>,
137 yielder: Option<Rc<RefCell<dyn Yielder>>>,
138 signals_chan: (Sender<Signal>, Receiver<Signal>),
139}
140
141impl Machine {
142 pub fn clear(&mut self) {
148 for clearable in self.clearables.as_slice() {
149 clearable.reset_state();
150 }
151 self.vm.reset();
152 self.compiler = Compiler::new(&self.callables, &self.global_defs)
153 .expect("Compiler creation succeeded during Machine init; must also succeed here");
154 self.image = Image::default();
155 }
156
157 fn run(&mut self, program: String) -> Result<()> {
158 self.clear();
159 self.compile(&mut program.as_bytes())
160 }
161
162 fn clear_actions(&mut self) {
164 self.actions.borrow_mut().clear();
165 }
166
167 fn drain_actions(&mut self) -> Result<bool> {
171 let actions: Vec<MachineAction> = self.actions.borrow_mut().drain(..).collect();
172 let mut running_stored_program = false;
173 for action in actions {
174 match action {
175 MachineAction::Clear => self.clear(),
176 MachineAction::Run(program) => {
177 self.run(program)?;
178 running_stored_program = true;
179 }
180 }
181 }
182 Ok(running_stored_program)
183 }
184
185 pub fn drain_signals(&mut self) {
187 while self.signals_chan.1.try_recv().is_ok() {
188 }
190 }
191
192 fn should_stop(&mut self) -> bool {
194 match self.signals_chan.1.try_recv() {
195 Ok(Signal::Break) => true,
196 Err(TryRecvError::Empty) => false,
197 Err(TryRecvError::Closed) => false,
198 }
199 }
200
201 async fn should_stop_after_yield(&mut self) -> bool {
203 if let Some(yielder) = self.yielder.as_ref() {
204 let mut yielder = yielder.borrow_mut();
205 yielder.yield_now().await;
206 }
207 self.should_stop()
208 }
209
210 pub fn compile(&mut self, input: &mut dyn io::Read) -> Result<()> {
212 self.compiler.compile_more(&mut self.image, input)?;
213 Ok(())
214 }
215
216 pub async fn exec(&mut self) -> Result<Option<i32>> {
218 let mut running_stored_program = false;
219 let result = loop {
220 match self.vm.exec(&self.image) {
221 StopReason::Eof => {
222 break Ok(None);
223 }
224
225 StopReason::End(code) => {
226 if !running_stored_program {
227 break Ok(Some(code.to_i32()));
228 }
229
230 if !code.is_success() {
231 self.console
232 .borrow_mut()
233 .print(&format!("Program exited with code {}", code.to_i32()))?;
234 }
235
236 break Ok(None);
237 }
238
239 StopReason::Exception(pos, msg) => {
240 break Err(Error::RuntimeError(pos, msg));
241 }
242
243 StopReason::UpcallAsync(handler) => {
244 let upcall_result = handler.invoke().await;
245
246 if self.should_stop() {
251 self.clear_actions();
252 self.vm.interrupt(&self.image);
253 break Err(Error::Break);
254 }
255
256 if let Err(e) = upcall_result {
257 self.clear_actions();
258 let (pos, message) = e.parts();
259 break Err(Error::RuntimeError(pos, message));
260 }
261
262 if self.drain_actions()? {
263 running_stored_program = true;
264 }
265 }
266
267 StopReason::Yield => {
268 if self.should_stop_after_yield().await {
269 self.vm.interrupt(&self.image);
270 break Err(Error::Break);
271 }
272 }
273 }
274 };
275 if running_stored_program {
276 self.vm.clear_error_handler();
277 }
278 result
279 }
280}
281
282#[derive(Default)]
286pub struct MachineBuilder {
287 callables: HashMap<SymbolKey, Rc<dyn Callable>>,
288 callables_metadata: Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>>,
289 clearables: Vec<Box<dyn Clearable>>,
290 console: Option<Rc<RefCell<dyn console::Console>>>,
291 datetime: Option<Rc<dyn datetime::DateTime>>,
292 gpio_pins: Option<Rc<RefCell<dyn gpio::Pins>>>,
293 actions: Rc<RefCell<Vec<MachineAction>>>,
294 yielder: Option<Rc<RefCell<dyn Yielder>>>,
295 signals_chan: Option<(Sender<Signal>, Receiver<Signal>)>,
296 global_defs: Vec<GlobalDef>,
297}
298
299impl MachineBuilder {
300 pub fn actions(&self) -> Rc<RefCell<Vec<MachineAction>>> {
304 self.actions.clone()
305 }
306
307 pub fn add_callable(&mut self, callable: Rc<dyn Callable>) {
309 let metadata = callable.metadata();
310 let key = SymbolKey::from(metadata.name());
311
312 let previous = self.callables.insert(key.clone(), callable);
313 debug_assert!(previous.is_none(), "Cannot insert a callable twice");
314
315 let previous = self.callables_metadata.borrow_mut().insert(key, metadata);
316 debug_assert!(previous.is_none(), "Cannot insert callable metadata twice");
317 }
318
319 pub fn callables_metadata(&self) -> Rc<RefCell<HashMap<SymbolKey, Rc<CallableMetadata>>>> {
321 self.callables_metadata.clone()
322 }
323
324 pub fn add_clearable(&mut self, clearable: Box<dyn Clearable>) {
331 self.clearables.push(clearable);
332 }
333
334 pub fn yielder(&self) -> Option<Rc<RefCell<dyn Yielder>>> {
336 self.yielder.clone()
337 }
338
339 pub fn with_console(mut self, console: Rc<RefCell<dyn console::Console>>) -> Self {
341 self.console = Some(console);
342 self
343 }
344
345 pub fn with_datetime(mut self, datetime: Rc<dyn datetime::DateTime>) -> Self {
347 self.datetime = Some(datetime);
348 self
349 }
350
351 pub fn with_globals(mut self, defs: Vec<GlobalDef>) -> Self {
353 self.global_defs.extend(defs);
354 self
355 }
356
357 pub fn with_gpio_pins(mut self, pins: Rc<RefCell<dyn gpio::Pins>>) -> Self {
359 self.gpio_pins = Some(pins);
360 self
361 }
362
363 pub fn with_yielder(mut self, yielder: Rc<RefCell<dyn Yielder>>) -> Self {
365 self.yielder = Some(yielder);
366 self
367 }
368
369 pub fn with_signals_chan(mut self, chan: (Sender<Signal>, Receiver<Signal>)) -> Self {
371 self.signals_chan = Some(chan);
372 self
373 }
374
375 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
377 if self.console.is_none() {
378 self.console = Some(Rc::from(RefCell::from(console::TrivialConsole::default())));
379 }
380 self.console.clone().unwrap()
381 }
382
383 pub fn get_datetime(&mut self) -> Rc<dyn datetime::DateTime> {
385 if self.datetime.is_none() {
386 self.datetime = Some(Rc::from(datetime::SystemDateTime::default()));
387 }
388 self.datetime.clone().unwrap()
389 }
390
391 fn get_gpio_pins(&mut self) -> Rc<RefCell<dyn gpio::Pins>> {
393 if self.gpio_pins.is_none() {
394 self.gpio_pins = Some(Rc::from(RefCell::from(gpio::NoopPins::default())))
395 }
396 self.gpio_pins.as_ref().expect("Must have been initialized above").clone()
397 }
398
399 pub fn build(mut self) -> Machine {
401 let console = self.get_console();
402 let datetime = self.get_datetime();
403 let gpio_pins = self.get_gpio_pins();
404
405 let signals_chan = match self.signals_chan.take() {
406 Some(pair) => pair,
407 None => async_channel::unbounded(),
408 };
409
410 arrays::add_all(&mut self);
411 console::add_all(&mut self, console.clone());
412 gfx::add_all(&mut self, console.clone());
413 data::add_all(&mut self);
414 datetime::add_all(&mut self, datetime);
415 gpio::add_all(&mut self, gpio_pins);
416 exec::add_scripting(&mut self);
417 numerics::add_all(&mut self);
418 sound::cmds::add_all(&mut self, console.clone());
419 strings::add_all(&mut self);
420
421 Machine {
422 compiler: Compiler::new(&self.callables, &self.global_defs)
423 .expect("Injected globals must be valid"),
424 image: Image::default(),
425 vm: Vm::new(self.callables.clone()),
426 callables: self.callables,
427 clearables: self.clearables,
428 actions: self.actions.clone(),
429 global_defs: self.global_defs.clone(),
430 console,
431 yielder: self.yielder.take(),
432 signals_chan,
433 }
434 }
435
436 pub fn make_interactive(self) -> InteractiveMachineBuilder {
438 InteractiveMachineBuilder::from(self)
439 }
440}
441
442pub struct InteractiveMachineBuilder {
449 builder: MachineBuilder,
450 program: Option<Rc<RefCell<dyn program::Program>>>,
451 storage: Option<Rc<RefCell<storage::Storage>>>,
452}
453
454impl InteractiveMachineBuilder {
455 fn from(builder: MachineBuilder) -> Self {
457 InteractiveMachineBuilder { builder, program: None, storage: None }
458 }
459
460 pub fn get_console(&mut self) -> Rc<RefCell<dyn console::Console>> {
462 self.builder.get_console()
463 }
464
465 pub fn get_program(&mut self) -> Rc<RefCell<dyn program::Program>> {
467 if self.program.is_none() {
468 self.program = Some(Rc::from(RefCell::from(program::ImmutableProgram::default())));
469 }
470 self.program.clone().unwrap()
471 }
472
473 pub fn get_storage(&mut self) -> Rc<RefCell<storage::Storage>> {
475 if self.storage.is_none() {
476 self.storage = Some(Rc::from(RefCell::from(storage::Storage::default())));
477 }
478 self.storage.clone().unwrap()
479 }
480
481 pub fn with_program(mut self, program: Rc<RefCell<dyn program::Program>>) -> Self {
483 self.program = Some(program);
484 self
485 }
486
487 pub fn with_storage(mut self, storage: Rc<RefCell<storage::Storage>>) -> Self {
489 self.storage = Some(storage);
490 self
491 }
492
493 pub fn build(mut self) -> Machine {
495 let console = self.builder.get_console();
496 let program = self.get_program();
497 let storage = self.get_storage();
498
499 exec::add_interactive(&mut self.builder);
500 let yielder = self.builder.yielder();
501
502 program::add_all(
503 &mut self.builder,
504 program,
505 console.clone(),
506 storage.clone(),
507 yielder.clone(),
508 );
509 storage::add_all(&mut self.builder, console.clone(), storage, yielder.clone());
510 help::add_all(&mut self.builder, console, yielder);
511
512 self.builder.build()
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 #[test]
521 fn test_error_wrappers_have_no_sources() {
522 let call_error = Error::from(CallError::Eval("Call error".to_owned()));
523 assert!(std::error::Error::source(&call_error).is_none());
524
525 let compiler_error = Error::from(CompilerError::Parse(
526 LineCol { line: 1, col: 2 },
527 "Compiler error".to_owned(),
528 ));
529 assert!(std::error::Error::source(&compiler_error).is_none());
530
531 let io_error = Error::from(io::Error::other("I/O error"));
532 assert!(std::error::Error::source(&io_error).is_none());
533 }
534
535 #[test]
536 fn test_should_stop_with_closed_channel() {
537 let (signals_tx, signals_rx) = async_channel::unbounded();
538 let mut machine =
539 MachineBuilder::default().with_signals_chan((signals_tx, signals_rx)).build();
540
541 machine.signals_chan.0.close();
542 assert!(!machine.should_stop());
543 }
544}