1use crate::{eval::*, model::*, render::Hashed, syntax::*};
7
8impl WorkbenchDefinition {
9 fn eval_to_model<'a>(
15 &'a self,
16 call_src_ref: SrcRef,
17 creator: Creator,
18 init: Option<&'a InitDefinition>,
19 context: &mut EvalContext,
20 ) -> EvalResult<Model> {
21 log::debug!(
22 "Evaluating model of `{id:?}` {kind}",
23 id = self.id,
24 kind = self.kind
25 );
26
27 let arguments = creator.arguments.clone();
28
29 let (mut properties, non_properties): (Vec<_>, Vec<_>) = arguments
31 .named_iter()
32 .map(|(id, value)| (id.clone(), value.clone()))
33 .partition(|(id, _)| self.plan.contains_key(id));
34
35 let missing: Vec<_> = self
37 .plan
38 .iter()
39 .filter(|param| !properties.iter().any(|(id, _)| param.id == *id))
40 .map(|param| param.id.clone())
41 .collect();
42 missing
43 .into_iter()
44 .for_each(|id| properties.push((id, Value::None)));
45
46 log::trace!("Properties: {properties:?}");
47 log::trace!("Non-Properties: {non_properties:?}");
48
49 let model = ModelBuilder::new(
51 Element::Workpiece(Workpiece {
52 kind: *self.kind,
53 properties: properties.into_iter().collect(),
55 creator: Hashed::new(creator),
56 }),
57 call_src_ref,
58 )
59 .attributes(self.attribute_list.eval(context)?)
60 .build();
61
62 context.scope(
63 StackFrame::Workbench(model, self.id.clone(), Default::default()),
64 |context| {
65 let model = context.get_model()?;
66
67 if let Some(init) = init {
69 log::trace!(
70 "Initializing`{id:?}` {kind}",
71 id = self.id,
72 kind = self.kind
73 );
74 if let Err(err) = init.eval(non_properties.into_iter().collect(), context) {
75 context.error(&self.src_ref(), err)?;
76 }
77 }
78
79 log::trace!("Run body`{id:?}` {kind}", id = self.id, kind = self.kind);
81 model.append_children(self.body.statements.eval(context)?);
82
83 {
85 let model_ = model.borrow();
86 match &*model_.element {
87 Element::Workpiece(workpiece) => {
88 let output_type = model.deduce_output_type();
89
90 let result = workpiece.check_output_type(output_type);
91 match result {
92 Ok(()) => {}
93 Err(EvalError::WorkbenchNoOutput(..)) => {
94 context.warning(&self.src_ref(), result.expect_err("Error"))?;
95 }
96 result => {
97 context.error(&self.src_ref(), result.expect_err("Error"))?;
98 }
99 }
100 }
101 _ => panic!("A workbench must produce a workpiece."),
102 }
103 }
104
105 Ok(model)
106 },
107 )
108 }
109}
110
111impl WorkbenchDefinition {
112 pub fn call(
119 &self,
120 call_src_ref: SrcRef,
121 symbol: Symbol,
122 arguments: &ArgumentValueList,
123 context: &mut EvalContext,
124 ) -> EvalResult<Model> {
125 log::debug!(
126 "{call} workbench {kind} {id:?}({arguments:?})",
127 call = crate::mark!(CALL),
128 id = self.id,
129 kind = self.kind
130 );
131
132 let mut models = Models::default();
134
135 let matches: Vec<_> = std::iter::once((
137 None,
138 self.plan
139 .eval(context)
140 .and_then(|params| ArgumentMatch::find_multi_match(arguments, ¶ms)),
141 ))
142 .chain(self.inits().map(|init| {
144 (
145 Some(init),
146 init.parameters
147 .eval(context)
148 .and_then(|params| ArgumentMatch::find_multi_match(arguments, ¶ms)),
149 )
150 }))
151 .inspect(|(i, m)| {
153 let result = match m {
154 Ok(m) => format!(
155 "{match_} [{priority:>10}]",
156 priority = m.priority,
157 match_ = crate::mark!(MATCH)
158 ),
159 Err(_) => crate::mark!(NO_MATCH),
160 };
161 if let Some(i) = i {
162 log::debug!("{result} {}::init({})", symbol.full_name(), i.parameters)
163 } else {
164 log::debug!("{result} {}({})", symbol.full_name(), self.plan)
165 }
166 })
167 .filter_map(|(i, m)| if let Ok(m) = m { Some((i, m)) } else { None })
169 .collect();
170
171 let matches = Priority::high_to_low().iter().find_map(|priority| {
173 let matches: Vec<_> = matches
174 .iter()
175 .filter(|(_, m)| m.priority == *priority)
176 .collect();
177 if matches.is_empty() {
178 None
179 } else {
180 Some(matches)
181 }
182 });
183
184 if let Some(mut matches) = matches {
185 if matches.len() > 1 {
186 let ambiguous = matches
187 .iter()
188 .map(|(init, _)| match init {
189 Some(init) => {
190 format!(
191 "{name}::init({params})",
192 name = symbol.full_name(),
193 params = init.parameters
194 )
195 }
196 None => format!(
197 "{name:?}({params})",
198 name = symbol.full_name(),
199 params = self.plan
200 ),
201 })
202 .collect::<Vec<_>>();
203 log::debug!(
204 "{match_} Ambiguous initialization: {name}({arguments})\nCould be one of:\n{ambiguous}",
205 name = symbol.full_name(),
206 ambiguous = ambiguous.join("\n"),
207 match_ = crate::mark!(AMBIGUOUS)
208 );
209 context.error(
210 arguments,
211 EvalError::AmbiguousInitialization {
212 src_ref: call_src_ref,
213 name: self.id.clone(),
214 actual_params: arguments.to_string(),
215 ambiguous_params: ambiguous,
216 },
217 )?;
218 } else if let Some(matched) = matches.pop() {
219 let what = if matched.0.is_none() {
220 "Building plan"
221 } else {
222 "Initializer"
223 };
224 log::debug!(
225 "{match_} {what}: {}",
226 matched
227 .1
228 .args
229 .iter()
230 .map(|m| format!("{m:?}"))
231 .collect::<Vec<_>>()
232 .join("\n"),
233 match_ = crate::mark!(MATCH!)
234 );
235
236 for arguments in matched.1.args.iter() {
238 models.push(self.eval_to_model(
239 call_src_ref.clone(),
240 Creator::new(symbol.clone(), arguments.clone()),
241 matched.0,
242 context,
243 )?);
244 }
245 }
246 } else {
247 log::debug!(
248 "{match_} Neither the building plan nor any initializer matches arguments",
249 match_ = crate::mark!(NO_MATCH!)
250 );
251 context.error(
252 arguments,
253 EvalError::NoInitializationFound {
254 src_ref: call_src_ref,
255 name: self.id.clone(),
256 actual_params: arguments.to_string(),
257 possible_params: self.possible_params(),
258 },
259 )?;
260 }
261
262 Ok(models.to_multiplicity(self.src_ref()))
263 }
264}