1use smallvec::SmallVec;
4use tracing::trace;
5
6use crate::interpreter::environment::Environment;
7use crate::interpreter::value::*;
8use crate::interpreter::{BuiltinContext, Interpreter};
9use crate::parser::ast::{Arg, Expr, Param};
10
11type PositionalArgs = SmallVec<[RValue; 4]>;
12
13type NamedArgs = SmallVec<[(String, RValue); 2]>;
14
15type EvaluatedCallArgs = (PositionalArgs, NamedArgs);
16
17struct ClosureCall<'a> {
18 func: &'a RValue,
19 params: &'a [Param],
20 body: &'a Expr,
21 closure_env: &'a Environment,
22 positional: &'a [RValue],
23 named: &'a [(String, RValue)],
24 call_expr: Option<Expr>,
25}
26
27impl Interpreter {
28 pub(super) fn eval_call(
29 &self,
30 func: &Expr,
31 args: &[Arg],
32 span: Option<crate::parser::ast::Span>,
33 env: &Environment,
34 ) -> Result<RValue, RFlow> {
35 eval_call(self, func, args, span, env)
36 }
37
38 pub fn call_function(
39 &self,
40 func: &RValue,
41 positional: &[RValue],
42 named: &[(String, RValue)],
43 env: &Environment,
44 ) -> Result<RValue, RFlow> {
45 call_function(self, func, positional, named, env)
46 }
47
48 pub(crate) fn call_function_with_call(
49 &self,
50 func: &RValue,
51 positional: &[RValue],
52 named: &[(String, RValue)],
53 env: &Environment,
54 call_expr: Option<Expr>,
55 ) -> Result<RValue, RFlow> {
56 call_function_with_call(self, func, positional, named, env, call_expr)
57 }
58}
59
60pub(super) fn eval_call(
61 interp: &Interpreter,
62 func: &Expr,
63 args: &[Arg],
64 span: Option<crate::parser::ast::Span>,
65 env: &Environment,
66) -> Result<RValue, RFlow> {
67 trace!(func = %expr_name(func), nargs = args.len(), "calling function");
68 let function = resolve_callable_for_call(interp, func, env)?;
69 let call_expr = Expr::Call {
70 func: Box::new(func.clone()),
71 args: args.to_vec(),
72 span,
73 };
74
75 if let Some(result) = try_call_special_builtin(interp, &function, args, env)? {
76 return Ok(result);
77 }
78
79 if let RValue::Function(RFunction::Closure {
83 params,
84 body,
85 env: closure_env,
86 }) = &function
87 {
88 let (positional, named) = create_promise_arguments(args, env);
89 return call_closure_lazy(
90 interp,
91 params,
92 body,
93 closure_env,
94 &function,
95 &positional,
96 &named,
97 call_expr,
98 );
99 }
100
101 let (positional, named) = evaluate_call_arguments(interp, args, env)?;
102 call_function_with_call(interp, &function, &positional, &named, env, Some(call_expr))
103}
104
105pub(super) fn call_function(
106 interp: &Interpreter,
107 func: &RValue,
108 positional: &[RValue],
109 named: &[(String, RValue)],
110 env: &Environment,
111) -> Result<RValue, RFlow> {
112 call_function_with_call(interp, func, positional, named, env, None)
113}
114
115#[tracing::instrument(level = "trace", name = "call_function", skip_all, fields(func_type))]
116pub(crate) fn call_function_with_call(
117 interp: &Interpreter,
118 func: &RValue,
119 positional: &[RValue],
120 named: &[(String, RValue)],
121 env: &Environment,
122 call_expr: Option<Expr>,
123) -> Result<RValue, RFlow> {
124 match func {
125 RValue::Function(RFunction::Builtin {
126 name,
127 implementation,
128 min_args,
129 max_args,
130 formals,
131 }) => {
132 tracing::Span::current().record("func_type", "builtin");
133 trace!(
134 builtin = name.as_str(),
135 positional = positional.len(),
136 named = named.len(),
137 "call builtin"
138 );
139 let actual_args = positional.len() + named.len();
142 Interpreter::ensure_builtin_min_arity(name, *min_args, actual_args)
143 .map_err(RFlow::from)?;
144 Interpreter::ensure_builtin_max_arity(name, *max_args, actual_args)
145 .map_err(RFlow::from)?;
146
147 let (forced_pos, forced_named) = interp.force_args(positional, named)?;
150
151 let (reordered_pos, remaining_named) =
154 reorder_builtin_args(&forced_pos, &forced_named, formals);
155 call_builtin(
156 interp,
157 name,
158 implementation,
159 0, None, &reordered_pos,
162 &remaining_named,
163 env,
164 )
165 }
166 RValue::Function(RFunction::Closure {
167 params,
168 body,
169 env: closure_env,
170 }) => {
171 tracing::Span::current().record("func_type", "closure");
172 trace!(
173 params = params.len(),
174 positional = positional.len(),
175 named = named.len(),
176 "call closure"
177 );
178 call_closure(
179 interp,
180 ClosureCall {
181 func,
182 params,
183 body,
184 closure_env,
185 positional,
186 named,
187 call_expr,
188 },
189 )
190 }
191 _ => Err(RError::new(
192 RErrorKind::Type,
193 "attempt to apply non-function".to_string(),
194 )
195 .into()),
196 }
197}
198
199fn resolve_callable_for_call(
200 interp: &Interpreter,
201 func: &Expr,
202 env: &Environment,
203) -> Result<RValue, RFlow> {
204 let value = interp.eval_in(func, env)?;
205 if matches!(value, RValue::Function(_)) {
206 return Ok(value);
207 }
208
209 if let Expr::Symbol(name) = func {
210 return env
211 .get_function(name)
212 .ok_or_else(|| RError::other("attempt to apply non-function".to_string()).into());
213 }
214
215 Ok(value)
216}
217
218fn try_call_special_builtin(
219 interp: &Interpreter,
220 function: &RValue,
221 args: &[Arg],
222 env: &Environment,
223) -> Result<Option<RValue>, RFlow> {
224 let RValue::Function(RFunction::Builtin {
225 name,
226 implementation,
227 min_args,
228 max_args,
229 ..
230 }) = function
231 else {
232 return Ok(None);
233 };
234
235 if name == "UseMethod" {
236 return interp.eval_use_method(args, env).map(Some);
237 }
238
239 if let BuiltinImplementation::PreEval(handler) = implementation {
240 Interpreter::ensure_builtin_min_arity(name, *min_args, args.len()).map_err(RFlow::from)?;
241 Interpreter::ensure_builtin_max_arity(name, *max_args, args.len()).map_err(RFlow::from)?;
242 let ctx = BuiltinContext::new(interp, env);
243 return handler(args, env, &ctx).map_err(Into::into).map(Some);
244 }
245
246 Ok(None)
247}
248
249fn evaluate_call_arguments(
250 interp: &Interpreter,
251 args: &[Arg],
252 env: &Environment,
253) -> Result<EvaluatedCallArgs, RFlow> {
254 let mut positional = PositionalArgs::new();
255 let mut named = NamedArgs::new();
256
257 for arg in args {
258 if let Some(name) = &arg.name {
259 if let Some(val_expr) = &arg.value {
260 named.push((name.clone(), interp.eval_in(val_expr, env)?));
261 } else {
262 named.push((name.clone(), RValue::Null));
263 }
264 continue;
265 }
266
267 let Some(val_expr) = &arg.value else {
268 continue;
269 };
270
271 if matches!(val_expr, Expr::Dots) {
272 expand_dots_arguments(env, &mut positional, &mut named);
273 } else {
274 positional.push(interp.eval_in(val_expr, env)?);
275 }
276 }
277
278 Ok((positional, named))
279}
280
281fn create_promise_arguments(args: &[Arg], env: &Environment) -> EvaluatedCallArgs {
290 let mut positional = PositionalArgs::new();
291 let mut named = NamedArgs::new();
292
293 for arg in args {
294 if let Some(name) = &arg.name {
295 if let Some(val_expr) = &arg.value {
296 named.push((name.clone(), RValue::promise(val_expr.clone(), env.clone())));
297 } else {
298 named.push((name.clone(), RValue::Null));
299 }
300 continue;
301 }
302
303 let Some(val_expr) = &arg.value else {
304 continue;
305 };
306
307 if matches!(val_expr, Expr::Dots) {
308 expand_dots_arguments(env, &mut positional, &mut named);
309 } else {
310 positional.push(RValue::promise(val_expr.clone(), env.clone()));
311 }
312 }
313
314 (positional, named)
315}
316
317fn expand_dots_arguments(
318 env: &Environment,
319 positional: &mut PositionalArgs,
320 named: &mut NamedArgs,
321) {
322 if let Some(RValue::List(list)) = env.get("...") {
323 for (opt_name, value) in &list.values {
324 if let Some(name) = opt_name {
325 named.push((name.clone(), value.clone()));
326 } else {
327 positional.push(value.clone());
328 }
329 }
330 }
331}
332
333fn reorder_builtin_args(
342 positional: &[RValue],
343 named: &[(String, RValue)],
344 formals: &[&str],
345) -> (PositionalArgs, NamedArgs) {
346 if formals.is_empty() || named.is_empty() {
347 return (
349 positional.iter().cloned().collect(),
350 named.iter().cloned().collect(),
351 );
352 }
353
354 let mut matched: Vec<Option<RValue>> = vec![None; formals.len()];
355
356 for (arg_name, value) in named {
358 if let Some(idx) = formals.iter().position(|f| *f == arg_name.as_str()) {
360 matched[idx] = Some(value.clone());
361 continue;
362 }
363 let candidates: Vec<usize> = formals
365 .iter()
366 .enumerate()
367 .filter(|(_, f)| f.starts_with(arg_name.as_str()))
368 .map(|(i, _)| i)
369 .collect();
370 if candidates.len() == 1 && matched[candidates[0]].is_none() {
371 matched[candidates[0]] = Some(value.clone());
372 }
373 }
374
375 let mut pos_iter = positional.iter();
377 for slot in &mut matched {
378 if slot.is_none() {
379 if let Some(val) = pos_iter.next() {
380 *slot = Some(val.clone());
381 }
382 }
383 }
384
385 let mut result: SmallVec<[RValue; 4]> = SmallVec::new();
389 for slot in &matched {
390 match slot {
391 Some(val) => result.push(val.clone()),
392 None => break,
393 }
394 }
395
396 result.extend(pos_iter.cloned());
398
399 (result, named.iter().cloned().collect())
402}
403
404#[allow(clippy::too_many_arguments)]
405fn call_builtin(
406 interp: &Interpreter,
407 name: &str,
408 implementation: &BuiltinImplementation,
409 min_args: usize,
410 max_args: Option<usize>,
411 positional: &[RValue],
412 named: &[(String, RValue)],
413 env: &Environment,
414) -> Result<RValue, RFlow> {
415 let actual_args = positional.len() + named.len();
416 Interpreter::ensure_builtin_min_arity(name, min_args, actual_args).map_err(RFlow::from)?;
417 Interpreter::ensure_builtin_max_arity(name, max_args, actual_args).map_err(RFlow::from)?;
418
419 match implementation {
420 BuiltinImplementation::Eager(func) => func(positional, named).map_err(Into::into),
421 BuiltinImplementation::Interpreter(handler) => {
422 handler(positional, named, &BuiltinContext::new(interp, env)).map_err(Into::into)
423 }
424 BuiltinImplementation::PreEval(_) => Err(RError::other(
425 "internal error: pre-eval builtin reached eager dispatch".to_string(),
426 )
427 .into()),
428 }
429}
430
431fn call_closure(interp: &Interpreter, closure_call: ClosureCall<'_>) -> Result<RValue, RFlow> {
432 let bound = interp.bind_closure_call(
433 closure_call.params,
434 closure_call.positional,
435 closure_call.named,
436 closure_call.closure_env,
437 closure_call.func,
438 closure_call.call_expr,
439 )?;
440 let call_env = bound.env;
441
442 interp.call_stack.borrow_mut().push(bound.frame);
443 let result = match interp.eval_in(closure_call.body, &call_env) {
444 Ok(value) => Ok(value),
445 Err(RFlow::Signal(RSignal::Return(value))) => Ok(value),
446 Err(err) => Err(err),
447 };
448 run_on_exit_handlers(interp, &call_env);
449 if result.is_err() {
450 interp.capture_traceback();
451 }
452 interp.call_stack.borrow_mut().pop();
453
454 result
455}
456
457#[allow(clippy::too_many_arguments)]
460fn call_closure_lazy(
461 interp: &Interpreter,
462 params: &[Param],
463 body: &Expr,
464 closure_env: &Environment,
465 func: &RValue,
466 positional: &[RValue],
467 named: &[(String, RValue)],
468 call_expr: Expr,
469) -> Result<RValue, RFlow> {
470 let bound = interp.bind_closure_call(
471 params,
472 positional,
473 named,
474 closure_env,
475 func,
476 Some(call_expr),
477 )?;
478 let call_env = bound.env;
479
480 interp.call_stack.borrow_mut().push(bound.frame);
481 let result = match interp.eval_in(body, &call_env) {
482 Ok(value) => Ok(value),
483 Err(RFlow::Signal(RSignal::Return(value))) => Ok(value),
484 Err(err) => Err(err),
485 };
486 run_on_exit_handlers(interp, &call_env);
487 if result.is_err() {
488 interp.capture_traceback();
489 }
490 interp.call_stack.borrow_mut().pop();
491
492 result
493}
494
495fn expr_name(expr: &Expr) -> &str {
497 match expr {
498 Expr::Symbol(name) => name.as_str(),
499 Expr::NsGet { name, .. } | Expr::NsGetInt { name, .. } => name.as_str(),
500 _ => "<expr>",
501 }
502}
503
504fn run_on_exit_handlers(interp: &Interpreter, call_env: &Environment) {
505 let on_exit_exprs = call_env.take_on_exit();
506 for expr in &on_exit_exprs {
507 if interp.eval_in(expr, call_env).is_err() {}
509 }
510}