1#[cfg(feature = "no_std")]
2use alloc::{format, string::{String, ToString}, vec::Vec};
3
4use crate::builtins::{error, expect_number};
5use crate::error::BopError;
6use crate::value::{Value, values_equal};
7
8pub fn array_method(
10 arr: &[Value],
11 method: &str,
12 args: &[Value],
13 line: u32,
14) -> Result<(Value, Option<Value>), BopError> {
15 match method {
16 "len" => Ok((Value::Int(arr.len() as i64), None)),
17 "push" => {
18 if args.len() != 1 {
19 return Err(error(line, ".push() needs exactly 1 argument"));
20 }
21 let mut new_arr = arr.to_vec();
22 new_arr.push(args[0].clone());
23 Ok((Value::None, Some(Value::new_array(new_arr))))
24 }
25 "pop" => {
26 let mut new_arr = arr.to_vec();
27 let popped = new_arr.pop().unwrap_or(Value::None);
28 Ok((popped, Some(Value::new_array(new_arr))))
29 }
30 "has" => {
31 if args.len() != 1 {
32 return Err(error(line, ".has() needs exactly 1 argument"));
33 }
34 let found = arr.iter().any(|v| values_equal(v, &args[0]));
35 Ok((Value::Bool(found), None))
36 }
37 "index_of" => {
38 if args.len() != 1 {
39 return Err(error(line, ".index_of() needs exactly 1 argument"));
40 }
41 let idx = arr.iter().position(|v| values_equal(v, &args[0]));
42 Ok((Value::Int(idx.map_or(-1, |i| i as i64)), None))
43 }
44 "insert" => {
45 if args.len() != 2 {
46 return Err(error(line, ".insert() needs 2 arguments: index and value"));
47 }
48 let i = expect_number("insert", &args[0], line)? as usize;
49 let mut new_arr = arr.to_vec();
50 if i > new_arr.len() {
51 return Err(error(line, format!("Insert index {} is out of bounds", i)));
52 }
53 new_arr.insert(i, args[1].clone());
54 Ok((Value::None, Some(Value::new_array(new_arr))))
55 }
56 "remove" => {
57 if args.len() != 1 {
58 return Err(error(line, ".remove() needs exactly 1 argument (index)"));
59 }
60 let i = expect_number("remove", &args[0], line)? as usize;
61 let mut new_arr = arr.to_vec();
62 if i >= new_arr.len() {
63 return Err(error(line, format!("Remove index {} is out of bounds", i)));
64 }
65 let removed = new_arr.remove(i);
66 Ok((removed, Some(Value::new_array(new_arr))))
67 }
68 "slice" => {
69 if args.len() != 2 {
70 return Err(error(line, ".slice() needs 2 arguments: start and end"));
71 }
72 let start = expect_number("slice", &args[0], line)? as usize;
73 let end = (expect_number("slice", &args[1], line)? as usize).min(arr.len());
74 let start = start.min(end);
75 let slice = arr[start..end].to_vec();
76 Ok((Value::new_array(slice), None))
77 }
78 "reverse" => {
79 let mut new_arr = arr.to_vec();
80 new_arr.reverse();
81 Ok((Value::None, Some(Value::new_array(new_arr))))
82 }
83 "sort" => {
84 let mut new_arr = arr.to_vec();
85 new_arr.sort_by(|a, b| match (a, b) {
86 (Value::Int(x), Value::Int(y)) => x.cmp(y),
87 (Value::Number(x), Value::Number(y)) => {
88 x.partial_cmp(y).unwrap_or(core::cmp::Ordering::Equal)
89 }
90 (Value::Int(x), Value::Number(y)) => (*x as f64)
94 .partial_cmp(y)
95 .unwrap_or(core::cmp::Ordering::Equal),
96 (Value::Number(x), Value::Int(y)) => x
97 .partial_cmp(&(*y as f64))
98 .unwrap_or(core::cmp::Ordering::Equal),
99 (Value::Str(x), Value::Str(y)) => x.cmp(y),
100 _ => core::cmp::Ordering::Equal,
101 });
102 Ok((Value::None, Some(Value::new_array(new_arr))))
103 }
104 "join" => {
105 if args.len() != 1 {
106 return Err(error(line, ".join() needs exactly 1 argument (separator)"));
107 }
108 let sep = match &args[0] {
109 Value::Str(s) => s.as_str(),
110 _ => return Err(error(line, ".join() separator must be a string")),
111 };
112 let result = arr
113 .iter()
114 .map(|v| format!("{}", v))
115 .collect::<Vec<_>>()
116 .join(sep);
117 Ok((Value::new_str(result), None))
118 }
119 "iter" => {
120 crate::builtins::expect_args("iter", args, 0, line)?;
121 Ok((Value::new_array_iter(arr.to_vec()), None))
126 }
127 _ => Err(error(line, format!("Array doesn't have a .{}() method", method))),
128 }
129}
130
131pub fn string_method(
132 s: &str,
133 method: &str,
134 args: &[Value],
135 line: u32,
136) -> Result<(Value, Option<Value>), BopError> {
137 match method {
138 "len" => Ok((Value::Int(s.chars().count() as i64), None)),
139 "contains" => {
140 if args.len() != 1 {
141 return Err(error(line, ".contains() needs 1 argument"));
142 }
143 let substr = match &args[0] {
144 Value::Str(s) => s.as_str(),
145 _ => return Err(error(line, ".contains() needs a string argument")),
146 };
147 Ok((Value::Bool(s.contains(substr)), None))
148 }
149 "starts_with" => {
150 if args.len() != 1 {
151 return Err(error(line, ".starts_with() needs 1 argument"));
152 }
153 let prefix = match &args[0] {
154 Value::Str(s) => s.as_str(),
155 _ => return Err(error(line, ".starts_with() needs a string")),
156 };
157 Ok((Value::Bool(s.starts_with(prefix)), None))
158 }
159 "ends_with" => {
160 if args.len() != 1 {
161 return Err(error(line, ".ends_with() needs 1 argument"));
162 }
163 let suffix = match &args[0] {
164 Value::Str(s) => s.as_str(),
165 _ => return Err(error(line, ".ends_with() needs a string")),
166 };
167 Ok((Value::Bool(s.ends_with(suffix)), None))
168 }
169 "index_of" => {
170 if args.len() != 1 {
171 return Err(error(line, ".index_of() needs 1 argument"));
172 }
173 let substr = match &args[0] {
174 Value::Str(s) => s.as_str(),
175 _ => return Err(error(line, ".index_of() needs a string")),
176 };
177 let idx = s.find(substr).map_or(-1, |i| i as i64);
178 Ok((Value::Int(idx), None))
179 }
180 "split" => {
181 if args.len() != 1 {
182 return Err(error(line, ".split() needs 1 argument"));
183 }
184 let sep = match &args[0] {
185 Value::Str(s) => s.as_str(),
186 _ => return Err(error(line, ".split() needs a string")),
187 };
188 let parts: Vec<Value> = s
189 .split(sep)
190 .map(|p| Value::new_str(p.to_string()))
191 .collect();
192 Ok((Value::new_array(parts), None))
193 }
194 "replace" => {
195 if args.len() != 2 {
196 return Err(error(line, ".replace() needs 2 arguments: old and new"));
197 }
198 let old = match &args[0] {
199 Value::Str(s) => s.as_str(),
200 _ => return Err(error(line, ".replace() arguments must be strings")),
201 };
202 let new = match &args[1] {
203 Value::Str(s) => s.as_str(),
204 _ => return Err(error(line, ".replace() arguments must be strings")),
205 };
206 let result = s.replace(old, new);
207 Ok((Value::new_str(result), None))
208 }
209 "upper" => {
210 let result = s.to_uppercase();
211 Ok((Value::new_str(result), None))
212 }
213 "lower" => {
214 let result = s.to_lowercase();
215 Ok((Value::new_str(result), None))
216 }
217 "trim" => {
218 let result = s.trim().to_string();
219 Ok((Value::new_str(result), None))
220 }
221 "slice" => {
222 if args.len() != 2 {
223 return Err(error(line, ".slice() needs 2 arguments: start and end"));
224 }
225 let start = expect_number("slice", &args[0], line)? as usize;
226 let chars: Vec<char> = s.chars().collect();
227 let end = (expect_number("slice", &args[1], line)? as usize).min(chars.len());
228 let start = start.min(end);
229 let result: String = chars[start..end].iter().collect();
230 Ok((Value::new_str(result), None))
231 }
232 "to_int" => {
233 if !args.is_empty() {
234 return Err(error(line, ".to_int() takes no arguments"));
235 }
236 if let Ok(n) = s.parse::<i64>() {
241 return Ok((Value::Int(n), None));
242 }
243 let n: f64 = s.parse().map_err(|_| {
244 error(line, format!("Can't convert \"{}\" to a number", s))
245 })?;
246 Ok((Value::Int(n as i64), None))
247 }
248 "to_float" => {
249 if !args.is_empty() {
250 return Err(error(line, ".to_float() takes no arguments"));
251 }
252 let n: f64 = s.parse().map_err(|_| {
253 error(line, format!("Can't convert \"{}\" to a number", s))
254 })?;
255 Ok((Value::Number(n), None))
256 }
257 "iter" => {
258 crate::builtins::expect_args("iter", args, 0, line)?;
259 let chars: Vec<char> = s.chars().collect();
260 Ok((Value::new_string_iter(chars), None))
261 }
262 _ => Err(error(line, format!("String doesn't have a .{}() method", method))),
263 }
264}
265
266pub fn dict_method(
267 entries: &[(String, Value)],
268 method: &str,
269 args: &[Value],
270 line: u32,
271) -> Result<(Value, Option<Value>), BopError> {
272 match method {
273 "len" => Ok((Value::Int(entries.len() as i64), None)),
274 "keys" => {
275 let keys: Vec<Value> = entries
276 .iter()
277 .map(|(k, _)| Value::new_str(k.clone()))
278 .collect();
279 Ok((Value::new_array(keys), None))
280 }
281 "values" => {
282 let vals: Vec<Value> = entries.iter().map(|(_, v)| v.clone()).collect();
283 Ok((Value::new_array(vals), None))
284 }
285 "has" => {
286 if args.len() != 1 {
287 return Err(error(line, ".has() needs 1 argument"));
288 }
289 let key = match &args[0] {
290 Value::Str(s) => s.as_str(),
291 _ => return Err(error(line, ".has() needs a string key")),
292 };
293 Ok((Value::Bool(entries.iter().any(|(k, _)| k == key)), None))
294 }
295 "iter" => {
296 crate::builtins::expect_args("iter", args, 0, line)?;
297 let keys: Vec<String> = entries.iter().map(|(k, _)| k.clone()).collect();
300 Ok((Value::new_dict_iter(keys), None))
301 }
302 _ => Err(error(line, format!("Dict doesn't have a .{}() method", method))),
303 }
304}
305
306pub fn common_method(
315 receiver: &Value,
316 method: &str,
317 args: &[Value],
318 line: u32,
319) -> Result<Option<(Value, Option<Value>)>, BopError> {
320 match method {
321 "type" => {
322 crate::builtins::expect_args("type", args, 0, line)?;
323 Ok(Some((
324 Value::new_str(receiver.type_name().to_string()),
325 None,
326 )))
327 }
328 "to_str" => {
329 crate::builtins::expect_args("to_str", args, 0, line)?;
330 Ok(Some((
331 Value::new_str(format!("{}", receiver)),
332 None,
333 )))
334 }
335 "inspect" => {
336 crate::builtins::expect_args("inspect", args, 0, line)?;
337 Ok(Some((Value::new_str(receiver.inspect()), None)))
338 }
339 "is_none" => {
347 crate::builtins::expect_args("is_none", args, 0, line)?;
348 Ok(Some((
349 Value::Bool(matches!(receiver, Value::None)),
350 None,
351 )))
352 }
353 "is_some" => {
354 crate::builtins::expect_args("is_some", args, 0, line)?;
355 Ok(Some((
356 Value::Bool(!matches!(receiver, Value::None)),
357 None,
358 )))
359 }
360 _ => Ok(None),
361 }
362}
363
364pub fn numeric_method(
370 receiver: &Value,
371 method: &str,
372 args: &[Value],
373 line: u32,
374) -> Result<(Value, Option<Value>), BopError> {
375 use crate::builtins::{expect_args, finite_to_int_or_number};
376 match method {
377 "abs" => {
379 expect_args("abs", args, 0, line)?;
380 match receiver {
381 Value::Int(n) => n
382 .checked_abs()
383 .map(Value::Int)
384 .map(|v| (v, None))
385 .ok_or_else(|| error(line, "Integer overflow in `.abs()`")),
386 Value::Number(n) => Ok((Value::Number(n.abs()), None)),
387 _ => unreachable!("numeric_method called on non-numeric receiver"),
388 }
389 }
390 "sqrt" => unary_number(receiver, args, line, "sqrt", crate::math::sqrt),
392 "sin" => unary_number(receiver, args, line, "sin", crate::math::sin),
393 "cos" => unary_number(receiver, args, line, "cos", crate::math::cos),
394 "tan" => unary_number(receiver, args, line, "tan", crate::math::tan),
395 "exp" => unary_number(receiver, args, line, "exp", crate::math::exp),
396 "log" => unary_number(receiver, args, line, "log", crate::math::ln),
397 "floor" => unary_round(receiver, args, line, "floor", crate::math::floor),
400 "ceil" => unary_round(receiver, args, line, "ceil", crate::math::ceil),
401 "round" => unary_round(receiver, args, line, "round", crate::math::round),
402 "pow" => {
403 expect_args("pow", args, 1, line)?;
404 let base = to_f64_or_error(receiver, "pow", line)?;
405 let exp = to_f64_or_error(&args[0], "pow", line)?;
406 Ok((Value::Number(crate::math::powf(base, exp)), None))
407 }
408 "min" => pair_pick(receiver, args, line, "min", true),
413 "max" => pair_pick(receiver, args, line, "max", false),
414 "to_int" => {
417 expect_args("to_int", args, 0, line)?;
418 match receiver {
419 Value::Int(n) => Ok((Value::Int(*n), None)),
420 Value::Number(n) => Ok((Value::Int(*n as i64), None)),
421 _ => unreachable!(),
422 }
423 }
424 "to_float" => {
425 expect_args("to_float", args, 0, line)?;
426 match receiver {
427 Value::Int(n) => Ok((Value::Number(*n as f64), None)),
428 Value::Number(n) => Ok((Value::Number(*n), None)),
429 _ => unreachable!(),
430 }
431 }
432 _ => {
433 let _ = finite_to_int_or_number;
434 Err(error(
435 line,
436 crate::error_messages::no_such_method(receiver.type_name(), method),
437 ))
438 }
439 }
440}
441
442pub fn bool_method(
446 receiver: &Value,
447 method: &str,
448 args: &[Value],
449 line: u32,
450) -> Result<(Value, Option<Value>), BopError> {
451 use crate::builtins::expect_args;
452 let b = match receiver {
453 Value::Bool(b) => *b,
454 _ => unreachable!("bool_method called on non-bool receiver"),
455 };
456 match method {
457 "to_int" => {
458 expect_args("to_int", args, 0, line)?;
459 Ok((Value::Int(if b { 1 } else { 0 }), None))
460 }
461 "to_float" => {
462 expect_args("to_float", args, 0, line)?;
463 Ok((Value::Number(if b { 1.0 } else { 0.0 }), None))
464 }
465 _ => Err(error(
466 line,
467 crate::error_messages::no_such_method("bool", method),
468 )),
469 }
470}
471
472fn to_f64_or_error(v: &Value, method: &str, line: u32) -> Result<f64, BopError> {
478 match v {
479 Value::Int(n) => Ok(*n as f64),
480 Value::Number(n) => Ok(*n),
481 other => Err(error(
482 line,
483 format!(
484 "`.{}` expects a number, got {}",
485 method,
486 other.type_name()
487 ),
488 )),
489 }
490}
491
492fn unary_number(
495 receiver: &Value,
496 args: &[Value],
497 line: u32,
498 method: &str,
499 op: fn(f64) -> f64,
500) -> Result<(Value, Option<Value>), BopError> {
501 crate::builtins::expect_args(method, args, 0, line)?;
502 let x = to_f64_or_error(receiver, method, line)?;
503 Ok((Value::Number(op(x)), None))
504}
505
506fn unary_round(
510 receiver: &Value,
511 args: &[Value],
512 line: u32,
513 method: &str,
514 op: fn(f64) -> f64,
515) -> Result<(Value, Option<Value>), BopError> {
516 use crate::builtins::{expect_args, finite_to_int_or_number};
517 expect_args(method, args, 0, line)?;
518 match receiver {
519 Value::Int(n) => Ok((Value::Int(*n), None)),
520 Value::Number(n) => Ok((finite_to_int_or_number(op(*n)), None)),
521 _ => unreachable!("unary_round called on non-numeric receiver"),
522 }
523}
524
525fn pair_pick(
528 receiver: &Value,
529 args: &[Value],
530 line: u32,
531 method: &str,
532 pick_smaller: bool,
533) -> Result<(Value, Option<Value>), BopError> {
534 use crate::builtins::expect_args;
535 expect_args(method, args, 1, line)?;
536 match (receiver, &args[0]) {
537 (Value::Int(a), Value::Int(b)) => {
538 let pick = if pick_smaller { (*a).min(*b) } else { (*a).max(*b) };
539 Ok((Value::Int(pick), None))
540 }
541 (Value::Number(a), Value::Number(b)) => {
542 let pick = if pick_smaller { a.min(*b) } else { a.max(*b) };
543 Ok((Value::Number(pick), None))
544 }
545 (Value::Int(a), Value::Number(b)) => {
546 let af = *a as f64;
547 let pick = if pick_smaller { af.min(*b) } else { af.max(*b) };
548 Ok((Value::Number(pick), None))
549 }
550 (Value::Number(a), Value::Int(b)) => {
551 let bf = *b as f64;
552 let pick = if pick_smaller { a.min(bf) } else { a.max(bf) };
553 Ok((Value::Number(pick), None))
554 }
555 (_, other) => Err(error(
556 line,
557 format!(
558 "`.{}({})` expects a number, got {}",
559 method,
560 other.type_name(),
561 other.type_name()
562 ),
563 )),
564 }
565}
566
567pub fn is_mutating_method(method: &str) -> bool {
568 matches!(
569 method,
570 "push" | "pop" | "insert" | "remove" | "reverse" | "sort"
571 )
572}
573
574pub fn is_builtin_result(receiver: &Value) -> bool {
589 match receiver {
590 Value::EnumVariant(e) => {
591 e.module_path() == crate::value::BUILTIN_MODULE_PATH && e.type_name() == "Result"
592 }
593 _ => false,
594 }
595}
596
597pub enum ResultCallableKind {
602 Map,
604 MapErr,
606 AndThen,
609}
610
611pub fn is_result_callable_method(method: &str) -> Option<ResultCallableKind> {
612 match method {
613 "map" => Some(ResultCallableKind::Map),
614 "map_err" => Some(ResultCallableKind::MapErr),
615 "and_then" => Some(ResultCallableKind::AndThen),
616 _ => None,
617 }
618}
619
620pub fn result_method(
630 receiver: &Value,
631 method: &str,
632 args: &[Value],
633 line: u32,
634) -> Result<Option<Value>, BopError> {
635 use crate::builtins::expect_args;
636 let e = match receiver {
637 Value::EnumVariant(e) => e,
638 _ => return Ok(None),
639 };
640 let ok_payload = || -> Option<Value> {
641 if e.variant() != "Ok" {
642 return None;
643 }
644 match e.payload() {
645 crate::value::EnumPayload::Tuple(items) if items.len() == 1 => {
646 Some(items[0].clone())
647 }
648 _ => None,
649 }
650 };
651 let err_payload = || -> Option<Value> {
652 if e.variant() != "Err" {
653 return None;
654 }
655 match e.payload() {
656 crate::value::EnumPayload::Tuple(items) if items.len() == 1 => {
657 Some(items[0].clone())
658 }
659 _ => None,
660 }
661 };
662 match method {
663 "is_ok" => {
664 expect_args("is_ok", args, 0, line)?;
665 Ok(Some(Value::Bool(e.variant() == "Ok")))
666 }
667 "is_err" => {
668 expect_args("is_err", args, 0, line)?;
669 Ok(Some(Value::Bool(e.variant() == "Err")))
670 }
671 "unwrap" => {
672 expect_args("unwrap", args, 0, line)?;
673 if let Some(v) = ok_payload() {
674 return Ok(Some(v));
675 }
676 let detail = match err_payload() {
680 Some(payload) => format!("unwrap on Err: {}", payload.inspect()),
681 None => String::from("unwrap on Err"),
682 };
683 Err(error(line, detail))
684 }
685 "expect" => {
686 expect_args("expect", args, 1, line)?;
687 if let Some(v) = ok_payload() {
688 return Ok(Some(v));
689 }
690 let message = match &args[0] {
691 Value::Str(s) => s.as_str().to_string(),
692 other => format!("{}", other),
693 };
694 Err(error(line, message))
695 }
696 "unwrap_or" => {
697 expect_args("unwrap_or", args, 1, line)?;
698 if let Some(v) = ok_payload() {
699 return Ok(Some(v));
700 }
701 Ok(Some(args[0].clone()))
702 }
703 _ => Ok(None),
704 }
705}
706
707pub fn make_result_ok(value: Value) -> Value {
713 Value::new_enum_tuple(
714 String::from(crate::value::BUILTIN_MODULE_PATH),
715 String::from("Result"),
716 String::from("Ok"),
717 alloc_vec_of(value),
718 )
719}
720
721pub fn make_result_err(value: Value) -> Value {
723 Value::new_enum_tuple(
724 String::from(crate::value::BUILTIN_MODULE_PATH),
725 String::from("Result"),
726 String::from("Err"),
727 alloc_vec_of(value),
728 )
729}
730
731fn alloc_vec_of(value: Value) -> Vec<Value> {
732 let mut v = Vec::with_capacity(1);
733 v.push(value);
734 v
735}
736
737pub fn iter_method(
755 receiver: &Value,
756 method: &str,
757 args: &[Value],
758 line: u32,
759) -> Result<(Value, Option<Value>), BopError> {
760 use crate::builtins::{expect_args, make_iter_done, make_iter_next};
761 let cell = match receiver {
762 Value::Iter(cell) => cell,
763 _ => unreachable!("iter_method called on non-iterator receiver"),
764 };
765 match method {
766 "next" => {
767 expect_args("next", args, 0, line)?;
768 let mut inner = cell.borrow_mut();
769 match inner.next() {
770 Some(v) => Ok((make_iter_next(v), None)),
771 None => Ok((make_iter_done(), None)),
772 }
773 }
774 "iter" => {
775 expect_args("iter", args, 0, line)?;
776 Ok((receiver.clone(), None))
779 }
780 _ => Err(error(
781 line,
782 crate::error_messages::no_such_method("iter", method),
783 )),
784 }
785}