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
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 impl Interpreter {
258 fn pop_string(&mut self) -> Result<Rc<str>, RuntimeError> {
259 let value = self.pop()?;
260 match value {
261 Value::String(s) => Ok(s),
262 _ => Err(RuntimeError::TypeError("Expected string".to_string())),
263 }
264 }
265
266 fn pop_boolean(&mut self) -> Result<bool, RuntimeError> {
267 let value = self.pop()?;
268 match value {
269 Value::Boolean(b) => Ok(b),
270 _ => Err(RuntimeError::TypeError("Expected boolean".to_string())),
271 }
272 }
273 }
274
275 #[test]
276 fn test_atom_interning() {
277 let mut interp = Interpreter::new();
278
279 let atom1 = interp.intern_atom("hello");
280 let atom2 = interp.intern_atom("hello");
281
282 assert!(Rc::ptr_eq(&atom1, &atom2));
283 }
284
285 #[test]
286 fn test_stack_operations() {
287 let mut interp = Interpreter::new();
288
289 interp.push(Value::Number(42.0));
290 let popped = interp.pop().unwrap();
291
292 match popped {
293 Value::Number(n) => assert_eq!(n, 42.0),
294 _ => panic!("Expected number"),
295 }
296
297 assert!(interp.pop().is_err());
298 }
299
300 #[test]
301 fn test_list_construction() {
302 let interp = Interpreter::new();
303
304 let empty = interp.make_list(vec![]);
305 match empty {
306 Value::Nil => (),
307 _ => panic!("Expected Nil for empty list"),
308 }
309
310 let single = interp.make_list(vec![Value::Number(42.0)]);
311 match single {
312 Value::Pair(car, cdr) => match (car.as_ref(), cdr.as_ref()) {
313 (Value::Number(n), Value::Nil) => assert_eq!(*n, 42.0),
314 _ => panic!("Expected Pair(42, Nil)"),
315 },
316 _ => panic!("Expected Pair for single element list"),
317 }
318
319 let multi = interp.make_list(vec![
321 Value::Number(1.0),
322 Value::Number(2.0),
323 Value::Number(3.0),
324 ]);
325 match multi {
326 Value::Pair(car1, cdr1) => match (car1.as_ref(), cdr1.as_ref()) {
327 (Value::Number(n1), Value::Pair(car2, cdr2)) => {
328 assert_eq!(*n1, 1.0);
329 match (car2.as_ref(), cdr2.as_ref()) {
330 (Value::Number(n2), Value::Pair(car3, cdr3)) => {
331 assert_eq!(*n2, 2.0);
332 match (car3.as_ref(), cdr3.as_ref()) {
333 (Value::Number(n3), Value::Nil) => assert_eq!(*n3, 3.0),
334 _ => panic!("Expected third element to be 3.0 followed by Nil"),
335 }
336 }
337 _ => panic!("Expected second element to be 2.0"),
338 }
339 }
340 _ => panic!("Expected first element to be 1.0"),
341 },
342 _ => panic!("Expected Pair for multi-element list"),
343 }
344 }
345
346 #[test]
347 fn test_pop_number_success() {
348 let mut interp = Interpreter::new();
349 interp.push(Value::Number(42.0));
350 assert_eq!(interp.pop_number().unwrap(), 42.0);
351 }
352
353 #[test]
354 fn test_pop_number_type_error() {
355 let mut interp = Interpreter::new();
356 interp.push(Value::Nil);
357 assert!(matches!(
358 interp.pop_number(),
359 Err(RuntimeError::TypeError(msg)) if msg == "Expected number"
360 ));
361 }
362
363 #[test]
364 fn test_pop_number_underflow() {
365 let mut interp = Interpreter::new();
366 assert!(matches!(
367 interp.pop_number(),
368 Err(RuntimeError::StackUnderflow)
369 ));
370 }
371
372 #[test]
373 fn test_dictionary_operations() {
374 let mut interp = Interpreter::new();
375
376 let key = interp.intern_atom("test");
378 let entry = DictEntry {
379 value: Value::Number(99.0),
380 is_executable: false, doc: None,
382 };
383 interp.dictionary.insert(key.clone(), entry);
384
385 match interp.dictionary.get(&key) {
386 Some(dict_entry) => {
387 match &dict_entry.value {
388 Value::Number(n) => assert_eq!(*n, 99.0),
389 _ => panic!("Expected to find Number(99.0) in dictionary entry"),
390 }
391 assert!(!dict_entry.is_executable);
392 }
393 _ => panic!("Expected to find dictionary entry"),
394 }
395
396 let missing = interp.intern_atom("missing");
398 assert!(interp.dictionary.get(&missing).is_none());
399 }
400
401 #[test]
402 fn test_atom_interning_different_atoms() {
403 let mut interp = Interpreter::new();
404
405 let atom1 = interp.intern_atom("hello");
406 let atom2 = interp.intern_atom("world");
407 let atom3 = interp.intern_atom("hello");
408
409 assert!(Rc::ptr_eq(&atom1, &atom3));
411
412 assert!(!Rc::ptr_eq(&atom1, &atom2));
414
415 assert_eq!(&*atom1, "hello");
417 assert_eq!(&*atom2, "world");
418 }
419
420 #[test]
421 fn test_pop_string() {
422 let mut interp = Interpreter::new();
423
424 let string_val: Rc<str> = "hello world".into();
426 interp.push(Value::String(string_val));
427 let s = interp.pop_string().unwrap();
428 assert_eq!(&*s, "hello world");
429
430 interp.push(Value::Number(42.0));
432 assert!(matches!(
433 interp.pop_string(),
434 Err(RuntimeError::TypeError(msg)) if msg == "Expected string"
435 ));
436
437 assert!(matches!(
439 interp.pop_string(),
440 Err(RuntimeError::StackUnderflow)
441 ));
442 }
443
444 #[test]
445 fn test_string_vs_atom_distinction() {
446 let mut interp = Interpreter::new();
447
448 let string1 = Value::String("hello".into());
450 let string2 = Value::String("hello".into());
451
452 let atom1 = Value::Atom(interp.intern_atom("hello"));
454 let atom2 = Value::Atom(interp.intern_atom("hello"));
455
456 if let (Value::String(s1), Value::String(s2)) = (&string1, &string2) {
458 assert_eq!(s1, s2); assert!(!Rc::ptr_eq(s1, s2)); }
461
462 if let (Value::Atom(a1), Value::Atom(a2)) = (&atom1, &atom2) {
464 assert_eq!(a1, a2); assert!(Rc::ptr_eq(a1, a2)); }
467 }
468
469 #[test]
470 fn test_pop_boolean() {
471 let mut interp = Interpreter::new();
472
473 interp.push(Value::Boolean(true));
475 assert_eq!(interp.pop_boolean().unwrap(), true);
476
477 interp.push(Value::Boolean(false));
478 assert_eq!(interp.pop_boolean().unwrap(), false);
479
480 interp.push(Value::Number(42.0));
482 assert!(matches!(
483 interp.pop_boolean(),
484 Err(RuntimeError::TypeError(msg)) if msg == "Expected boolean"
485 ));
486
487 assert!(matches!(
489 interp.pop_boolean(),
490 Err(RuntimeError::StackUnderflow)
491 ));
492 }
493
494 #[test]
495 fn test_is_null() {
496 let interp = Interpreter::new();
497
498 assert!(interp.is_null(&Value::Null));
499 assert!(!interp.is_null(&Value::Nil));
500 assert!(!interp.is_null(&Value::Boolean(false)));
501 assert!(!interp.is_null(&Value::Number(0.0)));
502 }
503
504 #[test]
505 fn test_is_truthy() {
506 let interp = Interpreter::new();
507
508 assert!(interp.is_truthy(&Value::Boolean(true)));
510 assert!(!interp.is_truthy(&Value::Boolean(false)));
511
512 assert!(!interp.is_truthy(&Value::Null));
514 assert!(interp.is_truthy(&Value::Nil));
515
516 assert!(!interp.is_truthy(&Value::Number(0.0)));
518 assert!(!interp.is_truthy(&Value::Number(f64::NAN))); assert!(interp.is_truthy(&Value::Number(42.0)));
520 assert!(interp.is_truthy(&Value::Number(-1.0)));
521 assert!(interp.is_truthy(&Value::Number(f64::INFINITY)));
522 assert!(interp.is_truthy(&Value::Number(f64::NEG_INFINITY)));
523
524 assert!(!interp.is_truthy(&Value::String("".into())));
526 assert!(interp.is_truthy(&Value::String("hello".into())));
527
528 assert!(interp.is_truthy(&Value::Atom("hello".into())));
530 assert!(interp.is_truthy(&Value::QuotedAtom("hello".into())));
531
532 assert!(interp.is_truthy(&Value::Pair(
534 Rc::new(Value::Number(1.0)),
535 Rc::new(Value::Nil)
536 )));
537 }
538}