1use crate::compat::{Rc, String, Vec, Box, ToString};
2use crate::tokenizer::SourcePos;
3use crate::value::{RuntimeError, Value};
4use crate::output::Output;
5use crate::time_source::TimeSource;
6use num_traits::Zero;
7
8#[cfg(target_os = "none")]
9use num_traits::Float;
10
11#[cfg(not(target_os = "none"))]
12use std::collections::HashMap;
13#[cfg(target_os = "none")]
14use alloc::collections::BTreeMap as HashMap;
15
16#[derive(Debug, Clone)]
19pub struct DictEntry {
20 pub value: Value,
21 pub is_executable: bool, pub doc: Option<Rc<str>>, }
24
25pub struct Interpreter {
26 pub stack: Vec<Value>,
27 pub return_stack: Vec<Value>, pub dictionary: HashMap<Rc<str>, DictEntry>,
29 pub atoms: HashMap<String, Rc<str>>,
30 pub local_frames: Vec<HashMap<Rc<str>, Value>>, pub current_pos: Option<SourcePos>, pending_doc_target: Option<Rc<str>>, output: Option<Box<dyn Output>>, pub time_source: Option<Box<dyn TimeSource>>, #[cfg(feature = "hardware-microbit")]
38 pub buttons: Option<microbit::board::Buttons>,
39 #[cfg(feature = "hardware-microbit")]
40 pub display_buffer: [[u8; 5]; 5], #[cfg(feature = "hardware-pico2")]
44 pub gpio_pins: Option<rp235x_hal::gpio::Pins>,
45}
46
47impl Interpreter {
48 pub fn new() -> Self {
49 let mut interpreter = Self {
50 stack: Vec::new(),
51 return_stack: Vec::new(), dictionary: HashMap::new(),
53 atoms: HashMap::new(),
54 local_frames: Vec::new(), current_pos: None, pending_doc_target: None,
57 output: None, time_source: None, #[cfg(feature = "hardware-microbit")]
62 buttons: None,
63 #[cfg(feature = "hardware-microbit")]
64 display_buffer: [[0u8; 5]; 5], #[cfg(feature = "hardware-pico2")]
67 gpio_pins: None,
68 };
69
70 crate::builtins::register_builtins(&mut interpreter);
73
74 if let Err(_e) = crate::prelude::load_prelude(&mut interpreter) {
76 }
80
81 interpreter
82 }
83
84 pub fn intern_atom(&mut self, text: &str) -> Rc<str> {
85 if let Some(existing) = self.atoms.get(text) {
86 existing.clone()
87 } else {
88 let atom: Rc<str> = text.into();
89 self.atoms.insert(text.to_string(), atom.clone());
90 atom
91 }
92 }
93
94 pub fn set_pending_doc_target(&mut self, atom: Rc<str>) {
95 self.pending_doc_target = Some(atom);
96 }
97
98 pub fn take_pending_doc_target(&mut self) -> Option<Rc<str>> {
99 self.pending_doc_target.take()
100 }
101
102 pub fn attach_doc(&mut self, atom: &Rc<str>, doc: Rc<str>) -> Result<(), RuntimeError> {
103 if let Some(entry) = self.dictionary.get_mut(atom) {
104 entry.doc = Some(doc);
105 Ok(())
106 } else {
107 Err(RuntimeError::UndefinedWord(atom.to_string()))
108 }
109 }
110
111 pub fn push(&mut self, value: Value) {
112 self.stack.push(value);
113 }
114
115 pub fn pop(&mut self) -> Result<Value, RuntimeError> {
116 self.stack.pop().ok_or(RuntimeError::StackUnderflow)
117 }
118
119 pub fn pop_number(&mut self) -> Result<f64, RuntimeError> {
120 let value = self.pop()?;
121 match value {
122 Value::Number(n) => Ok(n),
123 Value::Int32(i) => Ok(i as f64),
124 _ => Err(RuntimeError::TypeError("Expected number".to_string())),
125 }
126 }
127
128 pub fn pop_integer(&mut self) -> Result<usize, RuntimeError> {
129 use num_traits::ToPrimitive;
130 let value = self.pop()?;
131 match value {
132 Value::Int32(i) => {
133 if i >= 0 {
134 Ok(i as usize)
135 } else {
136 Err(RuntimeError::TypeError("Expected non-negative integer".to_string()))
137 }
138 }
139 Value::Integer(i) => i.to_usize().ok_or_else(|| {
140 RuntimeError::TypeError("Integer value too large for index".to_string())
141 }),
142 Value::Number(n) => {
143 if n.fract() == 0.0 && n >= 0.0 && n.is_finite() {
144 Ok(n as usize)
145 } else {
146 Err(RuntimeError::TypeError("Expected non-negative integer".to_string()))
147 }
148 }
149 _ => Err(RuntimeError::TypeError("Expected integer".to_string())),
150 }
151 }
152
153 pub fn make_list(&self, items: Vec<Value>) -> Value {
154 items.into_iter().rev().fold(Value::Nil, |acc, item| {
155 Value::Pair(Rc::new(item), Rc::new(acc))
156 })
157 }
158
159 pub fn make_array(&self, items: Vec<Value>) -> Value {
160 #[cfg(not(target_os = "none"))]
161 {
162 Value::Array(Rc::new(std::cell::RefCell::new(items)))
163 }
164 #[cfg(target_os = "none")]
165 {
166 use core::cell::RefCell;
167 Value::Array(Rc::new(RefCell::new(items)))
168 }
169 }
170
171 pub fn is_null(&self, value: &Value) -> bool {
172 matches!(value, Value::Null)
173 }
174
175 pub fn is_truthy(&self, value: &Value) -> bool {
179 match value {
180 Value::Boolean(false) => false, Value::Null => false, Value::String(s) if s.is_empty() => false, Value::Int32(0) => false,
187 Value::Number(n) if *n == 0.0 || n.is_nan() => false, Value::Integer(i) if i.is_zero() => false,
189 Value::Rational(r) if r.is_zero() => false,
190 #[cfg(feature = "complex_numbers")]
191 Value::GaussianInt(re, im) if re.is_zero() && im.is_zero() => false, #[cfg(feature = "complex_numbers")]
193 Value::Complex(c) if (c.re == 0.0 && c.im == 0.0) || c.re.is_nan() || c.im.is_nan() => false, _ => true,
197 }
198 }
199
200 pub fn push_return(&mut self, value: Value) {
204 self.return_stack.push(value);
205 }
206
207 pub fn pop_return(&mut self) -> Result<Value, RuntimeError> {
208 self.return_stack.pop().ok_or(RuntimeError::StackUnderflow)
209 }
210
211 pub fn peek_return(&self) -> Result<&Value, RuntimeError> {
212 self.return_stack.last().ok_or(RuntimeError::StackUnderflow)
213 }
214
215 pub fn pop_with_context(&mut self, context: &str) -> Result<Value, RuntimeError> {
227 if let Some(pos) = &self.current_pos {
228 self.stack
229 .pop()
230 .ok_or_else(|| RuntimeError::StackUnderflowAt {
231 pos: pos.clone(),
232 context: context.to_string(),
233 })
234 } else {
235 self.stack.pop().ok_or(RuntimeError::StackUnderflow)
236 }
237 }
238
239 pub fn set_output(&mut self, output: Box<dyn Output>) {
241 self.output = Some(output);
242 }
243
244 #[allow(dead_code)]
245 pub fn has_output(&self) -> bool {
246 self.output.is_some()
247 }
248
249 pub fn set_time_source(&mut self, time_source: Box<dyn TimeSource>) {
250 self.time_source = Some(time_source);
251 }
252
253 #[allow(dead_code)]
254 pub fn has_time_source(&self) -> bool {
255 self.time_source.is_some()
256 }
257
258 pub fn writeln(&mut self, text: &str) -> Result<(), ()> {
260 if let Some(output) = &mut self.output {
261 output.write(text.as_bytes())?;
262 #[cfg(not(target_os = "none"))]
264 output.write(b"\n")?;
265 #[cfg(target_os = "none")]
266 output.write(b"\r\n")?;
267 output.flush()?;
268 }
269 Ok(())
270 }
271
272 pub fn write_str(&mut self, text: &str) -> Result<(), ()> {
274 if let Some(output) = &mut self.output {
275 output.write(text.as_bytes())?;
276 output.flush()?;
277 }
278 Ok(())
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 impl Interpreter {
288 fn pop_string(&mut self) -> Result<Rc<str>, RuntimeError> {
289 let value = self.pop()?;
290 match value {
291 Value::String(s) => Ok(s),
292 _ => Err(RuntimeError::TypeError("Expected string".to_string())),
293 }
294 }
295
296 fn pop_boolean(&mut self) -> Result<bool, RuntimeError> {
297 let value = self.pop()?;
298 match value {
299 Value::Boolean(b) => Ok(b),
300 _ => Err(RuntimeError::TypeError("Expected boolean".to_string())),
301 }
302 }
303 }
304
305 #[test]
306 fn test_atom_interning() {
307 let mut interp = Interpreter::new();
308
309 let atom1 = interp.intern_atom("hello");
310 let atom2 = interp.intern_atom("hello");
311
312 assert!(Rc::ptr_eq(&atom1, &atom2));
313 }
314
315 #[test]
316 fn test_stack_operations() {
317 let mut interp = Interpreter::new();
318
319 interp.push(Value::Number(42.0));
320 let popped = interp.pop().unwrap();
321
322 match popped {
323 Value::Number(n) => assert_eq!(n, 42.0),
324 _ => panic!("Expected number"),
325 }
326
327 assert!(interp.pop().is_err());
328 }
329
330 #[test]
331 fn test_list_construction() {
332 let interp = Interpreter::new();
333
334 let empty = interp.make_list(vec![]);
335 match empty {
336 Value::Nil => (),
337 _ => panic!("Expected Nil for empty list"),
338 }
339
340 let single = interp.make_list(vec![Value::Number(42.0)]);
341 match single {
342 Value::Pair(car, cdr) => match (car.as_ref(), cdr.as_ref()) {
343 (Value::Number(n), Value::Nil) => assert_eq!(*n, 42.0),
344 _ => panic!("Expected Pair(42, Nil)"),
345 },
346 _ => panic!("Expected Pair for single element list"),
347 }
348
349 let multi = interp.make_list(vec![
351 Value::Number(1.0),
352 Value::Number(2.0),
353 Value::Number(3.0),
354 ]);
355 match multi {
356 Value::Pair(car1, cdr1) => match (car1.as_ref(), cdr1.as_ref()) {
357 (Value::Number(n1), Value::Pair(car2, cdr2)) => {
358 assert_eq!(*n1, 1.0);
359 match (car2.as_ref(), cdr2.as_ref()) {
360 (Value::Number(n2), Value::Pair(car3, cdr3)) => {
361 assert_eq!(*n2, 2.0);
362 match (car3.as_ref(), cdr3.as_ref()) {
363 (Value::Number(n3), Value::Nil) => assert_eq!(*n3, 3.0),
364 _ => panic!("Expected third element to be 3.0 followed by Nil"),
365 }
366 }
367 _ => panic!("Expected second element to be 2.0"),
368 }
369 }
370 _ => panic!("Expected first element to be 1.0"),
371 },
372 _ => panic!("Expected Pair for multi-element list"),
373 }
374 }
375
376 #[test]
377 fn test_pop_number_success() {
378 let mut interp = Interpreter::new();
379 interp.push(Value::Number(42.0));
380 assert_eq!(interp.pop_number().unwrap(), 42.0);
381 }
382
383 #[test]
384 fn test_pop_number_type_error() {
385 let mut interp = Interpreter::new();
386 interp.push(Value::Nil);
387 assert!(matches!(
388 interp.pop_number(),
389 Err(RuntimeError::TypeError(msg)) if msg == "Expected number"
390 ));
391 }
392
393 #[test]
394 fn test_pop_number_underflow() {
395 let mut interp = Interpreter::new();
396 assert!(matches!(
397 interp.pop_number(),
398 Err(RuntimeError::StackUnderflow)
399 ));
400 }
401
402 #[test]
403 fn test_dictionary_operations() {
404 let mut interp = Interpreter::new();
405
406 let key = interp.intern_atom("test");
408 let entry = DictEntry {
409 value: Value::Number(99.0),
410 is_executable: false, doc: None,
412 };
413 interp.dictionary.insert(key.clone(), entry);
414
415 match interp.dictionary.get(&key) {
416 Some(dict_entry) => {
417 match &dict_entry.value {
418 Value::Number(n) => assert_eq!(*n, 99.0),
419 _ => panic!("Expected to find Number(99.0) in dictionary entry"),
420 }
421 assert!(!dict_entry.is_executable);
422 }
423 _ => panic!("Expected to find dictionary entry"),
424 }
425
426 let missing = interp.intern_atom("missing");
428 assert!(interp.dictionary.get(&missing).is_none());
429 }
430
431 #[test]
432 fn test_atom_interning_different_atoms() {
433 let mut interp = Interpreter::new();
434
435 let atom1 = interp.intern_atom("hello");
436 let atom2 = interp.intern_atom("world");
437 let atom3 = interp.intern_atom("hello");
438
439 assert!(Rc::ptr_eq(&atom1, &atom3));
441
442 assert!(!Rc::ptr_eq(&atom1, &atom2));
444
445 assert_eq!(&*atom1, "hello");
447 assert_eq!(&*atom2, "world");
448 }
449
450 #[test]
451 fn test_pop_string() {
452 let mut interp = Interpreter::new();
453
454 let string_val: Rc<str> = "hello world".into();
456 interp.push(Value::String(string_val));
457 let s = interp.pop_string().unwrap();
458 assert_eq!(&*s, "hello world");
459
460 interp.push(Value::Number(42.0));
462 assert!(matches!(
463 interp.pop_string(),
464 Err(RuntimeError::TypeError(msg)) if msg == "Expected string"
465 ));
466
467 assert!(matches!(
469 interp.pop_string(),
470 Err(RuntimeError::StackUnderflow)
471 ));
472 }
473
474 #[test]
475 fn test_string_vs_atom_distinction() {
476 let mut interp = Interpreter::new();
477
478 let string1 = Value::String("hello".into());
480 let string2 = Value::String("hello".into());
481
482 let atom1 = Value::Atom(interp.intern_atom("hello"));
484 let atom2 = Value::Atom(interp.intern_atom("hello"));
485
486 if let (Value::String(s1), Value::String(s2)) = (&string1, &string2) {
488 assert_eq!(s1, s2); assert!(!Rc::ptr_eq(s1, s2)); }
491
492 if let (Value::Atom(a1), Value::Atom(a2)) = (&atom1, &atom2) {
494 assert_eq!(a1, a2); assert!(Rc::ptr_eq(a1, a2)); }
497 }
498
499 #[test]
500 fn test_pop_boolean() {
501 let mut interp = Interpreter::new();
502
503 interp.push(Value::Boolean(true));
505 assert_eq!(interp.pop_boolean().unwrap(), true);
506
507 interp.push(Value::Boolean(false));
508 assert_eq!(interp.pop_boolean().unwrap(), false);
509
510 interp.push(Value::Number(42.0));
512 assert!(matches!(
513 interp.pop_boolean(),
514 Err(RuntimeError::TypeError(msg)) if msg == "Expected boolean"
515 ));
516
517 assert!(matches!(
519 interp.pop_boolean(),
520 Err(RuntimeError::StackUnderflow)
521 ));
522 }
523
524 #[test]
525 fn test_is_null() {
526 let interp = Interpreter::new();
527
528 assert!(interp.is_null(&Value::Null));
529 assert!(!interp.is_null(&Value::Nil));
530 assert!(!interp.is_null(&Value::Boolean(false)));
531 assert!(!interp.is_null(&Value::Number(0.0)));
532 }
533
534 #[test]
535 fn test_is_truthy() {
536 let interp = Interpreter::new();
537
538 assert!(interp.is_truthy(&Value::Boolean(true)));
540 assert!(!interp.is_truthy(&Value::Boolean(false)));
541
542 assert!(!interp.is_truthy(&Value::Null));
544 assert!(interp.is_truthy(&Value::Nil));
545
546 assert!(!interp.is_truthy(&Value::Number(0.0)));
548 assert!(!interp.is_truthy(&Value::Number(f64::NAN))); assert!(interp.is_truthy(&Value::Number(42.0)));
550 assert!(interp.is_truthy(&Value::Number(-1.0)));
551 assert!(interp.is_truthy(&Value::Number(f64::INFINITY)));
552 assert!(interp.is_truthy(&Value::Number(f64::NEG_INFINITY)));
553
554 assert!(!interp.is_truthy(&Value::String("".into())));
556 assert!(interp.is_truthy(&Value::String("hello".into())));
557
558 assert!(interp.is_truthy(&Value::Atom("hello".into())));
560 assert!(interp.is_truthy(&Value::QuotedAtom("hello".into())));
561
562 assert!(interp.is_truthy(&Value::Pair(
564 Rc::new(Value::Number(1.0)),
565 Rc::new(Value::Nil)
566 )));
567 }
568}