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            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            // Arithmetic operations with float support
519            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)), // IEEE 754: /0 = Inf
554                (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            // Comparison
574            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            // Logical
582            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    /// Evaluate comparison operators (<, <=, >, >=).
592    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            // Mixed numeric comparisons (promote integer to float)
614            (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    /// Capture output for the print function with truncation support.
677    ///
678    /// This is used for testing to capture print output. Output is truncated
679    /// if it exceeds MAX_OUTPUT_LINES or MAX_OUTPUT_BYTES.
680    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    /// Call a built-in or user-defined function by name.
704    fn call_function(
705        &mut self,
706        name: &str,
707        arguments: &[Expression],
708    ) -> Result<Value, RuntimeError> {
709        // Evaluate arguments
710        let args: Vec<Value> = arguments
711            .iter()
712            .map(|arg| self.evaluate_expression(arg))
713            .collect::<Result<_, _>>()?;
714
715        // Check for built-in function (copy the fn pointer to avoid borrow issues)
716        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        // Check for user-defined function
724        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    /// Call a method on a receiver expression.
732    ///
733    /// This implements method call syntax as syntactic sugar over function calls.
734    /// Method calls like `receiver.method(arg1, arg2)` are transformed into regular
735    /// function calls with the receiver prepended: `method(receiver, arg1, arg2)`.
736    ///
737    /// # Examples
738    ///
739    /// ```text
740    /// "hello".len()        -> len("hello")
741    /// arr.push(42)         -> push(arr, 42)
742    /// "hi".split(",")      -> split("hi", ",")
743    /// ```
744    ///
745    /// # Arguments
746    ///
747    /// * `receiver` - The expression before the dot (e.g., `"hello"` in `"hello".len()`)
748    /// * `method` - The method name to call (e.g., `"len"`)
749    /// * `arguments` - The arguments passed to the method (empty for `len()`)
750    ///
751    /// # Returns
752    ///
753    /// Returns the result of calling the builtin or user-defined function with
754    /// the receiver prepended to the argument list.
755    ///
756    /// # Errors
757    ///
758    /// Returns `RuntimeError::UndefinedFunction` if no builtin or user function
759    /// with the given method name exists.
760    fn call_method(
761        &mut self,
762        receiver: &Expression,
763        method: &str,
764        arguments: &[Expression],
765    ) -> Result<Value, RuntimeError> {
766        // Evaluate receiver first
767        let receiver_value = self.evaluate_expression(receiver)?;
768
769        // Pre-allocate with correct capacity and build args in order
770        // This avoids the O(n) cost of insert(0, ...)
771        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        // Check for built-in function (copy the fn pointer to avoid borrow issues)
779        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        // Check for user-defined function
787        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    /// Call a user-defined function with pre-evaluated arguments.
795    fn call_user_function(
796        &mut self,
797        func: &UserFunction,
798        args: Vec<Value>,
799    ) -> Result<Value, RuntimeError> {
800        // Check recursion depth
801        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        // Increment call depth
816        self.call_depth += 1;
817
818        // Create new scope for function
819        self.push_scope();
820
821        // Bind arguments to parameters
822        for (param, arg) in func.params.iter().zip(args) {
823            self.define_variable(param.clone(), arg);
824        }
825
826        // Execute function body
827        let result = self.execute_block(&func.body);
828
829        self.pop_scope();
830
831        // Decrement call depth (even on error)
832        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) // 0 + 1 + 2 + 3 + 4
938        );
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        // Generate more than MAX_OUTPUT_LINES (1000) print statements
1138        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        // Should have MAX_OUTPUT_LINES entries plus "[truncated]"
1145        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        // Generate a long string that exceeds MAX_OUTPUT_BYTES (64KB)
1157        let long_string = "x".repeat(70_000);
1158        let source = format!(r#"print("{}");"#, long_string);
1159        interpreter.run(&source).unwrap();
1160
1161        // The single line exceeds the byte limit, so it should be truncated
1162        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        // Just a few print statements should not trigger truncation
1173        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        // Test that reasonable recursion depth works (50 levels, well under MAX_CALL_DEPTH of 64)
1202        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        // Test that dictionary maintains insertion order (IndexMap behavior)
1259        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        // Keys should be in insertion order
1275        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    // Edge case tests (#15)
1286
1287    #[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        // Note: -9223372036854775808 would be parsed as negate(9223372036854775808)
1298        // which overflows, so we use a slightly different approach
1299        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        // Test various Unicode characters
1308        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        // Test deeply nested parenthesized expressions
1321        // (1 + 2) = 3, * 3 = 9, - 4 = 5, / 2 = 2, + 1 = 3, * 2 = 6
1322        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)); // Rust semantics: -7 % 3 = -1
1330    }
1331
1332    #[test]
1333    fn test_modulo_with_negative_divisor() {
1334        let result = run("7 % -3;").unwrap();
1335        assert_eq!(result, Value::Integer(1)); // Rust semantics: 7 % -3 = 1
1336    }
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    // Method call syntax tests
1379
1380    #[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        // Test method chaining: trim then lowercase
1444        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        // Test array method chaining: push twice then get length
1451        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        // Both syntaxes should produce the same result
1464        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        // Test method with arguments that are function calls
1514        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        // Test deeply nested method chains
1521        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        // Call method on result of a function call
1528        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        // Mix method and function syntax in same expression
1535        let result = run(r#"len("hello".uppercase());"#).unwrap();
1536        assert_eq!(result, Value::Integer(5));
1537    }
1538
1539    // === Contains tests ===
1540
1541    #[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    // === Math function tests ===
1578
1579    #[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    // === Timestamp function tests ===
1622
1623    #[test]
1624    fn test_timestamp() {
1625        let result = run("timestamp();").unwrap();
1626        if let Value::Integer(ts) = result {
1627            // Should be after Jan 1, 2020
1628            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            // Should be after Jan 1, 2020 in milliseconds
1649            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            // Should be after Jan 1, 2020 in microseconds
1660            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            // Should be ISO 8601 format
1671            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        // Test that timestamps can be used in arithmetic
1682        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    // === Float tests ===
1689
1690    #[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}