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