Skip to main content

virtual_rust/interpreter/
methods.rs

1//! Method dispatch for built-in types.
2//!
3//! Each section handles methods for a specific value type:
4//! String, Array/Vec, Int, Float, Char, Bool, Option.
5
6use crate::ast::BinOp;
7use crate::interpreter::error::RuntimeError;
8use crate::interpreter::value::Value;
9use crate::interpreter::Interpreter;
10
11impl Interpreter {
12    /// Dispatches a method call on a runtime value.
13    pub(crate) fn call_method(
14        &mut self,
15        object: Value,
16        method: &str,
17        args: Vec<Value>,
18    ) -> Result<Value, RuntimeError> {
19        match &object {
20            Value::String(s) => self.string_method(s.clone(), method, args),
21            Value::Array(arr) => self.array_method(arr.clone(), method, args),
22            Value::Int(n) => self.int_method(*n, method, &args),
23            Value::Float(n) => self.float_method(*n, method, &args),
24            Value::Bool(b) => self.bool_method(*b, method),
25            Value::Char(c) => self.char_method(*c, method),
26            Value::Option(opt) => self.option_method(opt.clone(), method, args),
27            _ => {
28                // Generic methods available on all types
29                match method {
30                    "to_string" => Ok(Value::String(format!("{object}"))),
31                    "clone" => Ok(object.clone()),
32                    _ => Err(RuntimeError::new(format!(
33                        "Unknown method '{method}' on type '{}'",
34                        object.type_name()
35                    ))),
36                }
37            }
38        }
39    }
40}
41
42// ── String methods ───────────────────────────────────────────────────────
43
44impl Interpreter {
45    fn string_method(
46        &mut self,
47        s: String,
48        method: &str,
49        args: Vec<Value>,
50    ) -> Result<Value, RuntimeError> {
51        match method {
52            "len" => Ok(Value::Int(s.len() as i64)),
53            "is_empty" => Ok(Value::Bool(s.is_empty())),
54            "contains" => {
55                let substr = expect_string_arg(&args, "contains")?;
56                Ok(Value::Bool(s.contains(substr.as_str())))
57            }
58            "starts_with" => {
59                let prefix = expect_string_arg(&args, "starts_with")?;
60                Ok(Value::Bool(s.starts_with(prefix.as_str())))
61            }
62            "ends_with" => {
63                let suffix = expect_string_arg(&args, "ends_with")?;
64                Ok(Value::Bool(s.ends_with(suffix.as_str())))
65            }
66            "trim" => Ok(Value::String(s.trim().to_string())),
67            "trim_start" => Ok(Value::String(s.trim_start().to_string())),
68            "trim_end" => Ok(Value::String(s.trim_end().to_string())),
69            "to_uppercase" => Ok(Value::String(s.to_uppercase())),
70            "to_lowercase" => Ok(Value::String(s.to_lowercase())),
71            "replace" => {
72                if args.len() < 2 {
73                    return Err(RuntimeError::new("replace() expects two arguments"));
74                }
75                match (&args[0], &args[1]) {
76                    (Value::String(from), Value::String(to)) => {
77                        Ok(Value::String(s.replace(from.as_str(), to.as_str())))
78                    }
79                    _ => Err(RuntimeError::new(
80                        "replace() expects two string arguments",
81                    )),
82                }
83            }
84            "split" => {
85                let delim = expect_string_arg(&args, "split")?;
86                let parts = s
87                    .split(delim.as_str())
88                    .map(|p| Value::String(p.to_string()))
89                    .collect();
90                Ok(Value::Array(parts))
91            }
92            "chars" => Ok(Value::Array(s.chars().map(Value::Char).collect())),
93            "bytes" => Ok(Value::Array(
94                s.bytes().map(|b| Value::Int(b as i64)).collect(),
95            )),
96            "to_string" => Ok(Value::String(s)),
97            "parse" => {
98                if let Ok(n) = s.parse::<i64>() {
99                    Ok(Value::Option(Some(Box::new(Value::Int(n)))))
100                } else if let Ok(n) = s.parse::<f64>() {
101                    Ok(Value::Option(Some(Box::new(Value::Float(n)))))
102                } else {
103                    Ok(Value::Option(None))
104                }
105            }
106            "push_str" => {
107                let extra = expect_string_arg(&args, "push_str")?;
108                Ok(Value::String(format!("{s}{extra}")))
109            }
110            "push" => {
111                let c = expect_char_arg(&args, "push")?;
112                let mut new_s = s;
113                new_s.push(c);
114                Ok(Value::String(new_s))
115            }
116            "repeat" => {
117                let n = expect_int_arg(&args, "repeat")?;
118                Ok(Value::String(s.repeat(n as usize)))
119            }
120            "lines" => {
121                let lines = s.lines().map(|l| Value::String(l.to_string())).collect();
122                Ok(Value::Array(lines))
123            }
124            "clone" => Ok(Value::String(s)),
125            _ => Err(RuntimeError::new(format!(
126                "Unknown method '{method}' on String"
127            ))),
128        }
129    }
130}
131
132// ── Array / Vec methods ──────────────────────────────────────────────────
133
134impl Interpreter {
135    fn array_method(
136        &mut self,
137        arr: Vec<Value>,
138        method: &str,
139        args: Vec<Value>,
140    ) -> Result<Value, RuntimeError> {
141        match method {
142            // ── Size & access ────────────────────────────────────────
143            "len" | "count" => Ok(Value::Int(arr.len() as i64)),
144            "is_empty" => Ok(Value::Bool(arr.is_empty())),
145            "first" => Ok(Value::Option(arr.first().map(|v| Box::new(v.clone())))),
146            "last" => Ok(Value::Option(arr.last().map(|v| Box::new(v.clone())))),
147
148            // ── Mutation-style (returns new value) ───────────────────
149            "push" => {
150                let mut new_arr = arr;
151                if let Some(val) = args.into_iter().next() {
152                    new_arr.push(val);
153                }
154                Ok(Value::Array(new_arr))
155            }
156            "pop" => {
157                let mut new_arr = arr;
158                let popped = new_arr.pop();
159                Ok(Value::Option(popped.map(Box::new)))
160            }
161            "reverse" => {
162                let mut new_arr = arr;
163                new_arr.reverse();
164                Ok(Value::Array(new_arr))
165            }
166
167            // ── Search ───────────────────────────────────────────────
168            "contains" => {
169                if let Some(val) = args.first() {
170                    let found = arr
171                        .iter()
172                        .any(|v| format!("{v}") == format!("{val}"));
173                    Ok(Value::Bool(found))
174                } else {
175                    Ok(Value::Bool(false))
176                }
177            }
178
179            // ── Iterators (pass-through) ─────────────────────────────
180            "iter" | "into_iter" | "collect" => Ok(Value::Array(arr)),
181
182            // ── Higher-order methods ─────────────────────────────────
183            "map" => self.array_hof_map(arr, args),
184            "filter" => self.array_hof_filter(arr, args),
185            "fold" => self.array_hof_fold(arr, args),
186            "for_each" => self.array_hof_for_each(arr, args),
187            "any" => self.array_hof_any(arr, args),
188            "all" => self.array_hof_all(arr, args),
189            "find" => self.array_hof_find(arr, args),
190            "position" => self.array_hof_position(arr, args),
191            "flat_map" => self.array_hof_flat_map(arr, args),
192
193            // ── Transform ────────────────────────────────────────────
194            "enumerate" => {
195                let enumerated = arr
196                    .iter()
197                    .enumerate()
198                    .map(|(i, v)| Value::Tuple(vec![Value::Int(i as i64), v.clone()]))
199                    .collect();
200                Ok(Value::Array(enumerated))
201            }
202            "zip" => {
203                let other = match args.first() {
204                    Some(Value::Array(a)) => a,
205                    _ => return Err(RuntimeError::new("zip() expects an array argument")),
206                };
207                let zipped = arr
208                    .iter()
209                    .zip(other.iter())
210                    .map(|(a, b)| Value::Tuple(vec![a.clone(), b.clone()]))
211                    .collect();
212                Ok(Value::Array(zipped))
213            }
214            "skip" => {
215                let n = expect_int_arg(&args, "skip")? as usize;
216                Ok(Value::Array(arr.into_iter().skip(n).collect()))
217            }
218            "take" => {
219                let n = expect_int_arg(&args, "take")? as usize;
220                Ok(Value::Array(arr.into_iter().take(n).collect()))
221            }
222
223            // ── Aggregation ──────────────────────────────────────────
224            "sum" => {
225                let mut acc = Value::Int(0);
226                for item in &arr {
227                    acc = self.eval_binary_op(&acc, &BinOp::Add, item)?;
228                }
229                Ok(acc)
230            }
231            "product" => {
232                let mut acc = Value::Int(1);
233                for item in &arr {
234                    acc = self.eval_binary_op(&acc, &BinOp::Mul, item)?;
235                }
236                Ok(acc)
237            }
238            "min" => self.array_min_max(&arr, true),
239            "max" => self.array_min_max(&arr, false),
240            "join" => {
241                let sep = match args.first() {
242                    Some(Value::String(s)) => s.as_str(),
243                    _ => "",
244                };
245                let parts: Vec<String> = arr.iter().map(|v| format!("{v}")).collect();
246                Ok(Value::String(parts.join(sep)))
247            }
248
249            _ => Err(RuntimeError::new(format!(
250                "Unknown method '{method}' on array"
251            ))),
252        }
253    }
254
255    // ── Higher-order function helpers ────────────────────────────────
256
257    fn array_hof_map(&mut self, arr: Vec<Value>, args: Vec<Value>) -> Result<Value, RuntimeError> {
258        let func = expect_callable(args, "map")?;
259        let mut results = Vec::with_capacity(arr.len());
260        for item in arr {
261            results.push(self.call_function(func.clone(), vec![item])?);
262        }
263        Ok(Value::Array(results))
264    }
265
266    fn array_hof_filter(
267        &mut self,
268        arr: Vec<Value>,
269        args: Vec<Value>,
270    ) -> Result<Value, RuntimeError> {
271        let func = expect_callable(args, "filter")?;
272        let mut results = Vec::new();
273        for item in arr {
274            let keep = self.call_function(func.clone(), vec![item.clone()])?;
275            if keep.is_truthy() {
276                results.push(item);
277            }
278        }
279        Ok(Value::Array(results))
280    }
281
282    fn array_hof_fold(
283        &mut self,
284        arr: Vec<Value>,
285        args: Vec<Value>,
286    ) -> Result<Value, RuntimeError> {
287        if args.len() < 2 {
288            return Err(RuntimeError::new(
289                "fold() expects an initial value and a closure",
290            ));
291        }
292        let mut acc = args[0].clone();
293        let func = args[1].clone();
294        for item in arr {
295            acc = self.call_function(func.clone(), vec![acc, item])?;
296        }
297        Ok(acc)
298    }
299
300    fn array_hof_for_each(
301        &mut self,
302        arr: Vec<Value>,
303        args: Vec<Value>,
304    ) -> Result<Value, RuntimeError> {
305        let func = expect_callable(args, "for_each")?;
306        for item in arr {
307            self.call_function(func.clone(), vec![item])?;
308        }
309        Ok(Value::Unit)
310    }
311
312    fn array_hof_any(&mut self, arr: Vec<Value>, args: Vec<Value>) -> Result<Value, RuntimeError> {
313        let func = expect_callable(args, "any")?;
314        for item in arr {
315            if self.call_function(func.clone(), vec![item])?.is_truthy() {
316                return Ok(Value::Bool(true));
317            }
318        }
319        Ok(Value::Bool(false))
320    }
321
322    fn array_hof_all(&mut self, arr: Vec<Value>, args: Vec<Value>) -> Result<Value, RuntimeError> {
323        let func = expect_callable(args, "all")?;
324        for item in arr {
325            if !self.call_function(func.clone(), vec![item])?.is_truthy() {
326                return Ok(Value::Bool(false));
327            }
328        }
329        Ok(Value::Bool(true))
330    }
331
332    fn array_hof_find(
333        &mut self,
334        arr: Vec<Value>,
335        args: Vec<Value>,
336    ) -> Result<Value, RuntimeError> {
337        let func = expect_callable(args, "find")?;
338        for item in arr {
339            if self
340                .call_function(func.clone(), vec![item.clone()])?
341                .is_truthy()
342            {
343                return Ok(Value::Option(Some(Box::new(item))));
344            }
345        }
346        Ok(Value::Option(None))
347    }
348
349    fn array_hof_position(
350        &mut self,
351        arr: Vec<Value>,
352        args: Vec<Value>,
353    ) -> Result<Value, RuntimeError> {
354        let func = expect_callable(args, "position")?;
355        for (i, item) in arr.iter().enumerate() {
356            if self
357                .call_function(func.clone(), vec![item.clone()])?
358                .is_truthy()
359            {
360                return Ok(Value::Option(Some(Box::new(Value::Int(i as i64)))));
361            }
362        }
363        Ok(Value::Option(None))
364    }
365
366    fn array_hof_flat_map(
367        &mut self,
368        arr: Vec<Value>,
369        args: Vec<Value>,
370    ) -> Result<Value, RuntimeError> {
371        let func = expect_callable(args, "flat_map")?;
372        let mut results = Vec::new();
373        for item in arr {
374            let result = self.call_function(func.clone(), vec![item])?;
375            match result {
376                Value::Array(inner) => results.extend(inner),
377                other => results.push(other),
378            }
379        }
380        Ok(Value::Array(results))
381    }
382
383    fn array_min_max(&self, arr: &[Value], is_min: bool) -> Result<Value, RuntimeError> {
384        if arr.is_empty() {
385            return Ok(Value::Option(None));
386        }
387        let cmp_op = if is_min { BinOp::Lt } else { BinOp::Gt };
388        let mut best = arr[0].clone();
389        for item in arr.iter().skip(1) {
390            if let Value::Bool(true) = self.eval_binary_op(item, &cmp_op, &best)? {
391                best = item.clone();
392            }
393        }
394        Ok(Value::Option(Some(Box::new(best))))
395    }
396}
397
398// ── Integer methods ──────────────────────────────────────────────────────
399
400impl Interpreter {
401    fn int_method(
402        &self,
403        n: i64,
404        method: &str,
405        args: &[Value],
406    ) -> Result<Value, RuntimeError> {
407        match method {
408            "abs" => Ok(Value::Int(n.abs())),
409            "pow" => {
410                let exp = expect_int_arg(args, "pow")?;
411                Ok(Value::Int(n.pow(exp as u32)))
412            }
413            "to_string" => Ok(Value::String(n.to_string())),
414            "min" => {
415                let other = expect_int_arg(args, "min")?;
416                Ok(Value::Int(n.min(other)))
417            }
418            "max" => {
419                let other = expect_int_arg(args, "max")?;
420                Ok(Value::Int(n.max(other)))
421            }
422            "clamp" => {
423                if args.len() < 2 {
424                    return Err(RuntimeError::new("clamp() expects two arguments"));
425                }
426                match (&args[0], &args[1]) {
427                    (Value::Int(min), Value::Int(max)) => Ok(Value::Int(n.clamp(*min, *max))),
428                    _ => Err(RuntimeError::new("clamp() expects integer arguments")),
429                }
430            }
431            _ => Err(RuntimeError::new(format!(
432                "Unknown method '{method}' on i64"
433            ))),
434        }
435    }
436}
437
438// ── Float methods ────────────────────────────────────────────────────────
439
440impl Interpreter {
441    fn float_method(
442        &self,
443        n: f64,
444        method: &str,
445        args: &[Value],
446    ) -> Result<Value, RuntimeError> {
447        match method {
448            "abs" => Ok(Value::Float(n.abs())),
449            "sqrt" => Ok(Value::Float(n.sqrt())),
450            "floor" => Ok(Value::Float(n.floor())),
451            "ceil" => Ok(Value::Float(n.ceil())),
452            "round" => Ok(Value::Float(n.round())),
453            "sin" => Ok(Value::Float(n.sin())),
454            "cos" => Ok(Value::Float(n.cos())),
455            "tan" => Ok(Value::Float(n.tan())),
456            "log" => {
457                if let Some(Value::Float(base)) = args.first() {
458                    Ok(Value::Float(n.log(*base)))
459                } else {
460                    Ok(Value::Float(n.ln()))
461                }
462            }
463            "ln" => Ok(Value::Float(n.ln())),
464            "log2" => Ok(Value::Float(n.log2())),
465            "log10" => Ok(Value::Float(n.log10())),
466            "powi" => {
467                let exp = expect_int_arg(args, "powi")?;
468                Ok(Value::Float(n.powi(exp as i32)))
469            }
470            "powf" => {
471                if let Some(Value::Float(exp)) = args.first() {
472                    Ok(Value::Float(n.powf(*exp)))
473                } else {
474                    Err(RuntimeError::new("powf() expects a float argument"))
475                }
476            }
477            "is_nan" => Ok(Value::Bool(n.is_nan())),
478            "is_infinite" => Ok(Value::Bool(n.is_infinite())),
479            "is_finite" => Ok(Value::Bool(n.is_finite())),
480            "to_string" => Ok(Value::String(n.to_string())),
481            _ => Err(RuntimeError::new(format!(
482                "Unknown method '{method}' on f64"
483            ))),
484        }
485    }
486}
487
488// ── Bool methods ─────────────────────────────────────────────────────────
489
490impl Interpreter {
491    fn bool_method(&self, b: bool, method: &str) -> Result<Value, RuntimeError> {
492        match method {
493            "to_string" => Ok(Value::String(b.to_string())),
494            _ => Err(RuntimeError::new(format!(
495                "Unknown method '{method}' on bool"
496            ))),
497        }
498    }
499}
500
501// ── Char methods ─────────────────────────────────────────────────────────
502
503impl Interpreter {
504    fn char_method(&self, c: char, method: &str) -> Result<Value, RuntimeError> {
505        match method {
506            "is_alphabetic" => Ok(Value::Bool(c.is_alphabetic())),
507            "is_numeric" => Ok(Value::Bool(c.is_numeric())),
508            "is_alphanumeric" => Ok(Value::Bool(c.is_alphanumeric())),
509            "is_whitespace" => Ok(Value::Bool(c.is_whitespace())),
510            "is_uppercase" => Ok(Value::Bool(c.is_uppercase())),
511            "is_lowercase" => Ok(Value::Bool(c.is_lowercase())),
512            "to_uppercase" => Ok(Value::String(c.to_uppercase().to_string())),
513            "to_lowercase" => Ok(Value::String(c.to_lowercase().to_string())),
514            "to_string" => Ok(Value::String(c.to_string())),
515            _ => Err(RuntimeError::new(format!(
516                "Unknown method '{method}' on char"
517            ))),
518        }
519    }
520}
521
522// ── Option methods ───────────────────────────────────────────────────────
523
524impl Interpreter {
525    fn option_method(
526        &mut self,
527        opt: Option<Box<Value>>,
528        method: &str,
529        args: Vec<Value>,
530    ) -> Result<Value, RuntimeError> {
531        match method {
532            "unwrap" => match opt {
533                Some(v) => Ok(*v),
534                None => Err(RuntimeError::new("Called unwrap() on a None value")),
535            },
536            "unwrap_or" => match opt {
537                Some(v) => Ok(*v),
538                None => args
539                    .into_iter()
540                    .next()
541                    .ok_or_else(|| RuntimeError::new("unwrap_or() expects a default value")),
542            },
543            "is_some" => Ok(Value::Bool(opt.is_some())),
544            "is_none" => Ok(Value::Bool(opt.is_none())),
545            "map" => {
546                let func = expect_callable(args, "map")?;
547                match opt {
548                    Some(v) => {
549                        let result = self.call_function(func, vec![*v])?;
550                        Ok(Value::Option(Some(Box::new(result))))
551                    }
552                    None => Ok(Value::Option(None)),
553                }
554            }
555            _ => Err(RuntimeError::new(format!(
556                "Unknown method '{method}' on Option"
557            ))),
558        }
559    }
560}
561
562// ── Argument extraction helpers ──────────────────────────────────────────
563
564fn expect_string_arg(args: &[Value], method_name: &str) -> Result<String, RuntimeError> {
565    match args.first() {
566        Some(Value::String(s)) => Ok(s.clone()),
567        _ => Err(RuntimeError::new(format!(
568            "{method_name}() expects a string argument"
569        ))),
570    }
571}
572
573fn expect_int_arg(args: &[Value], method_name: &str) -> Result<i64, RuntimeError> {
574    match args.first() {
575        Some(Value::Int(n)) => Ok(*n),
576        _ => Err(RuntimeError::new(format!(
577            "{method_name}() expects an integer argument"
578        ))),
579    }
580}
581
582fn expect_char_arg(args: &[Value], method_name: &str) -> Result<char, RuntimeError> {
583    match args.first() {
584        Some(Value::Char(c)) => Ok(*c),
585        _ => Err(RuntimeError::new(format!(
586            "{method_name}() expects a char argument"
587        ))),
588    }
589}
590
591fn expect_callable(args: Vec<Value>, method_name: &str) -> Result<Value, RuntimeError> {
592    args.into_iter().next().ok_or_else(|| {
593        RuntimeError::new(format!("{method_name}() expects a closure argument"))
594    })
595}