r/interpreter/
arguments.rs1use std::collections::{HashMap, HashSet};
11
12use crate::interpreter::call::CallFrame;
13use crate::interpreter::environment::Environment;
14use crate::interpreter::value::{RError, RErrorKind, RFlow, RList, RValue};
15use crate::interpreter::Interpreter;
16use crate::parser::ast::{Expr, Param};
17use itertools::Itertools;
18
19pub(crate) struct BoundClosureCall {
20 pub env: Environment,
21 pub frame: CallFrame,
22}
23
24impl Interpreter {
25 pub(crate) fn bind_closure_call(
26 &self,
27 params: &[Param],
28 positional: &[RValue],
29 named: &[(String, RValue)],
30 closure_env: &Environment,
31 function: &RValue,
32 call: Option<Expr>,
33 ) -> Result<BoundClosureCall, RFlow> {
34 let call_env = Environment::new_child(closure_env);
35 let has_dots = params.iter().any(|p| p.is_dots);
36
37 let formal_names: Vec<&str> = params
39 .iter()
40 .filter(|p| !p.is_dots)
41 .map(|p| p.name.as_str())
42 .collect();
43
44 let mut named_to_formal: HashMap<usize, &str> = HashMap::new();
46 let mut matched_formals: HashSet<&str> = HashSet::new();
47
48 for (i, (arg_name, _)) in named.iter().enumerate() {
50 if let Some(&formal) = formal_names.iter().find(|&&f| f == arg_name) {
51 if !matched_formals.contains(formal) {
52 matched_formals.insert(formal);
53 named_to_formal.insert(i, formal);
54 }
55 }
56 }
57
58 for (i, (arg_name, _)) in named.iter().enumerate() {
60 if named_to_formal.contains_key(&i) {
61 continue;
62 }
63 let candidates: Vec<&str> = formal_names
64 .iter()
65 .filter(|&&f| !matched_formals.contains(f) && f.starts_with(arg_name.as_str()))
66 .copied()
67 .collect();
68 match candidates.len() {
69 1 => {
70 matched_formals.insert(candidates[0]);
71 named_to_formal.insert(i, candidates[0]);
72 }
73 n if n > 1 && !has_dots => {
74 return Err(RError::new(
75 RErrorKind::Argument,
76 format!(
77 "argument '{}' matches multiple formal arguments: {}",
78 arg_name,
79 candidates.join(", ")
80 ),
81 )
82 .into());
83 }
84 _ => {} }
86 }
87
88 let formal_to_named: HashMap<&str, usize> = named_to_formal
90 .iter()
91 .map(|(&idx, &formal)| (formal, idx))
92 .collect();
93
94 let mut pos_idx = 0usize;
96 let mut dots_vals: Vec<(Option<String>, RValue)> = Vec::new();
97 let mut formal_args = HashSet::new();
98 let mut supplied_args = HashSet::new();
99
100 for param in params {
101 if param.is_dots {
102 formal_args.insert("...".to_string());
103 while pos_idx < positional.len() {
105 dots_vals.push((None, positional[pos_idx].clone()));
106 pos_idx += 1;
107 }
108 for (i, (name, value)) in named.iter().enumerate() {
110 if !named_to_formal.contains_key(&i) {
111 dots_vals.push((Some(name.clone()), value.clone()));
112 }
113 }
114 continue;
115 }
116
117 formal_args.insert(param.name.clone());
118
119 if let Some(&named_idx) = formal_to_named.get(param.name.as_str()) {
120 call_env.set(param.name.clone(), named[named_idx].1.clone());
122 supplied_args.insert(param.name.clone());
123 } else if pos_idx < positional.len() {
124 call_env.set(param.name.clone(), positional[pos_idx].clone());
126 supplied_args.insert(param.name.clone());
127 pos_idx += 1;
128 } else if let Some(default) = ¶m.default {
129 let value = RValue::promise(default.clone(), call_env.clone());
132 call_env.set(param.name.clone(), value);
133 }
134 }
135
136 if !has_dots {
138 if pos_idx < positional.len() {
139 return Err(RError::new(
140 RErrorKind::Argument,
141 format!(
142 "unused argument{}",
143 if positional.len() - pos_idx == 1 {
144 ""
145 } else {
146 "s"
147 }
148 ),
149 )
150 .into());
151 }
152 let unused_named: Vec<&str> = named
153 .iter()
154 .enumerate()
155 .filter(|(i, _)| !named_to_formal.contains_key(i))
156 .map(|(_, (name, _))| name.as_str())
157 .collect();
158 if !unused_named.is_empty() {
159 return Err(RError::new(
160 RErrorKind::Argument,
161 format!(
162 "unused argument{} ({})",
163 if unused_named.len() == 1 { "" } else { "s" },
164 unused_named.iter().map(|n| format!("{n} = ")).join(", ")
165 ),
166 )
167 .into());
168 }
169 }
170
171 if has_dots {
172 call_env.set("...".to_string(), RValue::List(RList::new(dots_vals)));
173 }
174
175 let supplied_arg_count = supplied_args.len();
176 Ok(BoundClosureCall {
177 env: call_env.clone(),
178 frame: CallFrame {
179 call,
180 function: function.clone(),
181 env: call_env,
182 formal_args,
183 supplied_args,
184 supplied_positional: positional.iter().cloned().collect(),
185 supplied_named: named.iter().cloned().collect(),
186 supplied_arg_count,
187 is_native_boundary: false,
188 },
189 })
190 }
191}