1use crate::{eval::*, value::*};
7
8#[derive(Default)]
10pub struct ArgumentMatch<'a> {
11 arguments: Vec<(&'a Identifier, &'a ArgumentValue)>,
12 params: Vec<(&'a Identifier, &'a ParameterValue)>,
13 result: Tuple,
14}
15
16impl<'a> ArgumentMatch<'a> {
17 pub fn find_match(
21 arguments: &'a ArgumentValueList,
22 params: &'a ParameterValueList,
23 ) -> EvalResult<Tuple> {
24 let am = Self::new(arguments, params)?;
25 am.check_exact_types(params)?;
26 Ok(am.result)
27 }
28
29 pub fn find_multi_match(
33 arguments: &'a ArgumentValueList,
34 params: &'a ParameterValueList,
35 ) -> EvalResult<Vec<Tuple>> {
36 Ok(Self::new(arguments, params)?.multiply(params))
37 }
38
39 fn new(arguments: &'a ArgumentValueList, params: &'a ParameterValueList) -> EvalResult<Self> {
41 let mut am = Self {
42 arguments: arguments.iter().map(|(id, v)| (id, v)).collect(),
43 params: params.iter().collect(),
44 result: Tuple::new_named(std::collections::HashMap::new(), arguments.src_ref()),
45 };
46
47 fn match_exact(left: &Type, right: &Type) -> bool {
48 left == right
49 }
50
51 fn match_auto(left: &Type, right: &Type) -> bool {
52 left.is_matching(right)
53 }
54
55 am.match_ids();
56 am.match_types(true, match_exact);
57 am.match_types(false, match_auto);
58 am.match_defaults();
59 am.match_types(false, match_auto);
60 am.check_missing()?;
61
62 Ok(am)
63 }
64
65 fn match_ids(&mut self) {
67 if !self.arguments.is_empty() {
68 log::trace!("find id match for:\n{self:?}");
69 self.arguments.retain(|(id, arg)| {
70 let id = match (id.is_empty(), &arg.inline_id) {
71 (true, Some(id)) => id,
72 _ => id,
73 };
74
75 if !id.is_empty() {
76 if let Some(n) = self.params.iter().position(|(i, _)| *i == id) {
77 if let Some(ty) = &self.params[n].1.specified_type {
78 if !arg.ty().is_matching(ty) {
79 return true;
80 }
81 }
82 let (id, _) = self.params.swap_remove(n);
83 log::trace!(
84 "{found} parameter by id: {id:?}",
85 found = crate::mark!(MATCH)
86 );
87 self.result.insert((*id).clone(), arg.value.clone());
88 return false;
89 }
90 }
91 true
92 });
93 }
94 }
95
96 fn match_types(&mut self, mut exclude_defaults: bool, match_fn: impl Fn(&Type, &Type) -> bool) {
98 if !self.arguments.is_empty() {
99 if exclude_defaults {
100 log::trace!("find type matches for (defaults):\n{self:?}");
101 } else {
102 log::trace!("find type matches for:\n{self:?}");
103 }
104 self.arguments.retain(|(arg_id, arg)| {
105 let same_type: Vec<_> = self
107 .params
108 .iter()
109 .enumerate()
110 .filter(|(..)| arg_id.is_empty())
111 .filter_map(|(n, (id, param))| {
112 if param.ty() == Type::Invalid
113 || if let Some(ty) = ¶m.specified_type {
114 match_fn(&arg.ty(), ty)
115 } else {
116 false
117 }
118 {
119 Some((n, id, param))
120 } else {
121 None
122 }
123 })
124 .collect();
125
126 if same_type.len() == 1 {
128 exclude_defaults = false;
129 }
130 let mut same_type = same_type
132 .iter()
133 .filter(|(.., param)| !exclude_defaults || param.default_value.is_none());
134
135 if let Some((n, id, _)) = same_type.next() {
136 if same_type.next().is_none() {
137 log::trace!(
138 "{found} parameter by type: {id:?}",
139 found = crate::mark!(MATCH)
140 );
141 self.result.insert((**id).clone(), arg.value.clone());
142 self.params.swap_remove(*n);
143 return false;
144 } else {
145 log::debug!("more than one parameter with that type")
146 }
147 } else {
148 log::debug!("no parameter with that type (or id mismatch)")
149 }
150 true
151 })
152 }
153 }
154
155 fn match_defaults(&mut self) {
157 if !self.params.is_empty() {
158 log::trace!("find default match for:\n{self:?}");
159 self.params.retain(|(id, param)| {
161 if let Some(def) = ¶m.default_value {
163 if def.ty() == param.ty() {
165 log::trace!(
166 "{found} argument by default: {id:?} = {def}",
167 found = crate::mark!(MATCH)
168 );
169 self.result.insert((*id).clone(), def.clone());
170 return false;
171 }
172 }
173 true
174 })
175 }
176 }
177
178 fn check_missing(&self) -> EvalResult<()> {
180 if !self.params.is_empty() {
181 let mut missing: IdentifierList =
182 self.params.iter().map(|(id, _)| (*id).clone()).collect();
183 missing.sort();
184 Err(EvalError::MissingArguments(missing))
185 } else if !self.arguments.is_empty() {
186 let mut too_many: IdentifierList =
187 self.arguments.iter().map(|(id, _)| (*id).clone()).collect();
188 too_many.sort();
189 Err(EvalError::TooManyArguments(too_many))
190 } else {
191 Ok(())
192 }
193 }
194
195 fn check_exact_types(&self, params: &ParameterValueList) -> EvalResult<()> {
196 let multipliers = Self::multipliers(&self.result, params);
197 if multipliers.is_empty() {
198 return Ok(());
199 }
200 Err(EvalError::MultiplicityNotAllowed(multipliers))
201 }
202
203 fn multiply(&self, params: &ParameterValueList) -> Vec<Tuple> {
207 let ids: IdentifierList = Self::multipliers(&self.result, params);
208 if !ids.is_empty() {
209 let mut result = Vec::new();
210 self.result.multiplicity(ids, |t| result.push(t));
211 result
212 } else {
213 vec![self.result.clone()]
214 }
215 }
216
217 fn multipliers(args: &impl ValueAccess, params: &ParameterValueList) -> IdentifierList {
219 let mut result: IdentifierList = params
220 .iter()
221 .filter_map(|(id, param)| {
222 if let Some(a) = args.by_id(id) {
223 if a.ty().is_array_of(¶m.ty()) {
224 return Some(id);
225 }
226 }
227 None
228 })
229 .cloned()
230 .collect();
231 result.sort();
232 result
233 }
234}
235
236impl std::fmt::Debug for ArgumentMatch<'_> {
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 write!(
239 f,
240 " Arguments: {args}\n Parameters: {params}",
241 args = self
242 .arguments
243 .iter()
244 .map(|(id, arg)| format!("{id:?} = {arg:?}"))
245 .collect::<Vec<_>>()
246 .join(", "),
247 params = self
248 .params
249 .iter()
250 .map(|(id, param)| format!("{id:?} = {param:?}"))
251 .collect::<Vec<_>>()
252 .join(", "),
253 )
254 }
255}
256
257#[test]
258fn argument_matching() {
259 use microcad_core::Length;
260 let params: ParameterValueList = [
261 crate::parameter!(a: Scalar),
262 crate::parameter!(b: Length),
263 crate::parameter!(c: Scalar),
264 crate::parameter!(d: Length = Length::mm(4.0)),
265 ]
266 .into_iter()
267 .collect();
268
269 let arguments: ArgumentValueList = [
270 crate::argument!(a: Scalar = 1.0),
271 crate::argument!(b: Length = Length::mm(2.0)),
272 crate::argument!(Scalar = 3.0),
273 ]
274 .into_iter()
275 .collect();
276
277 let result = ArgumentMatch::find_match(&arguments, ¶ms).expect("expect valid arguments");
278
279 assert_eq!(result, crate::tuple!("(a=1.0, b=2.0mm, c=3.0, d=4.0mm)"));
280}
281
282#[test]
283fn argument_match_fail() {
284 use microcad_core::Length;
285
286 let params: ParameterValueList = [
287 crate::parameter!(x: Scalar),
288 crate::parameter!(y: Length),
289 crate::parameter!(z: Area),
290 ]
291 .into_iter()
292 .collect();
293 let arguments: ArgumentValueList = [
294 crate::argument!(x: Scalar = 1.0),
295 crate::argument!(Length = Length::mm(1.0)),
296 ]
297 .into_iter()
298 .collect();
299 assert!(ArgumentMatch::find_match(&arguments, ¶ms).is_err());
300}