microcad_lang/eval/call/
mod.rs

1// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! µcad Call related evaluation entities
5
6#[macro_use]
7mod argument;
8mod call_method;
9mod call_trait;
10
11pub use call_method::*;
12pub use call_trait::*;
13
14use crate::{eval::*, syntax::*, value::*};
15
16use thiserror::Error;
17
18impl Eval<ArgumentValueList> for ArgumentList {
19    /// Evaluate into a [`ArgumentValueList`].
20    fn eval(&self, context: &mut Context) -> EvalResult<ArgumentValueList> {
21        self.iter()
22            .map(|arg| {
23                (
24                    arg.id.clone().unwrap_or(Identifier::none()),
25                    arg.eval_value(context),
26                )
27            })
28            .map(|(id, arg)| match arg {
29                Ok(arg) => Ok((id.clone(), arg)),
30                Err(err) => Err(err),
31            })
32            .collect()
33    }
34}
35
36impl Eval for Call {
37    fn eval(&self, context: &mut Context) -> EvalResult<Value> {
38        // find self in symbol table by own name
39        let symbol = match context.lookup(&self.name) {
40            Ok(symbol) => symbol,
41            Err(err) => {
42                context.error(self, err)?;
43                return Ok(Value::None);
44            }
45        };
46
47        // evaluate arguments
48        let args = match self.argument_list.eval(context) {
49            Ok(args) => args,
50            Err(err) => {
51                // For builtin calls ONLY: If arguments cannot be evaluated put
52                // the native argument code into a ArgumentValueList.
53                // E.g. this is needed to give assert_valid() a qualified name.
54                if matches!(symbol.borrow().def, SymbolDefinition::Builtin(..)) {
55                    self.argument_list
56                        .iter()
57                        .map(|arg| match context.source_code(&arg.value) {
58                            Ok(code) => Ok((
59                                arg.id.clone().unwrap_or(Identifier::none()),
60                                ArgumentValue::new(
61                                    code.into(),
62                                    arg.id.clone(),
63                                    arg.src_ref.clone(),
64                                ),
65                            )),
66                            Err(err) => Err(err),
67                        })
68                        .collect::<EvalResult<ArgumentValueList>>()?
69                } else {
70                    Err(err)?
71                }
72            }
73        };
74
75        log::debug!(
76            "{call} {name}({args})",
77            name = self.name,
78            call = crate::mark!(CALL),
79        );
80
81        match context.scope(
82            StackFrame::Call {
83                symbol: symbol.clone(),
84                args: args.clone(),
85                src_ref: self.src_ref(),
86            },
87            |context| match &symbol.borrow().def {
88                SymbolDefinition::Builtin(f) => f.call(&args, context),
89                SymbolDefinition::Workbench(w) => {
90                    if matches!(*w.kind, WorkbenchKind::Operation) {
91                        context.error(self, EvalError::CannotCallOperationWithoutWorkpiece)?;
92                        Ok(Value::None)
93                    } else {
94                        Ok(Value::Model(w.call(
95                            self.src_ref(),
96                            symbol.clone(),
97                            &args,
98                            context,
99                        )?))
100                    }
101                }
102                SymbolDefinition::Function(f) => f.call(&args, context),
103                def => {
104                    context.error(
105                        self,
106                        EvalError::SymbolCannotBeCalled(symbol.full_name(), Box::new(def.clone())),
107                    )?;
108                    Ok(Value::None)
109                }
110            },
111        ) {
112            Ok(value) => Ok(value),
113            Err(err) => {
114                context.error(self, err)?;
115                Ok(Value::None)
116            }
117        }
118    }
119}
120
121/// An error that occurred when looking for matching arguments between a call and a parameter definition.
122#[derive(Error, Debug)]
123pub enum MatchError {
124    /// Duplicated argument.
125    #[error("Duplicated argument: {0}")]
126    DuplicatedArgument(Identifier),
127    /// Occurs when a parameter was given in a call but not in the definition.
128    #[error("Parameter `{0}` is not defined.")]
129    ParameterNotDefined(Identifier),
130    /// Mismatching type.
131    #[error("Type mismatch for parameter `{0}`: expected `{1}`, got {2}")]
132    PositionalArgumentTypeMismatch(Identifier, Type, Type),
133    /// Parameter required by definition but given in the call.
134    #[error("Missing parameter: {0}")]
135    MissingParameter(Identifier),
136}