1use std::collections::HashMap;
7
8use indexmap::IndexMap;
9
10use crate::ast::{BinaryOp, Block, ElseClause, Expression, Program, Statement, UnaryOp};
11use crate::builtins::get_default_builtins;
12use crate::error::RuntimeError;
13use crate::lexer::Lexer;
14use crate::parser::Parser;
15use crate::Value;
16
17const MAX_CALL_DEPTH: usize = 64;
21
22const MAX_OUTPUT_LINES: usize = 1000;
24
25const MAX_OUTPUT_BYTES: usize = 64 * 1024; #[derive(Debug, Clone)]
30struct UserFunction {
31 params: Vec<String>,
33 body: Block,
35}
36
37enum ControlFlow {
39 Continue(Value),
41 Return(Value),
43}
44
45impl ControlFlow {
46 fn into_value(self) -> Value {
47 match self {
48 ControlFlow::Continue(v) | ControlFlow::Return(v) => v,
49 }
50 }
51}
52
53pub struct Interpreter {
55 scopes: Vec<HashMap<String, Value>>,
57 functions: HashMap<String, UserFunction>,
59 builtins: HashMap<String, fn(Vec<Value>) -> Result<Value, RuntimeError>>,
61 output: Vec<String>,
63 output_bytes: usize,
65 output_truncated: bool,
67 call_depth: usize,
69}
70
71impl Default for Interpreter {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl Interpreter {
78 pub fn new() -> Self {
83 Self::with_builtins(get_default_builtins())
84 }
85
86 pub fn with_builtins(
91 builtins: HashMap<String, fn(Vec<Value>) -> Result<Value, RuntimeError>>,
92 ) -> Self {
93 let mut global_scope = HashMap::new();
94
95 for (key, value) in std::env::vars() {
97 global_scope.insert(key, Value::String(value));
98 }
99
100 Self {
101 scopes: vec![global_scope],
102 functions: HashMap::new(),
103 builtins,
104 output: Vec::new(),
105 output_bytes: 0,
106 output_truncated: false,
107 call_depth: 0,
108 }
109 }
110
111 pub fn new_without_env() -> Self {
113 Self {
114 scopes: vec![HashMap::new()],
115 functions: HashMap::new(),
116 builtins: get_default_builtins(),
117 output: Vec::new(),
118 output_bytes: 0,
119 output_truncated: false,
120 call_depth: 0,
121 }
122 }
123
124 pub fn run(&mut self, source: &str) -> Result<Value, crate::FiddlerError> {
126 let mut lexer = Lexer::new(source);
127 let tokens = lexer.tokenize()?;
128 let mut parser = Parser::new(tokens);
129 let program = parser.parse()?;
130 Ok(self.execute(&program)?)
131 }
132
133 pub fn execute(&mut self, program: &Program) -> Result<Value, RuntimeError> {
135 let mut result = Value::Null;
136 for statement in &program.statements {
137 match self.execute_statement(statement)? {
138 ControlFlow::Continue(v) => result = v,
139 ControlFlow::Return(_) => {
140 return Err(RuntimeError::ReturnOutsideFunction);
141 }
142 }
143 }
144 Ok(result)
145 }
146
147 pub fn output(&self) -> &[String] {
149 &self.output
150 }
151
152 pub fn clear_output(&mut self) {
154 self.output.clear();
155 self.output_bytes = 0;
156 self.output_truncated = false;
157 }
158
159 pub fn is_output_truncated(&self) -> bool {
161 self.output_truncated
162 }
163
164 pub fn set_variable_value(&mut self, name: impl Into<String>, value: Value) {
175 if let Some(scope) = self.scopes.first_mut() {
176 scope.insert(name.into(), value);
177 }
178 }
179
180 pub fn set_variable_bytes(&mut self, name: impl Into<String>, bytes: Vec<u8>) {
188 self.set_variable_value(name, Value::Bytes(bytes));
189 }
190
191 pub fn set_variable_string(&mut self, name: impl Into<String>, value: impl Into<String>) {
199 self.set_variable_value(name, Value::String(value.into()));
200 }
201
202 pub fn set_variable_int(&mut self, name: impl Into<String>, value: i64) {
210 self.set_variable_value(name, Value::Integer(value));
211 }
212
213 pub fn set_variable_array(&mut self, name: impl Into<String>, values: Vec<Value>) {
221 self.set_variable_value(name, Value::Array(values));
222 }
223
224 pub fn set_variable_dict(&mut self, name: impl Into<String>, values: IndexMap<String, Value>) {
232 self.set_variable_value(name, Value::Dictionary(values));
233 }
234
235 pub fn get_value(&self, name: &str) -> Option<Value> {
246 for scope in self.scopes.iter().rev() {
247 if let Some(value) = scope.get(name) {
248 return Some(value.clone());
249 }
250 }
251 None
252 }
253
254 pub fn get_bytes(&self, name: &str) -> Option<Vec<u8>> {
265 self.get_value(name).map(|v| v.to_bytes())
266 }
267
268 pub fn has_variable(&self, name: &str) -> bool {
277 self.get_value(name).is_some()
278 }
279
280 fn push_scope(&mut self) {
283 self.scopes.push(HashMap::new());
284 }
285
286 fn pop_scope(&mut self) {
287 self.scopes.pop();
288 }
289
290 fn define_variable(&mut self, name: String, value: Value) {
291 if let Some(scope) = self.scopes.last_mut() {
292 scope.insert(name, value);
293 }
294 }
295
296 fn get_variable(&self, name: &str) -> Result<Value, RuntimeError> {
297 for scope in self.scopes.iter().rev() {
298 if let Some(value) = scope.get(name) {
299 return Ok(value.clone());
300 }
301 }
302 Err(RuntimeError::undefined_variable(name))
303 }
304
305 fn set_variable(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
306 for scope in self.scopes.iter_mut().rev() {
307 if scope.contains_key(name) {
308 scope.insert(name.to_string(), value);
309 return Ok(());
310 }
311 }
312 Err(RuntimeError::undefined_variable(name))
313 }
314
315 fn execute_statement(&mut self, stmt: &Statement) -> Result<ControlFlow, RuntimeError> {
318 match stmt {
319 Statement::Let { name, value, .. } => {
320 let val = self.evaluate_expression(value)?;
321 self.define_variable(name.clone(), val);
322 Ok(ControlFlow::Continue(Value::Null))
323 }
324
325 Statement::If {
326 condition,
327 then_block,
328 else_block,
329 ..
330 } => {
331 let cond = self.evaluate_expression(condition)?;
332 if self.is_truthy(&cond) {
333 self.execute_block(then_block)
334 } else if let Some(else_clause) = else_block {
335 match else_clause {
336 ElseClause::Block(block) => self.execute_block(block),
337 ElseClause::ElseIf(if_stmt) => self.execute_statement(if_stmt),
338 }
339 } else {
340 Ok(ControlFlow::Continue(Value::Null))
341 }
342 }
343
344 Statement::For {
345 init,
346 condition,
347 update,
348 body,
349 ..
350 } => {
351 self.push_scope();
352
353 if let Some(init_stmt) = init {
355 self.execute_statement(init_stmt)?;
356 }
357
358 loop {
360 if let Some(cond) = condition {
362 let cond_val = self.evaluate_expression(cond)?;
363 if !self.is_truthy(&cond_val) {
364 break;
365 }
366 }
367
368 match self.execute_block(body)? {
370 ControlFlow::Return(v) => {
371 self.pop_scope();
372 return Ok(ControlFlow::Return(v));
373 }
374 ControlFlow::Continue(_) => {}
375 }
376
377 if let Some(upd) = update {
379 self.evaluate_expression(upd)?;
380 }
381 }
382
383 self.pop_scope();
384 Ok(ControlFlow::Continue(Value::Null))
385 }
386
387 Statement::Return { value, .. } => {
388 let val = if let Some(expr) = value {
389 self.evaluate_expression(expr)?
390 } else {
391 Value::Null
392 };
393 Ok(ControlFlow::Return(val))
394 }
395
396 Statement::Function {
397 name, params, body, ..
398 } => {
399 self.functions.insert(
400 name.clone(),
401 UserFunction {
402 params: params.clone(),
403 body: body.clone(),
404 },
405 );
406 Ok(ControlFlow::Continue(Value::Null))
407 }
408
409 Statement::Expression { expression, .. } => {
410 let val = self.evaluate_expression(expression)?;
411 Ok(ControlFlow::Continue(val))
412 }
413
414 Statement::Block(block) => self.execute_block(block),
415 }
416 }
417
418 fn execute_block(&mut self, block: &Block) -> Result<ControlFlow, RuntimeError> {
419 self.push_scope();
420 let mut result = ControlFlow::Continue(Value::Null);
421
422 for stmt in &block.statements {
423 result = self.execute_statement(stmt)?;
424 if matches!(result, ControlFlow::Return(_)) {
425 break;
426 }
427 }
428
429 self.pop_scope();
430 Ok(result)
431 }
432
433 fn evaluate_expression(&mut self, expr: &Expression) -> Result<Value, RuntimeError> {
436 match expr {
437 Expression::Integer { value, .. } => Ok(Value::Integer(*value)),
438 Expression::Float { value, .. } => Ok(Value::Float(*value)),
439 Expression::String { value, .. } => Ok(Value::String(value.clone())),
440 Expression::Boolean { value, .. } => Ok(Value::Boolean(*value)),
441 Expression::Null { .. } => Ok(Value::Null),
442
443 Expression::Identifier { name, .. } => self.get_variable(name),
444
445 Expression::Binary {
446 left,
447 operator,
448 right,
449 ..
450 } => {
451 let left_val = self.evaluate_expression(left)?;
452 let right_val = self.evaluate_expression(right)?;
453 self.evaluate_binary_op(*operator, left_val, right_val)
454 }
455
456 Expression::Unary {
457 operator, operand, ..
458 } => {
459 let val = self.evaluate_expression(operand)?;
460 self.evaluate_unary_op(*operator, val)
461 }
462
463 Expression::Assignment { name, value, .. } => {
464 let val = self.evaluate_expression(value)?;
465 self.set_variable(name, val.clone())?;
466 Ok(val)
467 }
468
469 Expression::Call {
470 function,
471 arguments,
472 ..
473 } => self.call_function(function, arguments),
474
475 Expression::MethodCall {
476 receiver,
477 method,
478 arguments,
479 ..
480 } => self.call_method(receiver, method, arguments),
481
482 Expression::Grouped { expression, .. } => self.evaluate_expression(expression),
483
484 Expression::ArrayLiteral { elements, .. } => {
485 let values: Vec<Value> = elements
486 .iter()
487 .map(|e| self.evaluate_expression(e))
488 .collect::<Result<_, _>>()?;
489 Ok(Value::Array(values))
490 }
491
492 Expression::DictionaryLiteral { pairs, .. } => {
493 let mut dict = IndexMap::new();
494 for (key_expr, value_expr) in pairs {
495 let key = match self.evaluate_expression(key_expr)? {
496 Value::String(s) => s,
497 _ => {
498 return Err(RuntimeError::type_mismatch(
499 "Dictionary key must be a string",
500 ))
501 }
502 };
503 let value = self.evaluate_expression(value_expr)?;
504 dict.insert(key, value);
505 }
506 Ok(Value::Dictionary(dict))
507 }
508 }
509 }
510
511 fn evaluate_binary_op(
512 &mut self,
513 op: BinaryOp,
514 left: Value,
515 right: Value,
516 ) -> Result<Value, RuntimeError> {
517 match op {
518 BinaryOp::Add => match (&left, &right) {
520 (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a + b)),
521 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
522 (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
523 (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a + *b as f64)),
524 (Value::String(a), Value::String(b)) => Ok(Value::String(format!("{}{}", a, b))),
525 _ => Err(RuntimeError::type_mismatch(format!(
526 "Cannot add {:?} and {:?}",
527 left, right
528 ))),
529 },
530 BinaryOp::Subtract => match (&left, &right) {
531 (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a - b)),
532 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
533 (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 - b)),
534 (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a - *b as f64)),
535 _ => Err(RuntimeError::type_mismatch(
536 "Subtraction requires numeric types".to_string(),
537 )),
538 },
539 BinaryOp::Multiply => match (&left, &right) {
540 (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a * b)),
541 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
542 (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 * b)),
543 (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a * *b as f64)),
544 _ => Err(RuntimeError::type_mismatch(
545 "Multiplication requires numeric types".to_string(),
546 )),
547 },
548 BinaryOp::Divide => match (&left, &right) {
549 (Value::Integer(_), Value::Integer(0)) => {
550 Err(RuntimeError::DivisionByZero { position: None })
551 }
552 (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a / b)),
553 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a / b)), (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 / b)),
555 (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a / *b as f64)),
556 _ => Err(RuntimeError::type_mismatch(
557 "Division requires numeric types".to_string(),
558 )),
559 },
560 BinaryOp::Modulo => match (&left, &right) {
561 (Value::Integer(_), Value::Integer(0)) => {
562 Err(RuntimeError::DivisionByZero { position: None })
563 }
564 (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a % b)),
565 (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a % b)),
566 (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 % b)),
567 (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a % *b as f64)),
568 _ => Err(RuntimeError::type_mismatch(
569 "Modulo requires numeric types".to_string(),
570 )),
571 },
572
573 BinaryOp::Equal => Ok(Value::Boolean(left == right)),
575 BinaryOp::NotEqual => Ok(Value::Boolean(left != right)),
576 BinaryOp::LessThan
577 | BinaryOp::LessEqual
578 | BinaryOp::GreaterThan
579 | BinaryOp::GreaterEqual => self.evaluate_comparison(op, &left, &right),
580
581 BinaryOp::And => Ok(Value::Boolean(
583 self.is_truthy(&left) && self.is_truthy(&right),
584 )),
585 BinaryOp::Or => Ok(Value::Boolean(
586 self.is_truthy(&left) || self.is_truthy(&right),
587 )),
588 }
589 }
590
591 fn evaluate_comparison(
593 &self,
594 op: BinaryOp,
595 left: &Value,
596 right: &Value,
597 ) -> Result<Value, RuntimeError> {
598 let result = match (left, right) {
599 (Value::Integer(a), Value::Integer(b)) => match op {
600 BinaryOp::LessThan => a < b,
601 BinaryOp::LessEqual => a <= b,
602 BinaryOp::GreaterThan => a > b,
603 BinaryOp::GreaterEqual => a >= b,
604 _ => unreachable!(),
605 },
606 (Value::Float(a), Value::Float(b)) => match op {
607 BinaryOp::LessThan => a < b,
608 BinaryOp::LessEqual => a <= b,
609 BinaryOp::GreaterThan => a > b,
610 BinaryOp::GreaterEqual => a >= b,
611 _ => unreachable!(),
612 },
613 (Value::Integer(a), Value::Float(b)) => {
615 let a_float = *a as f64;
616 match op {
617 BinaryOp::LessThan => a_float < *b,
618 BinaryOp::LessEqual => a_float <= *b,
619 BinaryOp::GreaterThan => a_float > *b,
620 BinaryOp::GreaterEqual => a_float >= *b,
621 _ => unreachable!(),
622 }
623 }
624 (Value::Float(a), Value::Integer(b)) => {
625 let b_float = *b as f64;
626 match op {
627 BinaryOp::LessThan => *a < b_float,
628 BinaryOp::LessEqual => *a <= b_float,
629 BinaryOp::GreaterThan => *a > b_float,
630 BinaryOp::GreaterEqual => *a >= b_float,
631 _ => unreachable!(),
632 }
633 }
634 (Value::String(a), Value::String(b)) => match op {
635 BinaryOp::LessThan => a < b,
636 BinaryOp::LessEqual => a <= b,
637 BinaryOp::GreaterThan => a > b,
638 BinaryOp::GreaterEqual => a >= b,
639 _ => unreachable!(),
640 },
641 _ => {
642 return Err(RuntimeError::type_mismatch(
643 "Comparison requires matching or numeric types".to_string(),
644 ))
645 }
646 };
647 Ok(Value::Boolean(result))
648 }
649
650 fn evaluate_unary_op(&self, op: UnaryOp, operand: Value) -> Result<Value, RuntimeError> {
651 match op {
652 UnaryOp::Not => Ok(Value::Boolean(!self.is_truthy(&operand))),
653 UnaryOp::Negate => match operand {
654 Value::Integer(n) => Ok(Value::Integer(-n)),
655 Value::Float(f) => Ok(Value::Float(-f)),
656 _ => Err(RuntimeError::type_mismatch(
657 "Negation requires numeric type".to_string(),
658 )),
659 },
660 }
661 }
662
663 fn is_truthy(&self, value: &Value) -> bool {
664 match value {
665 Value::Boolean(b) => *b,
666 Value::Integer(n) => *n != 0,
667 Value::Float(f) => *f != 0.0 && !f.is_nan(),
668 Value::String(s) => !s.is_empty(),
669 Value::Bytes(b) => !b.is_empty(),
670 Value::Array(a) => !a.is_empty(),
671 Value::Dictionary(d) => !d.is_empty(),
672 Value::Null => false,
673 }
674 }
675
676 fn capture_print_output(&mut self, args: &[Value]) {
681 if self.output_truncated {
682 return;
683 }
684
685 let output_str = args
686 .iter()
687 .map(|v| format!("{}", v))
688 .collect::<Vec<_>>()
689 .join(" ");
690
691 let new_bytes = self.output_bytes + output_str.len();
692 let new_lines = self.output.len() + 1;
693
694 if new_lines > MAX_OUTPUT_LINES || new_bytes > MAX_OUTPUT_BYTES {
695 self.output.push("[truncated]".to_string());
696 self.output_truncated = true;
697 } else {
698 self.output_bytes = new_bytes;
699 self.output.push(output_str);
700 }
701 }
702
703 fn call_function(
705 &mut self,
706 name: &str,
707 arguments: &[Expression],
708 ) -> Result<Value, RuntimeError> {
709 let args: Vec<Value> = arguments
711 .iter()
712 .map(|arg| self.evaluate_expression(arg))
713 .collect::<Result<_, _>>()?;
714
715 if let Some(&builtin) = self.builtins.get(name) {
717 if name == "print" {
718 self.capture_print_output(&args);
719 }
720 return builtin(args);
721 }
722
723 if let Some(func) = self.functions.get(name).cloned() {
725 return self.call_user_function(&func, args);
726 }
727
728 Err(RuntimeError::undefined_function(name))
729 }
730
731 fn call_method(
761 &mut self,
762 receiver: &Expression,
763 method: &str,
764 arguments: &[Expression],
765 ) -> Result<Value, RuntimeError> {
766 let receiver_value = self.evaluate_expression(receiver)?;
768
769 let mut args = Vec::with_capacity(arguments.len() + 1);
772 args.push(receiver_value);
773
774 for arg in arguments {
775 args.push(self.evaluate_expression(arg)?);
776 }
777
778 if let Some(&builtin) = self.builtins.get(method) {
780 if method == "print" {
781 self.capture_print_output(&args);
782 }
783 return builtin(args);
784 }
785
786 if let Some(func) = self.functions.get(method).cloned() {
788 return self.call_user_function(&func, args);
789 }
790
791 Err(RuntimeError::undefined_function(method))
792 }
793
794 fn call_user_function(
796 &mut self,
797 func: &UserFunction,
798 args: Vec<Value>,
799 ) -> Result<Value, RuntimeError> {
800 if self.call_depth >= MAX_CALL_DEPTH {
802 return Err(RuntimeError::StackOverflow {
803 max_depth: MAX_CALL_DEPTH,
804 });
805 }
806
807 if args.len() != func.params.len() {
808 return Err(RuntimeError::WrongArgumentCount {
809 expected: func.params.len(),
810 actual: args.len(),
811 position: None,
812 });
813 }
814
815 self.call_depth += 1;
817
818 self.push_scope();
820
821 for (param, arg) in func.params.iter().zip(args) {
823 self.define_variable(param.clone(), arg);
824 }
825
826 let result = self.execute_block(&func.body);
828
829 self.pop_scope();
830
831 self.call_depth -= 1;
833
834 result.map(|cf| cf.into_value())
835 }
836}
837
838#[cfg(test)]
839mod tests {
840 use super::*;
841
842 fn run(source: &str) -> Result<Value, crate::FiddlerError> {
843 let mut interpreter = Interpreter::new();
844 interpreter.run(source)
845 }
846
847 #[test]
848 fn test_integer_literal() {
849 assert_eq!(run("42;").unwrap(), Value::Integer(42));
850 }
851
852 #[test]
853 fn test_string_literal() {
854 assert_eq!(
855 run(r#""hello";"#).unwrap(),
856 Value::String("hello".to_string())
857 );
858 }
859
860 #[test]
861 fn test_boolean_literal() {
862 assert_eq!(run("true;").unwrap(), Value::Boolean(true));
863 assert_eq!(run("false;").unwrap(), Value::Boolean(false));
864 }
865
866 #[test]
867 fn test_arithmetic() {
868 assert_eq!(run("5 + 3;").unwrap(), Value::Integer(8));
869 assert_eq!(run("5 - 3;").unwrap(), Value::Integer(2));
870 assert_eq!(run("5 * 3;").unwrap(), Value::Integer(15));
871 assert_eq!(run("6 / 2;").unwrap(), Value::Integer(3));
872 assert_eq!(run("7 % 3;").unwrap(), Value::Integer(1));
873 }
874
875 #[test]
876 fn test_string_concatenation() {
877 assert_eq!(
878 run(r#""hello" + " " + "world";"#).unwrap(),
879 Value::String("hello world".to_string())
880 );
881 }
882
883 #[test]
884 fn test_comparison() {
885 assert_eq!(run("5 > 3;").unwrap(), Value::Boolean(true));
886 assert_eq!(run("5 < 3;").unwrap(), Value::Boolean(false));
887 assert_eq!(run("5 == 5;").unwrap(), Value::Boolean(true));
888 assert_eq!(run("5 != 3;").unwrap(), Value::Boolean(true));
889 }
890
891 #[test]
892 fn test_logical() {
893 assert_eq!(run("true && true;").unwrap(), Value::Boolean(true));
894 assert_eq!(run("true && false;").unwrap(), Value::Boolean(false));
895 assert_eq!(run("true || false;").unwrap(), Value::Boolean(true));
896 assert_eq!(run("!true;").unwrap(), Value::Boolean(false));
897 }
898
899 #[test]
900 fn test_variable() {
901 assert_eq!(run("let x = 10; x;").unwrap(), Value::Integer(10));
902 }
903
904 #[test]
905 fn test_variable_assignment() {
906 assert_eq!(run("let x = 10; x = 20; x;").unwrap(), Value::Integer(20));
907 }
908
909 #[test]
910 fn test_if_true() {
911 assert_eq!(
912 run("let x = 0; if (true) { x = 1; } x;").unwrap(),
913 Value::Integer(1)
914 );
915 }
916
917 #[test]
918 fn test_if_false() {
919 assert_eq!(
920 run("let x = 0; if (false) { x = 1; } x;").unwrap(),
921 Value::Integer(0)
922 );
923 }
924
925 #[test]
926 fn test_if_else() {
927 assert_eq!(
928 run("let x = 0; if (false) { x = 1; } else { x = 2; } x;").unwrap(),
929 Value::Integer(2)
930 );
931 }
932
933 #[test]
934 fn test_for_loop() {
935 assert_eq!(
936 run("let sum = 0; for (let i = 0; i < 5; i = i + 1) { sum = sum + i; } sum;").unwrap(),
937 Value::Integer(10) );
939 }
940
941 #[test]
942 fn test_function() {
943 assert_eq!(
944 run("fn add(a, b) { return a + b; } add(2, 3);").unwrap(),
945 Value::Integer(5)
946 );
947 }
948
949 #[test]
950 fn test_recursion() {
951 let source = r#"
952 fn factorial(n) {
953 if (n <= 1) {
954 return 1;
955 }
956 return n * factorial(n - 1);
957 }
958 factorial(5);
959 "#;
960 assert_eq!(run(source).unwrap(), Value::Integer(120));
961 }
962
963 #[test]
964 fn test_division_by_zero() {
965 let result = run("5 / 0;");
966 assert!(matches!(
967 result,
968 Err(crate::FiddlerError::Runtime(RuntimeError::DivisionByZero {
969 position: None
970 }))
971 ));
972 }
973
974 #[test]
975 fn test_undefined_variable() {
976 let result = run("x;");
977 assert!(matches!(
978 result,
979 Err(crate::FiddlerError::Runtime(
980 RuntimeError::UndefinedVariable { .. }
981 ))
982 ));
983 }
984
985 #[test]
986 fn test_undefined_function() {
987 let result = run("foo();");
988 assert!(matches!(
989 result,
990 Err(crate::FiddlerError::Runtime(
991 RuntimeError::UndefinedFunction { .. }
992 ))
993 ));
994 }
995
996 #[test]
997 fn test_wrong_argument_count() {
998 let result = run("fn add(a, b) { return a + b; } add(1);");
999 assert!(matches!(
1000 result,
1001 Err(crate::FiddlerError::Runtime(
1002 RuntimeError::WrongArgumentCount {
1003 expected: 2,
1004 actual: 1,
1005 ..
1006 }
1007 ))
1008 ));
1009 }
1010
1011 #[test]
1012 fn test_set_and_get_variable() {
1013 let mut interpreter = Interpreter::new_without_env();
1014 interpreter.set_variable_value("x", Value::Integer(42));
1015 assert_eq!(interpreter.get_value("x"), Some(Value::Integer(42)));
1016 }
1017
1018 #[test]
1019 fn test_set_variable_string() {
1020 let mut interpreter = Interpreter::new_without_env();
1021 interpreter.set_variable_string("name", "Alice");
1022 assert_eq!(
1023 interpreter.get_value("name"),
1024 Some(Value::String("Alice".to_string()))
1025 );
1026 }
1027
1028 #[test]
1029 fn test_set_variable_bytes() {
1030 let mut interpreter = Interpreter::new_without_env();
1031 interpreter.set_variable_bytes("data", vec![1, 2, 3]);
1032 assert_eq!(
1033 interpreter.get_value("data"),
1034 Some(Value::Bytes(vec![1, 2, 3]))
1035 );
1036 }
1037
1038 #[test]
1039 fn test_get_bytes() {
1040 let mut interpreter = Interpreter::new_without_env();
1041 interpreter.set_variable_string("msg", "hello");
1042 assert_eq!(
1043 interpreter.get_bytes("msg"),
1044 Some("hello".as_bytes().to_vec())
1045 );
1046 }
1047
1048 #[test]
1049 fn test_has_variable() {
1050 let mut interpreter = Interpreter::new_without_env();
1051 interpreter.set_variable_int("count", 10);
1052 assert!(interpreter.has_variable("count"));
1053 assert!(!interpreter.has_variable("nonexistent"));
1054 }
1055
1056 #[test]
1057 fn test_env_vars_loaded() {
1058 std::env::set_var("FIDDLER_TEST_VAR", "test123");
1059 let interpreter = Interpreter::new();
1060 assert_eq!(
1061 interpreter.get_value("FIDDLER_TEST_VAR"),
1062 Some(Value::String("test123".to_string()))
1063 );
1064 std::env::remove_var("FIDDLER_TEST_VAR");
1065 }
1066
1067 #[test]
1068 fn test_use_injected_variable_in_script() {
1069 let mut interpreter = Interpreter::new_without_env();
1070 interpreter.set_variable_int("input", 5);
1071 let result = interpreter.run("input * 2;").unwrap();
1072 assert_eq!(result, Value::Integer(10));
1073 }
1074
1075 #[test]
1076 fn test_bytes_in_script() {
1077 let mut interpreter = Interpreter::new_without_env();
1078 interpreter.set_variable_bytes("data", b"hello".to_vec());
1079 let result = interpreter.run("bytes_to_string(data);").unwrap();
1080 assert_eq!(result, Value::String("hello".to_string()));
1081 }
1082
1083 #[test]
1084 fn test_parse_json_in_script() {
1085 let mut interpreter = Interpreter::new_without_env();
1086 interpreter.set_variable_bytes("json_data", br#"{"name": "test"}"#.to_vec());
1087 let result = interpreter.run("parse_json(json_data);").unwrap();
1088 assert!(matches!(result, Value::Dictionary(_)));
1089 }
1090
1091 #[test]
1092 fn test_bytes_truthy() {
1093 let mut interpreter = Interpreter::new_without_env();
1094 interpreter.set_variable_bytes("data", b"hello".to_vec());
1095 let result = interpreter.run("if (data) { 1; } else { 0; }").unwrap();
1096 assert_eq!(result, Value::Integer(1));
1097 }
1098
1099 #[test]
1100 fn test_empty_bytes_falsy() {
1101 let mut interpreter = Interpreter::new_without_env();
1102 interpreter.set_variable_bytes("empty", vec![]);
1103 let result = interpreter.run("if (empty) { 1; } else { 0; }").unwrap();
1104 assert_eq!(result, Value::Integer(0));
1105 }
1106
1107 #[test]
1108 fn test_print_output_capture() {
1109 let mut interpreter = Interpreter::new_without_env();
1110 interpreter
1111 .run(r#"print("hello"); print("world");"#)
1112 .unwrap();
1113 assert_eq!(interpreter.output(), &["hello", "world"]);
1114 }
1115
1116 #[test]
1117 fn test_print_output_multiple_args() {
1118 let mut interpreter = Interpreter::new_without_env();
1119 interpreter.run(r#"print("a", 42, true);"#).unwrap();
1120 assert_eq!(interpreter.output(), &["a 42 true"]);
1121 }
1122
1123 #[test]
1124 fn test_clear_output() {
1125 let mut interpreter = Interpreter::new_without_env();
1126 interpreter.run(r#"print("test");"#).unwrap();
1127 assert_eq!(interpreter.output().len(), 1);
1128 assert!(!interpreter.is_output_truncated());
1129 interpreter.clear_output();
1130 assert!(interpreter.output().is_empty());
1131 assert!(!interpreter.is_output_truncated());
1132 }
1133
1134 #[test]
1135 fn test_output_truncation_by_lines() {
1136 let mut interpreter = Interpreter::new_without_env();
1137 let mut source = String::new();
1139 for i in 0..1005 {
1140 source.push_str(&format!("print({});\n", i));
1141 }
1142 interpreter.run(&source).unwrap();
1143
1144 assert!(interpreter.output().len() <= MAX_OUTPUT_LINES + 1);
1146 assert!(interpreter.is_output_truncated());
1147 assert_eq!(
1148 interpreter.output().last(),
1149 Some(&"[truncated]".to_string())
1150 );
1151 }
1152
1153 #[test]
1154 fn test_output_truncation_by_bytes() {
1155 let mut interpreter = Interpreter::new_without_env();
1156 let long_string = "x".repeat(70_000);
1158 let source = format!(r#"print("{}");"#, long_string);
1159 interpreter.run(&source).unwrap();
1160
1161 assert!(interpreter.is_output_truncated());
1163 assert_eq!(
1164 interpreter.output().last(),
1165 Some(&"[truncated]".to_string())
1166 );
1167 }
1168
1169 #[test]
1170 fn test_output_not_truncated_within_limits() {
1171 let mut interpreter = Interpreter::new_without_env();
1172 interpreter
1174 .run(r#"print("a"); print("b"); print("c");"#)
1175 .unwrap();
1176 assert!(!interpreter.is_output_truncated());
1177 assert_eq!(interpreter.output(), &["a", "b", "c"]);
1178 }
1179
1180 #[test]
1181 fn test_stack_overflow() {
1182 let mut interpreter = Interpreter::new_without_env();
1183 let source = r#"
1184 fn recurse() {
1185 recurse();
1186 }
1187 recurse();
1188 "#;
1189 let result = interpreter.run(source);
1190 assert!(matches!(
1191 result,
1192 Err(crate::FiddlerError::Runtime(
1193 RuntimeError::StackOverflow { .. }
1194 ))
1195 ));
1196 }
1197
1198 #[test]
1199 fn test_deep_recursion_within_limit() {
1200 let mut interpreter = Interpreter::new_without_env();
1201 let source = r#"
1203 fn count_down(n) {
1204 if (n <= 0) {
1205 return 0;
1206 }
1207 return 1 + count_down(n - 1);
1208 }
1209 count_down(50);
1210 "#;
1211 let result = interpreter.run(source).unwrap();
1212 assert_eq!(result, Value::Integer(50));
1213 }
1214
1215 #[test]
1216 fn test_array_literal() {
1217 let result = run("[1, 2, 3];").unwrap();
1218 assert_eq!(
1219 result,
1220 Value::Array(vec![
1221 Value::Integer(1),
1222 Value::Integer(2),
1223 Value::Integer(3),
1224 ])
1225 );
1226 }
1227
1228 #[test]
1229 fn test_empty_array_literal() {
1230 let result = run("[];").unwrap();
1231 assert_eq!(result, Value::Array(vec![]));
1232 }
1233
1234 #[test]
1235 fn test_dictionary_literal() {
1236 let result = run(r#"{"key": 42};"#).unwrap();
1237 if let Value::Dictionary(dict) = result {
1238 assert_eq!(dict.get("key"), Some(&Value::Integer(42)));
1239 } else {
1240 panic!("Expected dictionary");
1241 }
1242 }
1243
1244 #[test]
1245 fn test_empty_dictionary_literal() {
1246 let result = run("{};").unwrap();
1247 assert_eq!(result, Value::Dictionary(IndexMap::new()));
1248 }
1249
1250 #[test]
1251 fn test_nested_literals() {
1252 let result = run(r#"{"arr": [1, 2], "nested": {"a": 1}};"#).unwrap();
1253 assert!(matches!(result, Value::Dictionary(_)));
1254 }
1255
1256 #[test]
1257 fn test_dictionary_preserves_insertion_order() {
1258 let result = run(r#"{"z": 1, "a": 2, "m": 3};"#).unwrap();
1260 if let Value::Dictionary(dict) = result {
1261 let keys: Vec<&String> = dict.keys().collect();
1262 assert_eq!(keys, vec!["z", "a", "m"]);
1263 } else {
1264 panic!("Expected dictionary");
1265 }
1266 }
1267
1268 #[test]
1269 fn test_keys_preserves_insertion_order() {
1270 let mut interpreter = Interpreter::new_without_env();
1271 let result = interpreter
1272 .run(r#"let d = {"third": 3, "first": 1, "second": 2}; keys(d);"#)
1273 .unwrap();
1274 assert_eq!(
1276 result,
1277 Value::Array(vec![
1278 Value::String("third".to_string()),
1279 Value::String("first".to_string()),
1280 Value::String("second".to_string()),
1281 ])
1282 );
1283 }
1284
1285 #[test]
1288 fn test_max_integer() {
1289 let max = i64::MAX;
1290 let source = format!("{};", max);
1291 let result = run(&source).unwrap();
1292 assert_eq!(result, Value::Integer(max));
1293 }
1294
1295 #[test]
1296 fn test_min_integer() {
1297 let min_plus_one = i64::MIN + 1;
1300 let source = format!("{};", min_plus_one);
1301 let result = run(&source).unwrap();
1302 assert_eq!(result, Value::Integer(min_plus_one));
1303 }
1304
1305 #[test]
1306 fn test_unicode_strings() {
1307 let result = run(r#""Hello, 世界! 🎉 émojis";"#).unwrap();
1309 assert_eq!(result, Value::String("Hello, 世界! 🎉 émojis".to_string()));
1310 }
1311
1312 #[test]
1313 fn test_unicode_string_concatenation() {
1314 let result = run(r#""こんにちは" + " " + "世界";"#).unwrap();
1315 assert_eq!(result, Value::String("こんにちは 世界".to_string()));
1316 }
1317
1318 #[test]
1319 fn test_deeply_nested_expressions() {
1320 let result = run("((((((1 + 2) * 3) - 4) / 2) + 1) * 2);").unwrap();
1323 assert_eq!(result, Value::Integer(6));
1324 }
1325
1326 #[test]
1327 fn test_modulo_with_negative_dividend() {
1328 let result = run("-7 % 3;").unwrap();
1329 assert_eq!(result, Value::Integer(-1)); }
1331
1332 #[test]
1333 fn test_modulo_with_negative_divisor() {
1334 let result = run("7 % -3;").unwrap();
1335 assert_eq!(result, Value::Integer(1)); }
1337
1338 #[test]
1339 fn test_empty_string_falsy() {
1340 let result = run(r#"if ("") { 1; } else { 0; }"#).unwrap();
1341 assert_eq!(result, Value::Integer(0));
1342 }
1343
1344 #[test]
1345 fn test_nonempty_string_truthy() {
1346 let result = run(r#"if ("x") { 1; } else { 0; }"#).unwrap();
1347 assert_eq!(result, Value::Integer(1));
1348 }
1349
1350 #[test]
1351 fn test_zero_integer_falsy() {
1352 let result = run("if (0) { 1; } else { 0; }").unwrap();
1353 assert_eq!(result, Value::Integer(0));
1354 }
1355
1356 #[test]
1357 fn test_nonzero_integer_truthy() {
1358 let result = run("if (-1) { 1; } else { 0; }").unwrap();
1359 assert_eq!(result, Value::Integer(1));
1360 }
1361
1362 #[test]
1363 fn test_empty_array_falsy() {
1364 let mut interpreter = Interpreter::new_without_env();
1365 interpreter.set_variable_array("arr", vec![]);
1366 let result = interpreter.run("if (arr) { 1; } else { 0; }").unwrap();
1367 assert_eq!(result, Value::Integer(0));
1368 }
1369
1370 #[test]
1371 fn test_nonempty_array_truthy() {
1372 let mut interpreter = Interpreter::new_without_env();
1373 interpreter.set_variable_array("arr", vec![Value::Integer(1)]);
1374 let result = interpreter.run("if (arr) { 1; } else { 0; }").unwrap();
1375 assert_eq!(result, Value::Integer(1));
1376 }
1377
1378 #[test]
1381 fn test_method_call_string_len() {
1382 let result = run(r#""hello".len();"#).unwrap();
1383 assert_eq!(result, Value::Integer(5));
1384 }
1385
1386 #[test]
1387 fn test_method_call_string_lowercase() {
1388 let result = run(r#""HELLO".lowercase();"#).unwrap();
1389 assert_eq!(result, Value::String("hello".to_string()));
1390 }
1391
1392 #[test]
1393 fn test_method_call_string_uppercase() {
1394 let result = run(r#""hello".uppercase();"#).unwrap();
1395 assert_eq!(result, Value::String("HELLO".to_string()));
1396 }
1397
1398 #[test]
1399 fn test_method_call_array_len() {
1400 let result = run("[1, 2, 3].len();").unwrap();
1401 assert_eq!(result, Value::Integer(3));
1402 }
1403
1404 #[test]
1405 fn test_method_call_array_push() {
1406 let result = run("[1, 2].push(3);").unwrap();
1407 assert_eq!(
1408 result,
1409 Value::Array(vec![
1410 Value::Integer(1),
1411 Value::Integer(2),
1412 Value::Integer(3)
1413 ])
1414 );
1415 }
1416
1417 #[test]
1418 fn test_method_call_array_get() {
1419 let result = run(r#"["a", "b", "c"].get(1);"#).unwrap();
1420 assert_eq!(result, Value::String("b".to_string()));
1421 }
1422
1423 #[test]
1424 fn test_method_call_dict_get() {
1425 let result = run(r#"{"name": "Alice"}.get("name");"#).unwrap();
1426 assert_eq!(result, Value::String("Alice".to_string()));
1427 }
1428
1429 #[test]
1430 fn test_method_call_dict_keys() {
1431 let result = run(r#"{"a": 1, "b": 2}.keys();"#).unwrap();
1432 assert_eq!(
1433 result,
1434 Value::Array(vec![
1435 Value::String("a".to_string()),
1436 Value::String("b".to_string())
1437 ])
1438 );
1439 }
1440
1441 #[test]
1442 fn test_method_call_chaining() {
1443 let result = run(r#"" HELLO ".trim().lowercase();"#).unwrap();
1445 assert_eq!(result, Value::String("hello".to_string()));
1446 }
1447
1448 #[test]
1449 fn test_method_call_chain_array() {
1450 let result = run("[1].push(2).push(3).len();").unwrap();
1452 assert_eq!(result, Value::Integer(3));
1453 }
1454
1455 #[test]
1456 fn test_method_call_on_variable() {
1457 let result = run(r#"let s = "HELLO"; s.lowercase();"#).unwrap();
1458 assert_eq!(result, Value::String("hello".to_string()));
1459 }
1460
1461 #[test]
1462 fn test_method_call_backwards_compatibility() {
1463 let func_result = run(r#"len("hello");"#).unwrap();
1465 let method_result = run(r#""hello".len();"#).unwrap();
1466 assert_eq!(func_result, method_result);
1467 }
1468
1469 #[test]
1470 fn test_method_call_split() {
1471 let result = run(r#""a,b,c".split(",");"#).unwrap();
1472 assert_eq!(
1473 result,
1474 Value::Array(vec![
1475 Value::String("a".to_string()),
1476 Value::String("b".to_string()),
1477 Value::String("c".to_string())
1478 ])
1479 );
1480 }
1481
1482 #[test]
1483 fn test_method_call_trim_prefix() {
1484 let result = run(r#""/path/file".trim_prefix("/path");"#).unwrap();
1485 assert_eq!(result, Value::String("/file".to_string()));
1486 }
1487
1488 #[test]
1489 fn test_method_call_has_prefix() {
1490 let result = run(r#""hello world".has_prefix("hello");"#).unwrap();
1491 assert_eq!(result, Value::Boolean(true));
1492 }
1493
1494 #[test]
1495 fn test_method_call_on_grouped_expression() {
1496 let result = run(r#"("hello" + " world").len();"#).unwrap();
1497 assert_eq!(result, Value::Integer(11));
1498 }
1499
1500 #[test]
1501 fn test_method_call_undefined_method() {
1502 let result = run(r#""hello".nonexistent();"#);
1503 assert!(matches!(
1504 result,
1505 Err(crate::FiddlerError::Runtime(
1506 RuntimeError::UndefinedFunction { .. }
1507 ))
1508 ));
1509 }
1510
1511 #[test]
1512 fn test_method_call_with_function_call_args() {
1513 let result = run(r#""hello".get(len("ab"));"#).unwrap();
1515 assert_eq!(result, Value::String("l".to_string()));
1516 }
1517
1518 #[test]
1519 fn test_method_call_complex_chain() {
1520 let result = run(r#"" HELLO WORLD ".trim().lowercase().split(" ").len();"#).unwrap();
1522 assert_eq!(result, Value::Integer(2));
1523 }
1524
1525 #[test]
1526 fn test_method_call_on_function_result() {
1527 let result = run(r#"str(42).len();"#).unwrap();
1529 assert_eq!(result, Value::Integer(2));
1530 }
1531
1532 #[test]
1533 fn test_method_and_function_mixed() {
1534 let result = run(r#"len("hello".uppercase());"#).unwrap();
1536 assert_eq!(result, Value::Integer(5));
1537 }
1538
1539 #[test]
1542 fn test_array_contains_found() {
1543 let result = run(r#"[1, 2, 3].contains(2);"#).unwrap();
1544 assert_eq!(result, Value::Boolean(true));
1545 }
1546
1547 #[test]
1548 fn test_array_contains_not_found() {
1549 let result = run(r#"[1, 2, 3].contains(5);"#).unwrap();
1550 assert_eq!(result, Value::Boolean(false));
1551 }
1552
1553 #[test]
1554 fn test_array_contains_string() {
1555 let result = run(r#"["apple", "banana"].contains("banana");"#).unwrap();
1556 assert_eq!(result, Value::Boolean(true));
1557 }
1558
1559 #[test]
1560 fn test_dict_contains_key() {
1561 let result = run(r#"{"name": "Alice", "age": 30}.contains("name");"#).unwrap();
1562 assert_eq!(result, Value::Boolean(true));
1563 }
1564
1565 #[test]
1566 fn test_dict_contains_key_not_found() {
1567 let result = run(r#"{"name": "Alice"}.contains("email");"#).unwrap();
1568 assert_eq!(result, Value::Boolean(false));
1569 }
1570
1571 #[test]
1572 fn test_contains_function_syntax() {
1573 let result = run(r#"contains([1, 2, 3], 2);"#).unwrap();
1574 assert_eq!(result, Value::Boolean(true));
1575 }
1576
1577 #[test]
1580 fn test_abs_positive() {
1581 let result = run("abs(42);").unwrap();
1582 assert_eq!(result, Value::Integer(42));
1583 }
1584
1585 #[test]
1586 fn test_abs_negative() {
1587 let result = run("abs(-42);").unwrap();
1588 assert_eq!(result, Value::Integer(42));
1589 }
1590
1591 #[test]
1592 fn test_abs_method_syntax() {
1593 let result = run("let x = -10; x.abs();").unwrap();
1594 assert_eq!(result, Value::Integer(10));
1595 }
1596
1597 #[test]
1598 fn test_ceil_identity() {
1599 let result = run("ceil(42);").unwrap();
1600 assert_eq!(result, Value::Integer(42));
1601 }
1602
1603 #[test]
1604 fn test_floor_identity() {
1605 let result = run("floor(42);").unwrap();
1606 assert_eq!(result, Value::Integer(42));
1607 }
1608
1609 #[test]
1610 fn test_round_identity() {
1611 let result = run("round(42);").unwrap();
1612 assert_eq!(result, Value::Integer(42));
1613 }
1614
1615 #[test]
1616 fn test_math_method_syntax() {
1617 let result = run("42.ceil();").unwrap();
1618 assert_eq!(result, Value::Integer(42));
1619 }
1620
1621 #[test]
1624 fn test_timestamp() {
1625 let result = run("timestamp();").unwrap();
1626 if let Value::Integer(ts) = result {
1627 assert!(ts > 1577836800);
1629 } else {
1630 panic!("Expected integer");
1631 }
1632 }
1633
1634 #[test]
1635 fn test_epoch_alias() {
1636 let result = run("epoch();").unwrap();
1637 if let Value::Integer(ts) = result {
1638 assert!(ts > 1577836800);
1639 } else {
1640 panic!("Expected integer");
1641 }
1642 }
1643
1644 #[test]
1645 fn test_timestamp_millis() {
1646 let result = run("timestamp_millis();").unwrap();
1647 if let Value::Integer(ts) = result {
1648 assert!(ts > 1577836800000);
1650 } else {
1651 panic!("Expected integer");
1652 }
1653 }
1654
1655 #[test]
1656 fn test_timestamp_micros() {
1657 let result = run("timestamp_micros();").unwrap();
1658 if let Value::Integer(ts) = result {
1659 assert!(ts > 1577836800000000);
1661 } else {
1662 panic!("Expected integer");
1663 }
1664 }
1665
1666 #[test]
1667 fn test_timestamp_iso8601() {
1668 let result = run("timestamp_iso8601();").unwrap();
1669 if let Value::String(s) = result {
1670 assert!(s.contains('T'));
1672 assert!(s.contains('-'));
1673 assert!(s.contains(':'));
1674 } else {
1675 panic!("Expected string");
1676 }
1677 }
1678
1679 #[test]
1680 fn test_timestamp_in_calculation() {
1681 let result =
1683 run("let start = timestamp_millis(); let end = timestamp_millis(); end - start >= 0;")
1684 .unwrap();
1685 assert_eq!(result, Value::Boolean(true));
1686 }
1687
1688 #[test]
1691 fn test_float_literal() {
1692 let result = run("3.14;").unwrap();
1693 assert_eq!(result, Value::Float(3.14));
1694 }
1695
1696 #[test]
1697 fn test_float_arithmetic() {
1698 let result = run("3.5 + 2.0;").unwrap();
1699 assert_eq!(result, Value::Float(5.5));
1700 }
1701
1702 #[test]
1703 fn test_float_subtraction() {
1704 let result = run("10.5 - 3.2;").unwrap();
1705 if let Value::Float(f) = result {
1706 assert!((f - 7.3).abs() < 1e-10);
1707 } else {
1708 panic!("Expected float");
1709 }
1710 }
1711
1712 #[test]
1713 fn test_float_multiplication() {
1714 let result = run("2.5 * 4.0;").unwrap();
1715 assert_eq!(result, Value::Float(10.0));
1716 }
1717
1718 #[test]
1719 fn test_float_division() {
1720 let result = run("7.5 / 2.5;").unwrap();
1721 assert_eq!(result, Value::Float(3.0));
1722 }
1723
1724 #[test]
1725 fn test_mixed_arithmetic_int_float() {
1726 let result = run("10 + 3.5;").unwrap();
1727 assert_eq!(result, Value::Float(13.5));
1728 }
1729
1730 #[test]
1731 fn test_mixed_arithmetic_float_int() {
1732 let result = run("2.5 * 4;").unwrap();
1733 assert_eq!(result, Value::Float(10.0));
1734 }
1735
1736 #[test]
1737 fn test_float_comparison() {
1738 let result = run("3.14 > 3.0;").unwrap();
1739 assert_eq!(result, Value::Boolean(true));
1740 }
1741
1742 #[test]
1743 fn test_float_equality() {
1744 let result = run("2.5 == 2.5;").unwrap();
1745 assert_eq!(result, Value::Boolean(true));
1746 }
1747
1748 #[test]
1749 fn test_cross_type_equality() {
1750 let result = run("1.0 == 1;").unwrap();
1751 assert_eq!(result, Value::Boolean(true));
1752 }
1753
1754 #[test]
1755 fn test_float_negation() {
1756 let result = run("-3.14;").unwrap();
1757 assert_eq!(result, Value::Float(-3.14));
1758 }
1759
1760 #[test]
1761 fn test_float_truthy() {
1762 let result = run("if (1.5) { 1; } else { 0; }").unwrap();
1763 assert_eq!(result, Value::Integer(1));
1764 }
1765
1766 #[test]
1767 fn test_float_zero_falsy() {
1768 let result = run("if (0.0) { 1; } else { 0; }").unwrap();
1769 assert_eq!(result, Value::Integer(0));
1770 }
1771
1772 #[test]
1773 fn test_float_conversion() {
1774 let result = run("float(42);").unwrap();
1775 assert_eq!(result, Value::Float(42.0));
1776 }
1777
1778 #[test]
1779 fn test_float_conversion_from_string() {
1780 let result = run(r#"float("3.14");"#).unwrap();
1781 assert_eq!(result, Value::Float(3.14));
1782 }
1783
1784 #[test]
1785 fn test_int_from_float() {
1786 let result = run("int(3.99);").unwrap();
1787 assert_eq!(result, Value::Integer(3));
1788 }
1789
1790 #[test]
1791 fn test_ceil_float() {
1792 let result = run("ceil(3.14);").unwrap();
1793 assert_eq!(result, Value::Integer(4));
1794 }
1795
1796 #[test]
1797 fn test_floor_float() {
1798 let result = run("floor(3.99);").unwrap();
1799 assert_eq!(result, Value::Integer(3));
1800 }
1801
1802 #[test]
1803 fn test_round_float() {
1804 let result = run("round(3.5);").unwrap();
1805 assert_eq!(result, Value::Integer(4));
1806 }
1807
1808 #[test]
1809 fn test_abs_float() {
1810 let result = run("abs(-3.14);").unwrap();
1811 assert_eq!(result, Value::Float(3.14));
1812 }
1813
1814 #[test]
1815 fn test_float_method_syntax() {
1816 let result = run("let x = -2.5; x.abs();").unwrap();
1817 assert_eq!(result, Value::Float(2.5));
1818 }
1819
1820 #[test]
1821 fn test_float_in_variable() {
1822 let result = run("let pi = 3.14159; pi * 2.0;").unwrap();
1823 if let Value::Float(f) = result {
1824 assert!((f - 6.28318).abs() < 1e-5);
1825 } else {
1826 panic!("Expected float");
1827 }
1828 }
1829
1830 #[test]
1831 fn test_float_division_by_zero() {
1832 let result = run("1.0 / 0.0;").unwrap();
1833 if let Value::Float(f) = result {
1834 assert!(f.is_infinite() && f.is_sign_positive());
1835 } else {
1836 panic!("Expected float infinity");
1837 }
1838 }
1839
1840 #[test]
1841 fn test_nan_creation() {
1842 let result = run("0.0 / 0.0;").unwrap();
1843 if let Value::Float(f) = result {
1844 assert!(f.is_nan());
1845 } else {
1846 panic!("Expected NaN");
1847 }
1848 }
1849
1850 #[test]
1851 fn test_mixed_comparison() {
1852 let result = run("1 < 1.5;").unwrap();
1853 assert_eq!(result, Value::Boolean(true));
1854 }
1855}