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