Skip to main content

fiddler_script/
interpreter.rs

1//! Interpreter for FiddlerScript.
2//!
3//! This module contains the execution engine that evaluates the AST and
4//! produces runtime values.
5
6use 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
17/// Maximum recursion depth to prevent stack overflow.
18/// Set conservatively to avoid hitting the actual OS stack limit,
19/// especially in test environments with smaller thread stacks.
20const MAX_CALL_DEPTH: usize = 64;
21
22/// Maximum number of print output lines to capture before truncation.
23const MAX_OUTPUT_LINES: usize = 1000;
24
25/// Maximum total bytes of print output to capture before truncation.
26const MAX_OUTPUT_BYTES: usize = 64 * 1024; // 64 KB
27
28/// A user-defined function.
29#[derive(Debug, Clone)]
30struct UserFunction {
31    /// Parameter names
32    params: Vec<String>,
33    /// Function body
34    body: Block,
35}
36
37/// Control flow signal for early returns.
38enum ControlFlow {
39    /// Normal execution continues
40    Continue(Value),
41    /// Return from function with value
42    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
53/// The FiddlerScript interpreter.
54pub struct Interpreter {
55    /// Stack of variable scopes (environment)
56    scopes: Vec<HashMap<String, Value>>,
57    /// User-defined functions
58    functions: HashMap<String, UserFunction>,
59    /// Built-in functions
60    builtins: HashMap<String, fn(Vec<Value>) -> Result<Value, RuntimeError>>,
61    /// Output capture (for testing)
62    output: Vec<String>,
63    /// Total bytes captured in output
64    output_bytes: usize,
65    /// Whether output has been truncated
66    output_truncated: bool,
67    /// Current call depth for recursion limiting
68    call_depth: usize,
69}
70
71impl Default for Interpreter {
72    fn default() -> Self {
73        Self::new()
74    }
75}
76
77impl Interpreter {
78    /// Create a new interpreter with default built-in functions.
79    ///
80    /// The interpreter is initialized with all current OS environment variables
81    /// available as script variables.
82    pub fn new() -> Self {
83        Self::with_builtins(get_default_builtins())
84    }
85
86    /// Create a new interpreter with custom built-in functions.
87    ///
88    /// The interpreter is initialized with all current OS environment variables
89    /// available as script variables.
90    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        // Load all OS environment variables into the global scope
96        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    /// Create a new interpreter without loading environment variables.
112    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    /// Run FiddlerScript source code.
125    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    /// Execute a parsed program.
134    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    /// Get captured output (for testing).
148    pub fn output(&self) -> &[String] {
149        &self.output
150    }
151
152    /// Clear captured output.
153    pub fn clear_output(&mut self) {
154        self.output.clear();
155        self.output_bytes = 0;
156        self.output_truncated = false;
157    }
158
159    /// Check if captured output was truncated.
160    pub fn is_output_truncated(&self) -> bool {
161        self.output_truncated
162    }
163
164    // === Public variable access API ===
165
166    /// Set a variable by name in the global scope.
167    ///
168    /// This allows external code to inject values into the interpreter
169    /// before running a script.
170    ///
171    /// # Arguments
172    /// * `name` - The variable name
173    /// * `value` - The value to set
174    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    /// Set a variable from bytes.
181    ///
182    /// Convenience method to set a variable with byte data.
183    ///
184    /// # Arguments
185    /// * `name` - The variable name
186    /// * `bytes` - The byte data
187    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    /// Set a variable from a string.
192    ///
193    /// Convenience method to set a variable with string data.
194    ///
195    /// # Arguments
196    /// * `name` - The variable name
197    /// * `value` - The string value
198    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    /// Set a variable from an integer.
203    ///
204    /// Convenience method to set a variable with an integer value.
205    ///
206    /// # Arguments
207    /// * `name` - The variable name
208    /// * `value` - The integer value
209    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    /// Set a variable as an array.
214    ///
215    /// Convenience method to set a variable with an array value.
216    ///
217    /// # Arguments
218    /// * `name` - The variable name
219    /// * `values` - The array values
220    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    /// Set a variable as a dictionary.
225    ///
226    /// Convenience method to set a variable with a dictionary value.
227    ///
228    /// # Arguments
229    /// * `name` - The variable name
230    /// * `values` - The dictionary values
231    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    /// Get a variable by name, returning the Value type.
236    ///
237    /// Searches all scopes from innermost to outermost.
238    ///
239    /// # Arguments
240    /// * `name` - The variable name
241    ///
242    /// # Returns
243    /// * `Some(Value)` if the variable exists
244    /// * `None` if the variable is not defined
245    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    /// Get a variable by name as bytes.
255    ///
256    /// Converts the variable value to its byte representation.
257    ///
258    /// # Arguments
259    /// * `name` - The variable name
260    ///
261    /// # Returns
262    /// * `Some(Vec<u8>)` if the variable exists
263    /// * `None` if the variable is not defined
264    pub fn get_bytes(&self, name: &str) -> Option<Vec<u8>> {
265        self.get_value(name).map(|v| v.to_bytes())
266    }
267
268    /// Check if a variable exists.
269    ///
270    /// # Arguments
271    /// * `name` - The variable name
272    ///
273    /// # Returns
274    /// * `true` if the variable exists in any scope
275    /// * `false` otherwise
276    pub fn has_variable(&self, name: &str) -> bool {
277        self.get_value(name).is_some()
278    }
279
280    // === Scope management ===
281
282    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    // === Statement execution ===
316
317    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                // Execute init
354                if let Some(init_stmt) = init {
355                    self.execute_statement(init_stmt)?;
356                }
357
358                // Loop
359                loop {
360                    // Check condition
361                    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                    // Execute body
369                    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                    // Execute update
378                    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    // === Expression evaluation ===
434
435    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
442            Expression::Identifier { name, .. } => self.get_variable(name),
443
444            Expression::Binary {
445                left,
446                operator,
447                right,
448                ..
449            } => {
450                let left_val = self.evaluate_expression(left)?;
451                let right_val = self.evaluate_expression(right)?;
452                self.evaluate_binary_op(*operator, left_val, right_val)
453            }
454
455            Expression::Unary {
456                operator, operand, ..
457            } => {
458                let val = self.evaluate_expression(operand)?;
459                self.evaluate_unary_op(*operator, val)
460            }
461
462            Expression::Assignment { name, value, .. } => {
463                let val = self.evaluate_expression(value)?;
464                self.set_variable(name, val.clone())?;
465                Ok(val)
466            }
467
468            Expression::Call {
469                function,
470                arguments,
471                ..
472            } => self.call_function(function, arguments),
473
474            Expression::MethodCall {
475                receiver,
476                method,
477                arguments,
478                ..
479            } => self.call_method(receiver, method, arguments),
480
481            Expression::Grouped { expression, .. } => self.evaluate_expression(expression),
482
483            Expression::ArrayLiteral { elements, .. } => {
484                let values: Vec<Value> = elements
485                    .iter()
486                    .map(|e| self.evaluate_expression(e))
487                    .collect::<Result<_, _>>()?;
488                Ok(Value::Array(values))
489            }
490
491            Expression::DictionaryLiteral { pairs, .. } => {
492                let mut dict = IndexMap::new();
493                for (key_expr, value_expr) in pairs {
494                    let key = match self.evaluate_expression(key_expr)? {
495                        Value::String(s) => s,
496                        _ => {
497                            return Err(RuntimeError::type_mismatch(
498                                "Dictionary key must be a string",
499                            ))
500                        }
501                    };
502                    let value = self.evaluate_expression(value_expr)?;
503                    dict.insert(key, value);
504                }
505                Ok(Value::Dictionary(dict))
506            }
507        }
508    }
509
510    fn evaluate_binary_op(
511        &mut self,
512        op: BinaryOp,
513        left: Value,
514        right: Value,
515    ) -> Result<Value, RuntimeError> {
516        match op {
517            // Arithmetic operations with float support
518            BinaryOp::Add => match (&left, &right) {
519                (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a + b)),
520                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
521                (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 + b)),
522                (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a + *b as f64)),
523                (Value::String(a), Value::String(b)) => Ok(Value::String(format!("{}{}", a, b))),
524                _ => Err(RuntimeError::type_mismatch(format!(
525                    "Cannot add {:?} and {:?}",
526                    left, right
527                ))),
528            },
529            BinaryOp::Subtract => match (&left, &right) {
530                (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a - b)),
531                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
532                (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 - b)),
533                (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a - *b as f64)),
534                _ => Err(RuntimeError::type_mismatch(
535                    "Subtraction requires numeric types".to_string(),
536                )),
537            },
538            BinaryOp::Multiply => match (&left, &right) {
539                (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a * b)),
540                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
541                (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 * b)),
542                (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a * *b as f64)),
543                _ => Err(RuntimeError::type_mismatch(
544                    "Multiplication requires numeric types".to_string(),
545                )),
546            },
547            BinaryOp::Divide => match (&left, &right) {
548                (Value::Integer(_), Value::Integer(0)) => {
549                    Err(RuntimeError::DivisionByZero { position: None })
550                }
551                (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a / b)),
552                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a / b)), // IEEE 754: /0 = Inf
553                (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 / b)),
554                (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a / *b as f64)),
555                _ => Err(RuntimeError::type_mismatch(
556                    "Division requires numeric types".to_string(),
557                )),
558            },
559            BinaryOp::Modulo => match (&left, &right) {
560                (Value::Integer(_), Value::Integer(0)) => {
561                    Err(RuntimeError::DivisionByZero { position: None })
562                }
563                (Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a % b)),
564                (Value::Float(a), Value::Float(b)) => Ok(Value::Float(a % b)),
565                (Value::Integer(a), Value::Float(b)) => Ok(Value::Float(*a as f64 % b)),
566                (Value::Float(a), Value::Integer(b)) => Ok(Value::Float(a % *b as f64)),
567                _ => Err(RuntimeError::type_mismatch(
568                    "Modulo requires numeric types".to_string(),
569                )),
570            },
571
572            // Comparison
573            BinaryOp::Equal => Ok(Value::Boolean(left == right)),
574            BinaryOp::NotEqual => Ok(Value::Boolean(left != right)),
575            BinaryOp::LessThan
576            | BinaryOp::LessEqual
577            | BinaryOp::GreaterThan
578            | BinaryOp::GreaterEqual => self.evaluate_comparison(op, &left, &right),
579
580            // Logical
581            BinaryOp::And => Ok(Value::Boolean(
582                self.is_truthy(&left) && self.is_truthy(&right),
583            )),
584            BinaryOp::Or => Ok(Value::Boolean(
585                self.is_truthy(&left) || self.is_truthy(&right),
586            )),
587        }
588    }
589
590    /// Evaluate comparison operators (<, <=, >, >=).
591    fn evaluate_comparison(
592        &self,
593        op: BinaryOp,
594        left: &Value,
595        right: &Value,
596    ) -> Result<Value, RuntimeError> {
597        let result = match (left, right) {
598            (Value::Integer(a), Value::Integer(b)) => match op {
599                BinaryOp::LessThan => a < b,
600                BinaryOp::LessEqual => a <= b,
601                BinaryOp::GreaterThan => a > b,
602                BinaryOp::GreaterEqual => a >= b,
603                _ => unreachable!(),
604            },
605            (Value::Float(a), Value::Float(b)) => match op {
606                BinaryOp::LessThan => a < b,
607                BinaryOp::LessEqual => a <= b,
608                BinaryOp::GreaterThan => a > b,
609                BinaryOp::GreaterEqual => a >= b,
610                _ => unreachable!(),
611            },
612            // Mixed numeric comparisons (promote integer to float)
613            (Value::Integer(a), Value::Float(b)) => {
614                let a_float = *a as f64;
615                match op {
616                    BinaryOp::LessThan => a_float < *b,
617                    BinaryOp::LessEqual => a_float <= *b,
618                    BinaryOp::GreaterThan => a_float > *b,
619                    BinaryOp::GreaterEqual => a_float >= *b,
620                    _ => unreachable!(),
621                }
622            }
623            (Value::Float(a), Value::Integer(b)) => {
624                let b_float = *b as f64;
625                match op {
626                    BinaryOp::LessThan => *a < b_float,
627                    BinaryOp::LessEqual => *a <= b_float,
628                    BinaryOp::GreaterThan => *a > b_float,
629                    BinaryOp::GreaterEqual => *a >= b_float,
630                    _ => unreachable!(),
631                }
632            }
633            (Value::String(a), Value::String(b)) => match op {
634                BinaryOp::LessThan => a < b,
635                BinaryOp::LessEqual => a <= b,
636                BinaryOp::GreaterThan => a > b,
637                BinaryOp::GreaterEqual => a >= b,
638                _ => unreachable!(),
639            },
640            _ => {
641                return Err(RuntimeError::type_mismatch(
642                    "Comparison requires matching or numeric types".to_string(),
643                ))
644            }
645        };
646        Ok(Value::Boolean(result))
647    }
648
649    fn evaluate_unary_op(&self, op: UnaryOp, operand: Value) -> Result<Value, RuntimeError> {
650        match op {
651            UnaryOp::Not => Ok(Value::Boolean(!self.is_truthy(&operand))),
652            UnaryOp::Negate => match operand {
653                Value::Integer(n) => Ok(Value::Integer(-n)),
654                Value::Float(f) => Ok(Value::Float(-f)),
655                _ => Err(RuntimeError::type_mismatch(
656                    "Negation requires numeric type".to_string(),
657                )),
658            },
659        }
660    }
661
662    fn is_truthy(&self, value: &Value) -> bool {
663        match value {
664            Value::Boolean(b) => *b,
665            Value::Integer(n) => *n != 0,
666            Value::Float(f) => *f != 0.0 && !f.is_nan(),
667            Value::String(s) => !s.is_empty(),
668            Value::Bytes(b) => !b.is_empty(),
669            Value::Array(a) => !a.is_empty(),
670            Value::Dictionary(d) => !d.is_empty(),
671            Value::Null => false,
672        }
673    }
674
675    /// Capture output for the print function with truncation support.
676    ///
677    /// This is used for testing to capture print output. Output is truncated
678    /// if it exceeds MAX_OUTPUT_LINES or MAX_OUTPUT_BYTES.
679    fn capture_print_output(&mut self, args: &[Value]) {
680        if self.output_truncated {
681            return;
682        }
683
684        let output_str = args
685            .iter()
686            .map(|v| format!("{}", v))
687            .collect::<Vec<_>>()
688            .join(" ");
689
690        let new_bytes = self.output_bytes + output_str.len();
691        let new_lines = self.output.len() + 1;
692
693        if new_lines > MAX_OUTPUT_LINES || new_bytes > MAX_OUTPUT_BYTES {
694            self.output.push("[truncated]".to_string());
695            self.output_truncated = true;
696        } else {
697            self.output_bytes = new_bytes;
698            self.output.push(output_str);
699        }
700    }
701
702    /// Call a built-in or user-defined function by name.
703    fn call_function(
704        &mut self,
705        name: &str,
706        arguments: &[Expression],
707    ) -> Result<Value, RuntimeError> {
708        // Evaluate arguments
709        let args: Vec<Value> = arguments
710            .iter()
711            .map(|arg| self.evaluate_expression(arg))
712            .collect::<Result<_, _>>()?;
713
714        // Check for built-in function (copy the fn pointer to avoid borrow issues)
715        if let Some(&builtin) = self.builtins.get(name) {
716            if name == "print" {
717                self.capture_print_output(&args);
718            }
719            return builtin(args);
720        }
721
722        // Check for user-defined function
723        if let Some(func) = self.functions.get(name).cloned() {
724            return self.call_user_function(&func, args);
725        }
726
727        Err(RuntimeError::undefined_function(name))
728    }
729
730    /// Call a method on a receiver expression.
731    ///
732    /// This implements method call syntax as syntactic sugar over function calls.
733    /// Method calls like `receiver.method(arg1, arg2)` are transformed into regular
734    /// function calls with the receiver prepended: `method(receiver, arg1, arg2)`.
735    ///
736    /// # Examples
737    ///
738    /// ```text
739    /// "hello".len()        -> len("hello")
740    /// arr.push(42)         -> push(arr, 42)
741    /// "hi".split(",")      -> split("hi", ",")
742    /// ```
743    ///
744    /// # Arguments
745    ///
746    /// * `receiver` - The expression before the dot (e.g., `"hello"` in `"hello".len()`)
747    /// * `method` - The method name to call (e.g., `"len"`)
748    /// * `arguments` - The arguments passed to the method (empty for `len()`)
749    ///
750    /// # Returns
751    ///
752    /// Returns the result of calling the builtin or user-defined function with
753    /// the receiver prepended to the argument list.
754    ///
755    /// # Errors
756    ///
757    /// Returns `RuntimeError::UndefinedFunction` if no builtin or user function
758    /// with the given method name exists.
759    fn call_method(
760        &mut self,
761        receiver: &Expression,
762        method: &str,
763        arguments: &[Expression],
764    ) -> Result<Value, RuntimeError> {
765        // Evaluate receiver first
766        let receiver_value = self.evaluate_expression(receiver)?;
767
768        // Pre-allocate with correct capacity and build args in order
769        // This avoids the O(n) cost of insert(0, ...)
770        let mut args = Vec::with_capacity(arguments.len() + 1);
771        args.push(receiver_value);
772
773        for arg in arguments {
774            args.push(self.evaluate_expression(arg)?);
775        }
776
777        // Check for built-in function (copy the fn pointer to avoid borrow issues)
778        if let Some(&builtin) = self.builtins.get(method) {
779            if method == "print" {
780                self.capture_print_output(&args);
781            }
782            return builtin(args);
783        }
784
785        // Check for user-defined function
786        if let Some(func) = self.functions.get(method).cloned() {
787            return self.call_user_function(&func, args);
788        }
789
790        Err(RuntimeError::undefined_function(method))
791    }
792
793    /// Call a user-defined function with pre-evaluated arguments.
794    fn call_user_function(
795        &mut self,
796        func: &UserFunction,
797        args: Vec<Value>,
798    ) -> Result<Value, RuntimeError> {
799        // Check recursion depth
800        if self.call_depth >= MAX_CALL_DEPTH {
801            return Err(RuntimeError::StackOverflow {
802                max_depth: MAX_CALL_DEPTH,
803            });
804        }
805
806        if args.len() != func.params.len() {
807            return Err(RuntimeError::WrongArgumentCount {
808                expected: func.params.len(),
809                actual: args.len(),
810                position: None,
811            });
812        }
813
814        // Increment call depth
815        self.call_depth += 1;
816
817        // Create new scope for function
818        self.push_scope();
819
820        // Bind arguments to parameters
821        for (param, arg) in func.params.iter().zip(args) {
822            self.define_variable(param.clone(), arg);
823        }
824
825        // Execute function body
826        let result = self.execute_block(&func.body);
827
828        self.pop_scope();
829
830        // Decrement call depth (even on error)
831        self.call_depth -= 1;
832
833        result.map(|cf| cf.into_value())
834    }
835}
836
837#[cfg(test)]
838mod tests {
839    use super::*;
840
841    fn run(source: &str) -> Result<Value, crate::FiddlerError> {
842        let mut interpreter = Interpreter::new();
843        interpreter.run(source)
844    }
845
846    #[test]
847    fn test_integer_literal() {
848        assert_eq!(run("42;").unwrap(), Value::Integer(42));
849    }
850
851    #[test]
852    fn test_string_literal() {
853        assert_eq!(
854            run(r#""hello";"#).unwrap(),
855            Value::String("hello".to_string())
856        );
857    }
858
859    #[test]
860    fn test_boolean_literal() {
861        assert_eq!(run("true;").unwrap(), Value::Boolean(true));
862        assert_eq!(run("false;").unwrap(), Value::Boolean(false));
863    }
864
865    #[test]
866    fn test_arithmetic() {
867        assert_eq!(run("5 + 3;").unwrap(), Value::Integer(8));
868        assert_eq!(run("5 - 3;").unwrap(), Value::Integer(2));
869        assert_eq!(run("5 * 3;").unwrap(), Value::Integer(15));
870        assert_eq!(run("6 / 2;").unwrap(), Value::Integer(3));
871        assert_eq!(run("7 % 3;").unwrap(), Value::Integer(1));
872    }
873
874    #[test]
875    fn test_string_concatenation() {
876        assert_eq!(
877            run(r#""hello" + " " + "world";"#).unwrap(),
878            Value::String("hello world".to_string())
879        );
880    }
881
882    #[test]
883    fn test_comparison() {
884        assert_eq!(run("5 > 3;").unwrap(), Value::Boolean(true));
885        assert_eq!(run("5 < 3;").unwrap(), Value::Boolean(false));
886        assert_eq!(run("5 == 5;").unwrap(), Value::Boolean(true));
887        assert_eq!(run("5 != 3;").unwrap(), Value::Boolean(true));
888    }
889
890    #[test]
891    fn test_logical() {
892        assert_eq!(run("true && true;").unwrap(), Value::Boolean(true));
893        assert_eq!(run("true && false;").unwrap(), Value::Boolean(false));
894        assert_eq!(run("true || false;").unwrap(), Value::Boolean(true));
895        assert_eq!(run("!true;").unwrap(), Value::Boolean(false));
896    }
897
898    #[test]
899    fn test_variable() {
900        assert_eq!(run("let x = 10; x;").unwrap(), Value::Integer(10));
901    }
902
903    #[test]
904    fn test_variable_assignment() {
905        assert_eq!(run("let x = 10; x = 20; x;").unwrap(), Value::Integer(20));
906    }
907
908    #[test]
909    fn test_if_true() {
910        assert_eq!(
911            run("let x = 0; if (true) { x = 1; } x;").unwrap(),
912            Value::Integer(1)
913        );
914    }
915
916    #[test]
917    fn test_if_false() {
918        assert_eq!(
919            run("let x = 0; if (false) { x = 1; } x;").unwrap(),
920            Value::Integer(0)
921        );
922    }
923
924    #[test]
925    fn test_if_else() {
926        assert_eq!(
927            run("let x = 0; if (false) { x = 1; } else { x = 2; } x;").unwrap(),
928            Value::Integer(2)
929        );
930    }
931
932    #[test]
933    fn test_for_loop() {
934        assert_eq!(
935            run("let sum = 0; for (let i = 0; i < 5; i = i + 1) { sum = sum + i; } sum;").unwrap(),
936            Value::Integer(10) // 0 + 1 + 2 + 3 + 4
937        );
938    }
939
940    #[test]
941    fn test_function() {
942        assert_eq!(
943            run("fn add(a, b) { return a + b; } add(2, 3);").unwrap(),
944            Value::Integer(5)
945        );
946    }
947
948    #[test]
949    fn test_recursion() {
950        let source = r#"
951            fn factorial(n) {
952                if (n <= 1) {
953                    return 1;
954                }
955                return n * factorial(n - 1);
956            }
957            factorial(5);
958        "#;
959        assert_eq!(run(source).unwrap(), Value::Integer(120));
960    }
961
962    #[test]
963    fn test_division_by_zero() {
964        let result = run("5 / 0;");
965        assert!(matches!(
966            result,
967            Err(crate::FiddlerError::Runtime(RuntimeError::DivisionByZero {
968                position: None
969            }))
970        ));
971    }
972
973    #[test]
974    fn test_undefined_variable() {
975        let result = run("x;");
976        assert!(matches!(
977            result,
978            Err(crate::FiddlerError::Runtime(
979                RuntimeError::UndefinedVariable { .. }
980            ))
981        ));
982    }
983
984    #[test]
985    fn test_undefined_function() {
986        let result = run("foo();");
987        assert!(matches!(
988            result,
989            Err(crate::FiddlerError::Runtime(
990                RuntimeError::UndefinedFunction { .. }
991            ))
992        ));
993    }
994
995    #[test]
996    fn test_wrong_argument_count() {
997        let result = run("fn add(a, b) { return a + b; } add(1);");
998        assert!(matches!(
999            result,
1000            Err(crate::FiddlerError::Runtime(
1001                RuntimeError::WrongArgumentCount {
1002                    expected: 2,
1003                    actual: 1,
1004                    ..
1005                }
1006            ))
1007        ));
1008    }
1009
1010    #[test]
1011    fn test_set_and_get_variable() {
1012        let mut interpreter = Interpreter::new_without_env();
1013        interpreter.set_variable_value("x", Value::Integer(42));
1014        assert_eq!(interpreter.get_value("x"), Some(Value::Integer(42)));
1015    }
1016
1017    #[test]
1018    fn test_set_variable_string() {
1019        let mut interpreter = Interpreter::new_without_env();
1020        interpreter.set_variable_string("name", "Alice");
1021        assert_eq!(
1022            interpreter.get_value("name"),
1023            Some(Value::String("Alice".to_string()))
1024        );
1025    }
1026
1027    #[test]
1028    fn test_set_variable_bytes() {
1029        let mut interpreter = Interpreter::new_without_env();
1030        interpreter.set_variable_bytes("data", vec![1, 2, 3]);
1031        assert_eq!(
1032            interpreter.get_value("data"),
1033            Some(Value::Bytes(vec![1, 2, 3]))
1034        );
1035    }
1036
1037    #[test]
1038    fn test_get_bytes() {
1039        let mut interpreter = Interpreter::new_without_env();
1040        interpreter.set_variable_string("msg", "hello");
1041        assert_eq!(
1042            interpreter.get_bytes("msg"),
1043            Some("hello".as_bytes().to_vec())
1044        );
1045    }
1046
1047    #[test]
1048    fn test_has_variable() {
1049        let mut interpreter = Interpreter::new_without_env();
1050        interpreter.set_variable_int("count", 10);
1051        assert!(interpreter.has_variable("count"));
1052        assert!(!interpreter.has_variable("nonexistent"));
1053    }
1054
1055    #[test]
1056    fn test_env_vars_loaded() {
1057        std::env::set_var("FIDDLER_TEST_VAR", "test123");
1058        let interpreter = Interpreter::new();
1059        assert_eq!(
1060            interpreter.get_value("FIDDLER_TEST_VAR"),
1061            Some(Value::String("test123".to_string()))
1062        );
1063        std::env::remove_var("FIDDLER_TEST_VAR");
1064    }
1065
1066    #[test]
1067    fn test_use_injected_variable_in_script() {
1068        let mut interpreter = Interpreter::new_without_env();
1069        interpreter.set_variable_int("input", 5);
1070        let result = interpreter.run("input * 2;").unwrap();
1071        assert_eq!(result, Value::Integer(10));
1072    }
1073
1074    #[test]
1075    fn test_bytes_in_script() {
1076        let mut interpreter = Interpreter::new_without_env();
1077        interpreter.set_variable_bytes("data", b"hello".to_vec());
1078        let result = interpreter.run("bytes_to_string(data);").unwrap();
1079        assert_eq!(result, Value::String("hello".to_string()));
1080    }
1081
1082    #[test]
1083    fn test_parse_json_in_script() {
1084        let mut interpreter = Interpreter::new_without_env();
1085        interpreter.set_variable_bytes("json_data", br#"{"name": "test"}"#.to_vec());
1086        let result = interpreter.run("parse_json(json_data);").unwrap();
1087        assert!(matches!(result, Value::Dictionary(_)));
1088    }
1089
1090    #[test]
1091    fn test_bytes_truthy() {
1092        let mut interpreter = Interpreter::new_without_env();
1093        interpreter.set_variable_bytes("data", b"hello".to_vec());
1094        let result = interpreter.run("if (data) { 1; } else { 0; }").unwrap();
1095        assert_eq!(result, Value::Integer(1));
1096    }
1097
1098    #[test]
1099    fn test_empty_bytes_falsy() {
1100        let mut interpreter = Interpreter::new_without_env();
1101        interpreter.set_variable_bytes("empty", vec![]);
1102        let result = interpreter.run("if (empty) { 1; } else { 0; }").unwrap();
1103        assert_eq!(result, Value::Integer(0));
1104    }
1105
1106    #[test]
1107    fn test_print_output_capture() {
1108        let mut interpreter = Interpreter::new_without_env();
1109        interpreter
1110            .run(r#"print("hello"); print("world");"#)
1111            .unwrap();
1112        assert_eq!(interpreter.output(), &["hello", "world"]);
1113    }
1114
1115    #[test]
1116    fn test_print_output_multiple_args() {
1117        let mut interpreter = Interpreter::new_without_env();
1118        interpreter.run(r#"print("a", 42, true);"#).unwrap();
1119        assert_eq!(interpreter.output(), &["a 42 true"]);
1120    }
1121
1122    #[test]
1123    fn test_clear_output() {
1124        let mut interpreter = Interpreter::new_without_env();
1125        interpreter.run(r#"print("test");"#).unwrap();
1126        assert_eq!(interpreter.output().len(), 1);
1127        assert!(!interpreter.is_output_truncated());
1128        interpreter.clear_output();
1129        assert!(interpreter.output().is_empty());
1130        assert!(!interpreter.is_output_truncated());
1131    }
1132
1133    #[test]
1134    fn test_output_truncation_by_lines() {
1135        let mut interpreter = Interpreter::new_without_env();
1136        // Generate more than MAX_OUTPUT_LINES (1000) print statements
1137        let mut source = String::new();
1138        for i in 0..1005 {
1139            source.push_str(&format!("print({});\n", i));
1140        }
1141        interpreter.run(&source).unwrap();
1142
1143        // Should have MAX_OUTPUT_LINES entries plus "[truncated]"
1144        assert!(interpreter.output().len() <= MAX_OUTPUT_LINES + 1);
1145        assert!(interpreter.is_output_truncated());
1146        assert_eq!(
1147            interpreter.output().last(),
1148            Some(&"[truncated]".to_string())
1149        );
1150    }
1151
1152    #[test]
1153    fn test_output_truncation_by_bytes() {
1154        let mut interpreter = Interpreter::new_without_env();
1155        // Generate a long string that exceeds MAX_OUTPUT_BYTES (64KB)
1156        let long_string = "x".repeat(70_000);
1157        let source = format!(r#"print("{}");"#, long_string);
1158        interpreter.run(&source).unwrap();
1159
1160        // The single line exceeds the byte limit, so it should be truncated
1161        assert!(interpreter.is_output_truncated());
1162        assert_eq!(
1163            interpreter.output().last(),
1164            Some(&"[truncated]".to_string())
1165        );
1166    }
1167
1168    #[test]
1169    fn test_output_not_truncated_within_limits() {
1170        let mut interpreter = Interpreter::new_without_env();
1171        // Just a few print statements should not trigger truncation
1172        interpreter
1173            .run(r#"print("a"); print("b"); print("c");"#)
1174            .unwrap();
1175        assert!(!interpreter.is_output_truncated());
1176        assert_eq!(interpreter.output(), &["a", "b", "c"]);
1177    }
1178
1179    #[test]
1180    fn test_stack_overflow() {
1181        let mut interpreter = Interpreter::new_without_env();
1182        let source = r#"
1183            fn recurse() {
1184                recurse();
1185            }
1186            recurse();
1187        "#;
1188        let result = interpreter.run(source);
1189        assert!(matches!(
1190            result,
1191            Err(crate::FiddlerError::Runtime(
1192                RuntimeError::StackOverflow { .. }
1193            ))
1194        ));
1195    }
1196
1197    #[test]
1198    fn test_deep_recursion_within_limit() {
1199        let mut interpreter = Interpreter::new_without_env();
1200        // Test that reasonable recursion depth works (50 levels, well under MAX_CALL_DEPTH of 64)
1201        let source = r#"
1202            fn count_down(n) {
1203                if (n <= 0) {
1204                    return 0;
1205                }
1206                return 1 + count_down(n - 1);
1207            }
1208            count_down(50);
1209        "#;
1210        let result = interpreter.run(source).unwrap();
1211        assert_eq!(result, Value::Integer(50));
1212    }
1213
1214    #[test]
1215    fn test_array_literal() {
1216        let result = run("[1, 2, 3];").unwrap();
1217        assert_eq!(
1218            result,
1219            Value::Array(vec![
1220                Value::Integer(1),
1221                Value::Integer(2),
1222                Value::Integer(3),
1223            ])
1224        );
1225    }
1226
1227    #[test]
1228    fn test_empty_array_literal() {
1229        let result = run("[];").unwrap();
1230        assert_eq!(result, Value::Array(vec![]));
1231    }
1232
1233    #[test]
1234    fn test_dictionary_literal() {
1235        let result = run(r#"{"key": 42};"#).unwrap();
1236        if let Value::Dictionary(dict) = result {
1237            assert_eq!(dict.get("key"), Some(&Value::Integer(42)));
1238        } else {
1239            panic!("Expected dictionary");
1240        }
1241    }
1242
1243    #[test]
1244    fn test_empty_dictionary_literal() {
1245        let result = run("{};").unwrap();
1246        assert_eq!(result, Value::Dictionary(IndexMap::new()));
1247    }
1248
1249    #[test]
1250    fn test_nested_literals() {
1251        let result = run(r#"{"arr": [1, 2], "nested": {"a": 1}};"#).unwrap();
1252        assert!(matches!(result, Value::Dictionary(_)));
1253    }
1254
1255    #[test]
1256    fn test_dictionary_preserves_insertion_order() {
1257        // Test that dictionary maintains insertion order (IndexMap behavior)
1258        let result = run(r#"{"z": 1, "a": 2, "m": 3};"#).unwrap();
1259        if let Value::Dictionary(dict) = result {
1260            let keys: Vec<&String> = dict.keys().collect();
1261            assert_eq!(keys, vec!["z", "a", "m"]);
1262        } else {
1263            panic!("Expected dictionary");
1264        }
1265    }
1266
1267    #[test]
1268    fn test_keys_preserves_insertion_order() {
1269        let mut interpreter = Interpreter::new_without_env();
1270        let result = interpreter
1271            .run(r#"let d = {"third": 3, "first": 1, "second": 2}; keys(d);"#)
1272            .unwrap();
1273        // Keys should be in insertion order
1274        assert_eq!(
1275            result,
1276            Value::Array(vec![
1277                Value::String("third".to_string()),
1278                Value::String("first".to_string()),
1279                Value::String("second".to_string()),
1280            ])
1281        );
1282    }
1283
1284    // Edge case tests (#15)
1285
1286    #[test]
1287    fn test_max_integer() {
1288        let max = i64::MAX;
1289        let source = format!("{};", max);
1290        let result = run(&source).unwrap();
1291        assert_eq!(result, Value::Integer(max));
1292    }
1293
1294    #[test]
1295    fn test_min_integer() {
1296        // Note: -9223372036854775808 would be parsed as negate(9223372036854775808)
1297        // which overflows, so we use a slightly different approach
1298        let min_plus_one = i64::MIN + 1;
1299        let source = format!("{};", min_plus_one);
1300        let result = run(&source).unwrap();
1301        assert_eq!(result, Value::Integer(min_plus_one));
1302    }
1303
1304    #[test]
1305    fn test_unicode_strings() {
1306        // Test various Unicode characters
1307        let result = run(r#""Hello, 世界! 🎉 émojis";"#).unwrap();
1308        assert_eq!(result, Value::String("Hello, 世界! 🎉 émojis".to_string()));
1309    }
1310
1311    #[test]
1312    fn test_unicode_string_concatenation() {
1313        let result = run(r#""こんにちは" + " " + "世界";"#).unwrap();
1314        assert_eq!(result, Value::String("こんにちは 世界".to_string()));
1315    }
1316
1317    #[test]
1318    fn test_deeply_nested_expressions() {
1319        // Test deeply nested parenthesized expressions
1320        // (1 + 2) = 3, * 3 = 9, - 4 = 5, / 2 = 2, + 1 = 3, * 2 = 6
1321        let result = run("((((((1 + 2) * 3) - 4) / 2) + 1) * 2);").unwrap();
1322        assert_eq!(result, Value::Integer(6));
1323    }
1324
1325    #[test]
1326    fn test_modulo_with_negative_dividend() {
1327        let result = run("-7 % 3;").unwrap();
1328        assert_eq!(result, Value::Integer(-1)); // Rust semantics: -7 % 3 = -1
1329    }
1330
1331    #[test]
1332    fn test_modulo_with_negative_divisor() {
1333        let result = run("7 % -3;").unwrap();
1334        assert_eq!(result, Value::Integer(1)); // Rust semantics: 7 % -3 = 1
1335    }
1336
1337    #[test]
1338    fn test_empty_string_falsy() {
1339        let result = run(r#"if ("") { 1; } else { 0; }"#).unwrap();
1340        assert_eq!(result, Value::Integer(0));
1341    }
1342
1343    #[test]
1344    fn test_nonempty_string_truthy() {
1345        let result = run(r#"if ("x") { 1; } else { 0; }"#).unwrap();
1346        assert_eq!(result, Value::Integer(1));
1347    }
1348
1349    #[test]
1350    fn test_zero_integer_falsy() {
1351        let result = run("if (0) { 1; } else { 0; }").unwrap();
1352        assert_eq!(result, Value::Integer(0));
1353    }
1354
1355    #[test]
1356    fn test_nonzero_integer_truthy() {
1357        let result = run("if (-1) { 1; } else { 0; }").unwrap();
1358        assert_eq!(result, Value::Integer(1));
1359    }
1360
1361    #[test]
1362    fn test_empty_array_falsy() {
1363        let mut interpreter = Interpreter::new_without_env();
1364        interpreter.set_variable_array("arr", vec![]);
1365        let result = interpreter.run("if (arr) { 1; } else { 0; }").unwrap();
1366        assert_eq!(result, Value::Integer(0));
1367    }
1368
1369    #[test]
1370    fn test_nonempty_array_truthy() {
1371        let mut interpreter = Interpreter::new_without_env();
1372        interpreter.set_variable_array("arr", vec![Value::Integer(1)]);
1373        let result = interpreter.run("if (arr) { 1; } else { 0; }").unwrap();
1374        assert_eq!(result, Value::Integer(1));
1375    }
1376
1377    // Method call syntax tests
1378
1379    #[test]
1380    fn test_method_call_string_len() {
1381        let result = run(r#""hello".len();"#).unwrap();
1382        assert_eq!(result, Value::Integer(5));
1383    }
1384
1385    #[test]
1386    fn test_method_call_string_lowercase() {
1387        let result = run(r#""HELLO".lowercase();"#).unwrap();
1388        assert_eq!(result, Value::String("hello".to_string()));
1389    }
1390
1391    #[test]
1392    fn test_method_call_string_uppercase() {
1393        let result = run(r#""hello".uppercase();"#).unwrap();
1394        assert_eq!(result, Value::String("HELLO".to_string()));
1395    }
1396
1397    #[test]
1398    fn test_method_call_array_len() {
1399        let result = run("[1, 2, 3].len();").unwrap();
1400        assert_eq!(result, Value::Integer(3));
1401    }
1402
1403    #[test]
1404    fn test_method_call_array_push() {
1405        let result = run("[1, 2].push(3);").unwrap();
1406        assert_eq!(
1407            result,
1408            Value::Array(vec![
1409                Value::Integer(1),
1410                Value::Integer(2),
1411                Value::Integer(3)
1412            ])
1413        );
1414    }
1415
1416    #[test]
1417    fn test_method_call_array_get() {
1418        let result = run(r#"["a", "b", "c"].get(1);"#).unwrap();
1419        assert_eq!(result, Value::String("b".to_string()));
1420    }
1421
1422    #[test]
1423    fn test_method_call_dict_get() {
1424        let result = run(r#"{"name": "Alice"}.get("name");"#).unwrap();
1425        assert_eq!(result, Value::String("Alice".to_string()));
1426    }
1427
1428    #[test]
1429    fn test_method_call_dict_keys() {
1430        let result = run(r#"{"a": 1, "b": 2}.keys();"#).unwrap();
1431        assert_eq!(
1432            result,
1433            Value::Array(vec![
1434                Value::String("a".to_string()),
1435                Value::String("b".to_string())
1436            ])
1437        );
1438    }
1439
1440    #[test]
1441    fn test_method_call_chaining() {
1442        // Test method chaining: trim then lowercase
1443        let result = run(r#""  HELLO  ".trim().lowercase();"#).unwrap();
1444        assert_eq!(result, Value::String("hello".to_string()));
1445    }
1446
1447    #[test]
1448    fn test_method_call_chain_array() {
1449        // Test array method chaining: push twice then get length
1450        let result = run("[1].push(2).push(3).len();").unwrap();
1451        assert_eq!(result, Value::Integer(3));
1452    }
1453
1454    #[test]
1455    fn test_method_call_on_variable() {
1456        let result = run(r#"let s = "HELLO"; s.lowercase();"#).unwrap();
1457        assert_eq!(result, Value::String("hello".to_string()));
1458    }
1459
1460    #[test]
1461    fn test_method_call_backwards_compatibility() {
1462        // Both syntaxes should produce the same result
1463        let func_result = run(r#"len("hello");"#).unwrap();
1464        let method_result = run(r#""hello".len();"#).unwrap();
1465        assert_eq!(func_result, method_result);
1466    }
1467
1468    #[test]
1469    fn test_method_call_split() {
1470        let result = run(r#""a,b,c".split(",");"#).unwrap();
1471        assert_eq!(
1472            result,
1473            Value::Array(vec![
1474                Value::String("a".to_string()),
1475                Value::String("b".to_string()),
1476                Value::String("c".to_string())
1477            ])
1478        );
1479    }
1480
1481    #[test]
1482    fn test_method_call_trim_prefix() {
1483        let result = run(r#""/path/file".trim_prefix("/path");"#).unwrap();
1484        assert_eq!(result, Value::String("/file".to_string()));
1485    }
1486
1487    #[test]
1488    fn test_method_call_has_prefix() {
1489        let result = run(r#""hello world".has_prefix("hello");"#).unwrap();
1490        assert_eq!(result, Value::Boolean(true));
1491    }
1492
1493    #[test]
1494    fn test_method_call_on_grouped_expression() {
1495        let result = run(r#"("hello" + " world").len();"#).unwrap();
1496        assert_eq!(result, Value::Integer(11));
1497    }
1498
1499    #[test]
1500    fn test_method_call_undefined_method() {
1501        let result = run(r#""hello".nonexistent();"#);
1502        assert!(matches!(
1503            result,
1504            Err(crate::FiddlerError::Runtime(
1505                RuntimeError::UndefinedFunction { .. }
1506            ))
1507        ));
1508    }
1509
1510    #[test]
1511    fn test_method_call_with_function_call_args() {
1512        // Test method with arguments that are function calls
1513        let result = run(r#""hello".get(len("ab"));"#).unwrap();
1514        assert_eq!(result, Value::String("l".to_string()));
1515    }
1516
1517    #[test]
1518    fn test_method_call_complex_chain() {
1519        // Test deeply nested method chains
1520        let result = run(r#""  HELLO WORLD  ".trim().lowercase().split(" ").len();"#).unwrap();
1521        assert_eq!(result, Value::Integer(2));
1522    }
1523
1524    #[test]
1525    fn test_method_call_on_function_result() {
1526        // Call method on result of a function call
1527        let result = run(r#"str(42).len();"#).unwrap();
1528        assert_eq!(result, Value::Integer(2));
1529    }
1530
1531    #[test]
1532    fn test_method_and_function_mixed() {
1533        // Mix method and function syntax in same expression
1534        let result = run(r#"len("hello".uppercase());"#).unwrap();
1535        assert_eq!(result, Value::Integer(5));
1536    }
1537
1538    // === Contains tests ===
1539
1540    #[test]
1541    fn test_array_contains_found() {
1542        let result = run(r#"[1, 2, 3].contains(2);"#).unwrap();
1543        assert_eq!(result, Value::Boolean(true));
1544    }
1545
1546    #[test]
1547    fn test_array_contains_not_found() {
1548        let result = run(r#"[1, 2, 3].contains(5);"#).unwrap();
1549        assert_eq!(result, Value::Boolean(false));
1550    }
1551
1552    #[test]
1553    fn test_array_contains_string() {
1554        let result = run(r#"["apple", "banana"].contains("banana");"#).unwrap();
1555        assert_eq!(result, Value::Boolean(true));
1556    }
1557
1558    #[test]
1559    fn test_dict_contains_key() {
1560        let result = run(r#"{"name": "Alice", "age": 30}.contains("name");"#).unwrap();
1561        assert_eq!(result, Value::Boolean(true));
1562    }
1563
1564    #[test]
1565    fn test_dict_contains_key_not_found() {
1566        let result = run(r#"{"name": "Alice"}.contains("email");"#).unwrap();
1567        assert_eq!(result, Value::Boolean(false));
1568    }
1569
1570    #[test]
1571    fn test_contains_function_syntax() {
1572        let result = run(r#"contains([1, 2, 3], 2);"#).unwrap();
1573        assert_eq!(result, Value::Boolean(true));
1574    }
1575
1576    // === Math function tests ===
1577
1578    #[test]
1579    fn test_abs_positive() {
1580        let result = run("abs(42);").unwrap();
1581        assert_eq!(result, Value::Integer(42));
1582    }
1583
1584    #[test]
1585    fn test_abs_negative() {
1586        let result = run("abs(-42);").unwrap();
1587        assert_eq!(result, Value::Integer(42));
1588    }
1589
1590    #[test]
1591    fn test_abs_method_syntax() {
1592        let result = run("let x = -10; x.abs();").unwrap();
1593        assert_eq!(result, Value::Integer(10));
1594    }
1595
1596    #[test]
1597    fn test_ceil_identity() {
1598        let result = run("ceil(42);").unwrap();
1599        assert_eq!(result, Value::Integer(42));
1600    }
1601
1602    #[test]
1603    fn test_floor_identity() {
1604        let result = run("floor(42);").unwrap();
1605        assert_eq!(result, Value::Integer(42));
1606    }
1607
1608    #[test]
1609    fn test_round_identity() {
1610        let result = run("round(42);").unwrap();
1611        assert_eq!(result, Value::Integer(42));
1612    }
1613
1614    #[test]
1615    fn test_math_method_syntax() {
1616        let result = run("42.ceil();").unwrap();
1617        assert_eq!(result, Value::Integer(42));
1618    }
1619
1620    // === Timestamp function tests ===
1621
1622    #[test]
1623    fn test_timestamp() {
1624        let result = run("timestamp();").unwrap();
1625        if let Value::Integer(ts) = result {
1626            // Should be after Jan 1, 2020
1627            assert!(ts > 1577836800);
1628        } else {
1629            panic!("Expected integer");
1630        }
1631    }
1632
1633    #[test]
1634    fn test_epoch_alias() {
1635        let result = run("epoch();").unwrap();
1636        if let Value::Integer(ts) = result {
1637            assert!(ts > 1577836800);
1638        } else {
1639            panic!("Expected integer");
1640        }
1641    }
1642
1643    #[test]
1644    fn test_timestamp_millis() {
1645        let result = run("timestamp_millis();").unwrap();
1646        if let Value::Integer(ts) = result {
1647            // Should be after Jan 1, 2020 in milliseconds
1648            assert!(ts > 1577836800000);
1649        } else {
1650            panic!("Expected integer");
1651        }
1652    }
1653
1654    #[test]
1655    fn test_timestamp_micros() {
1656        let result = run("timestamp_micros();").unwrap();
1657        if let Value::Integer(ts) = result {
1658            // Should be after Jan 1, 2020 in microseconds
1659            assert!(ts > 1577836800000000);
1660        } else {
1661            panic!("Expected integer");
1662        }
1663    }
1664
1665    #[test]
1666    fn test_timestamp_iso8601() {
1667        let result = run("timestamp_iso8601();").unwrap();
1668        if let Value::String(s) = result {
1669            // Should be ISO 8601 format
1670            assert!(s.contains('T'));
1671            assert!(s.contains('-'));
1672            assert!(s.contains(':'));
1673        } else {
1674            panic!("Expected string");
1675        }
1676    }
1677
1678    #[test]
1679    fn test_timestamp_in_calculation() {
1680        // Test that timestamps can be used in arithmetic
1681        let result =
1682            run("let start = timestamp_millis(); let end = timestamp_millis(); end - start >= 0;")
1683                .unwrap();
1684        assert_eq!(result, Value::Boolean(true));
1685    }
1686
1687    // === Float tests ===
1688
1689    #[test]
1690    fn test_float_literal() {
1691        let result = run("3.14;").unwrap();
1692        assert_eq!(result, Value::Float(3.14));
1693    }
1694
1695    #[test]
1696    fn test_float_arithmetic() {
1697        let result = run("3.5 + 2.0;").unwrap();
1698        assert_eq!(result, Value::Float(5.5));
1699    }
1700
1701    #[test]
1702    fn test_float_subtraction() {
1703        let result = run("10.5 - 3.2;").unwrap();
1704        if let Value::Float(f) = result {
1705            assert!((f - 7.3).abs() < 1e-10);
1706        } else {
1707            panic!("Expected float");
1708        }
1709    }
1710
1711    #[test]
1712    fn test_float_multiplication() {
1713        let result = run("2.5 * 4.0;").unwrap();
1714        assert_eq!(result, Value::Float(10.0));
1715    }
1716
1717    #[test]
1718    fn test_float_division() {
1719        let result = run("7.5 / 2.5;").unwrap();
1720        assert_eq!(result, Value::Float(3.0));
1721    }
1722
1723    #[test]
1724    fn test_mixed_arithmetic_int_float() {
1725        let result = run("10 + 3.5;").unwrap();
1726        assert_eq!(result, Value::Float(13.5));
1727    }
1728
1729    #[test]
1730    fn test_mixed_arithmetic_float_int() {
1731        let result = run("2.5 * 4;").unwrap();
1732        assert_eq!(result, Value::Float(10.0));
1733    }
1734
1735    #[test]
1736    fn test_float_comparison() {
1737        let result = run("3.14 > 3.0;").unwrap();
1738        assert_eq!(result, Value::Boolean(true));
1739    }
1740
1741    #[test]
1742    fn test_float_equality() {
1743        let result = run("2.5 == 2.5;").unwrap();
1744        assert_eq!(result, Value::Boolean(true));
1745    }
1746
1747    #[test]
1748    fn test_cross_type_equality() {
1749        let result = run("1.0 == 1;").unwrap();
1750        assert_eq!(result, Value::Boolean(true));
1751    }
1752
1753    #[test]
1754    fn test_float_negation() {
1755        let result = run("-3.14;").unwrap();
1756        assert_eq!(result, Value::Float(-3.14));
1757    }
1758
1759    #[test]
1760    fn test_float_truthy() {
1761        let result = run("if (1.5) { 1; } else { 0; }").unwrap();
1762        assert_eq!(result, Value::Integer(1));
1763    }
1764
1765    #[test]
1766    fn test_float_zero_falsy() {
1767        let result = run("if (0.0) { 1; } else { 0; }").unwrap();
1768        assert_eq!(result, Value::Integer(0));
1769    }
1770
1771    #[test]
1772    fn test_float_conversion() {
1773        let result = run("float(42);").unwrap();
1774        assert_eq!(result, Value::Float(42.0));
1775    }
1776
1777    #[test]
1778    fn test_float_conversion_from_string() {
1779        let result = run(r#"float("3.14");"#).unwrap();
1780        assert_eq!(result, Value::Float(3.14));
1781    }
1782
1783    #[test]
1784    fn test_int_from_float() {
1785        let result = run("int(3.99);").unwrap();
1786        assert_eq!(result, Value::Integer(3));
1787    }
1788
1789    #[test]
1790    fn test_ceil_float() {
1791        let result = run("ceil(3.14);").unwrap();
1792        assert_eq!(result, Value::Integer(4));
1793    }
1794
1795    #[test]
1796    fn test_floor_float() {
1797        let result = run("floor(3.99);").unwrap();
1798        assert_eq!(result, Value::Integer(3));
1799    }
1800
1801    #[test]
1802    fn test_round_float() {
1803        let result = run("round(3.5);").unwrap();
1804        assert_eq!(result, Value::Integer(4));
1805    }
1806
1807    #[test]
1808    fn test_abs_float() {
1809        let result = run("abs(-3.14);").unwrap();
1810        assert_eq!(result, Value::Float(3.14));
1811    }
1812
1813    #[test]
1814    fn test_float_method_syntax() {
1815        let result = run("let x = -2.5; x.abs();").unwrap();
1816        assert_eq!(result, Value::Float(2.5));
1817    }
1818
1819    #[test]
1820    fn test_float_in_variable() {
1821        let result = run("let pi = 3.14159; pi * 2.0;").unwrap();
1822        if let Value::Float(f) = result {
1823            assert!((f - 6.28318).abs() < 1e-5);
1824        } else {
1825            panic!("Expected float");
1826        }
1827    }
1828
1829    #[test]
1830    fn test_float_division_by_zero() {
1831        let result = run("1.0 / 0.0;").unwrap();
1832        if let Value::Float(f) = result {
1833            assert!(f.is_infinite() && f.is_sign_positive());
1834        } else {
1835            panic!("Expected float infinity");
1836        }
1837    }
1838
1839    #[test]
1840    fn test_nan_creation() {
1841        let result = run("0.0 / 0.0;").unwrap();
1842        if let Value::Float(f) = result {
1843            assert!(f.is_nan());
1844        } else {
1845            panic!("Expected NaN");
1846        }
1847    }
1848
1849    #[test]
1850    fn test_mixed_comparison() {
1851        let result = run("1 < 1.5;").unwrap();
1852        assert_eq!(result, Value::Boolean(true));
1853    }
1854}