1use crate::errors::{GentError, GentResult};
8use crate::interpreter::{Environment, Value};
9use crate::parser::ast::LambdaBody;
10use crate::runtime::tools::ToolRegistry;
11use crate::Span;
12
13pub fn call_array_method(arr: &mut Vec<Value>, method: &str, args: &[Value]) -> GentResult<Value> {
29 match method {
30 "length" => Ok(Value::Number(arr.len() as f64)),
31
32 "push" => {
33 let value = args.get(0).cloned().ok_or_else(|| GentError::TypeError {
34 expected: "argument for push()".to_string(),
35 got: "missing argument".to_string(),
36 span: Span::default(),
37 })?;
38 arr.push(value);
39 Ok(Value::Null)
40 }
41
42 "pop" => Ok(arr.pop().unwrap_or(Value::Null)),
43
44 "indexOf" => {
45 let target = args.get(0).ok_or_else(|| GentError::TypeError {
46 expected: "argument for indexOf()".to_string(),
47 got: "missing argument".to_string(),
48 span: Span::default(),
49 })?;
50 let index = arr.iter().position(|v| values_equal(v, target));
51 Ok(Value::Number(index.map(|i| i as f64).unwrap_or(-1.0)))
52 }
53
54 "join" => {
55 let sep = get_string_arg(args, 0, "join")?;
56 let strings: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
57 Ok(Value::String(strings.join(&sep)))
58 }
59
60 "slice" => {
61 let start = get_number_arg(args, 0, "slice")? as usize;
62 let end = get_number_arg(args, 1, "slice")? as usize;
63 let end = end.min(arr.len());
64 let start = start.min(end);
65 let sliced: Vec<Value> = arr[start..end].to_vec();
66 Ok(Value::Array(sliced))
67 }
68
69 "concat" => {
70 let other = get_array_arg(args, 0, "concat")?;
71 let mut result = arr.clone();
72 result.extend(other);
73 Ok(Value::Array(result))
74 }
75
76 _ => Err(GentError::UndefinedProperty {
77 property: method.to_string(),
78 type_name: "Array".to_string(),
79 span: Span::default(),
80 }),
81 }
82}
83
84fn values_equal(a: &Value, b: &Value) -> bool {
86 match (a, b) {
87 (Value::String(s1), Value::String(s2)) => s1 == s2,
88 (Value::Number(n1), Value::Number(n2)) => (n1 - n2).abs() < f64::EPSILON,
89 (Value::Boolean(b1), Value::Boolean(b2)) => b1 == b2,
90 (Value::Null, Value::Null) => true,
91 _ => false,
92 }
93}
94
95fn get_string_arg(args: &[Value], index: usize, method: &str) -> GentResult<String> {
97 args.get(index)
98 .and_then(|v| match v {
99 Value::String(s) => Some(s.clone()),
100 _ => None,
101 })
102 .ok_or_else(|| {
103 let got = args
104 .get(index)
105 .map(|v| v.type_name())
106 .unwrap_or_else(|| "missing argument".to_string());
107 GentError::TypeError {
108 expected: format!("String argument for {}()", method),
109 got,
110 span: Span::default(),
111 }
112 })
113}
114
115fn get_number_arg(args: &[Value], index: usize, method: &str) -> GentResult<f64> {
117 args.get(index)
118 .and_then(|v| match v {
119 Value::Number(n) => Some(*n),
120 _ => None,
121 })
122 .ok_or_else(|| {
123 let got = args
124 .get(index)
125 .map(|v| v.type_name())
126 .unwrap_or_else(|| "missing argument".to_string());
127 GentError::TypeError {
128 expected: format!("Number argument for {}()", method),
129 got,
130 span: Span::default(),
131 }
132 })
133}
134
135fn get_array_arg(args: &[Value], index: usize, method: &str) -> GentResult<Vec<Value>> {
137 args.get(index)
138 .and_then(|v| match v {
139 Value::Array(arr) => Some(arr.clone()),
140 _ => None,
141 })
142 .ok_or_else(|| {
143 let got = args
144 .get(index)
145 .map(|v| v.type_name())
146 .unwrap_or_else(|| "missing argument".to_string());
147 GentError::TypeError {
148 expected: format!("Array argument for {}()", method),
149 got,
150 span: Span::default(),
151 }
152 })
153}
154
155pub fn is_callback_method(method: &str) -> bool {
161 matches!(method, "map" | "filter" | "reduce" | "find")
162}
163
164pub fn call_array_method_with_callback<'a>(
166 arr: &'a [Value],
167 method: &'a str,
168 callback: &'a Value,
169 extra_args: &'a [Value],
170 env: &'a Environment,
171 tools: &'a ToolRegistry,
172) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
173 Box::pin(async move {
174 match method {
175 "map" => {
176 let mut results = Vec::new();
177 for item in arr {
178 let result = apply_callback(callback, &[item.clone()], env, tools).await?;
179 results.push(result);
180 }
181 Ok(Value::Array(results))
182 }
183
184 "filter" => {
185 let mut results = Vec::new();
186 for item in arr {
187 let result = apply_callback(callback, &[item.clone()], env, tools).await?;
188 if result.is_truthy() {
189 results.push(item.clone());
190 }
191 }
192 Ok(Value::Array(results))
193 }
194
195 "reduce" => {
196 let param_count = match callback {
198 Value::Lambda(lambda) => lambda.params.len(),
199 Value::Function(fn_val) => fn_val.params.len(),
200 _ => {
201 return Err(GentError::TypeError {
202 expected: "function or lambda".to_string(),
203 got: callback.type_name().to_string(),
204 span: Span::default(),
205 });
206 }
207 };
208 if param_count != 2 {
209 return Err(GentError::TypeError {
210 expected: "reduce callback requires 2 parameters".to_string(),
211 got: format!("{} parameters", param_count),
212 span: Span::default(),
213 });
214 }
215
216 let initial = extra_args.first().cloned().ok_or_else(|| GentError::TypeError {
217 expected: "initial value for reduce()".to_string(),
218 got: "missing argument".to_string(),
219 span: Span::default(),
220 })?;
221
222 let mut acc = initial;
223 for item in arr {
224 acc = apply_callback(callback, &[acc, item.clone()], env, tools).await?;
225 }
226 Ok(acc)
227 }
228
229 "find" => {
230 for item in arr {
231 let result = apply_callback(callback, &[item.clone()], env, tools).await?;
232 if result.is_truthy() {
233 return Ok(item.clone());
234 }
235 }
236 Ok(Value::Null)
237 }
238
239 _ => Err(GentError::UndefinedProperty {
240 property: method.to_string(),
241 type_name: "Array".to_string(),
242 span: Span::default(),
243 }),
244 }
245 })
246}
247
248fn apply_callback<'a>(
250 callback: &'a Value,
251 args: &'a [Value],
252 env: &'a Environment,
253 tools: &'a ToolRegistry,
254) -> std::pin::Pin<Box<dyn std::future::Future<Output = GentResult<Value>> + 'a>> {
255 Box::pin(async move {
256 match callback {
257 Value::Lambda(lambda) => {
258 let mut lambda_env = env.clone();
259 lambda_env.push_scope();
260
261 for (param, arg) in lambda.params.iter().zip(args.iter()) {
262 lambda_env.define(param, arg.clone());
263 }
264
265 let result = match &lambda.body {
266 LambdaBody::Expression(expr) => {
267 crate::interpreter::expr_eval::evaluate_expr(expr, &lambda_env)?
268 }
269 LambdaBody::Block(block) => {
270 crate::interpreter::block_eval::evaluate_block(block, &mut lambda_env, tools).await?
271 }
272 };
273
274 Ok(result)
275 }
276
277 Value::Function(fn_val) => {
278 let mut fn_env = env.clone();
279 fn_env.push_scope();
280
281 for (param, arg) in fn_val.params.iter().zip(args.iter()) {
282 fn_env.define(¶m.name, arg.clone());
283 }
284
285 let result = crate::interpreter::block_eval::evaluate_block(&fn_val.body, &mut fn_env, tools).await?;
286 Ok(result)
287 }
288
289 _ => Err(GentError::TypeError {
290 expected: "function or lambda".to_string(),
291 got: callback.type_name().to_string(),
292 span: Span::default(),
293 }),
294 }
295 })
296}