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