Skip to main content

microcad_lang/eval/call/
mod.rs

1// Copyright © 2024-2026 The µcad authors <info@microcad.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::*;
13use microcad_lang_base::SrcReferrer;
14
15use crate::{eval::*, symbol::SymbolDef, syntax::*, value::*};
16
17use thiserror::Error;
18
19impl Eval<ArgumentValueList> for ArgumentList {
20    /// Evaluate into a [`ArgumentValueList`].
21    fn eval(&self, context: &mut EvalContext) -> EvalResult<ArgumentValueList> {
22        self.iter()
23            .map(|arg| {
24                (
25                    arg.id.clone().unwrap_or(Identifier::none()),
26                    arg.eval(context),
27                )
28            })
29            .map(|(id, arg)| match arg {
30                Ok(arg) => Ok((id.clone(), arg)),
31                Err(err) => Err(err),
32            })
33            .collect()
34    }
35}
36
37impl Eval for Call {
38    fn eval(&self, context: &mut EvalContext) -> EvalResult<Value> {
39        // find self in symbol table by own name
40        let symbol = match context.lookup(&self.name, LookupTarget::Function) {
41            Ok(symbol) => symbol,
42            Err(err) => {
43                context.error(self, err)?;
44                return Ok(Value::None);
45            }
46        };
47
48        // evaluate arguments
49        let args: ArgumentValueList = self.argument_list.eval(context)?;
50
51        log::debug!(
52            "{call} {name:?}({args:?})",
53            name = self.name,
54            call = microcad_lang_base::mark!(CALL),
55        );
56
57        match context.scope(
58            StackFrame::Call {
59                symbol: symbol.clone(),
60                args: args.clone(),
61                src_ref: self.src_ref(),
62            },
63            |context| {
64                symbol.with_def(|def| match def {
65                    SymbolDef::Builtin(f) => f.call(&args, context),
66                    SymbolDef::Workbench(w) => {
67                        if matches!(*w.kind, WorkbenchKind::Operation) {
68                            context.error(self, EvalError::CannotCallOperationWithoutWorkpiece)?;
69                            Ok(Value::None)
70                        } else {
71                            Ok(Value::Model(w.call(
72                                self.src_ref(),
73                                symbol.clone(),
74                                &args,
75                                context,
76                            )?))
77                        }
78                    }
79                    SymbolDef::Function(f) => f.call(&args, context),
80                    _ => {
81                        context.error(self, EvalError::SymbolCannotBeCalled(symbol.full_name()))?;
82                        Ok(Value::None)
83                    }
84                })
85            },
86        ) {
87            Ok(value) => Ok(value),
88            Err(err) => {
89                context.error(self, err)?;
90                Ok(Value::None)
91            }
92        }
93    }
94}
95
96/// An error that occurred when looking for matching arguments between a call and a parameter definition.
97#[derive(Error, Debug)]
98pub enum MatchError {
99    /// Duplicated argument.
100    #[error("Duplicated argument: {0}")]
101    DuplicatedArgument(Identifier),
102    /// Occurs when a parameter was given in a call but not in the definition.
103    #[error("Parameter `{0}` is not defined.")]
104    ParameterNotDefined(Identifier),
105    /// Mismatching type.
106    #[error("Type mismatch for parameter `{0}`: expected `{1}`, got {2}")]
107    PositionalArgumentTypeMismatch(Identifier, Type, Type),
108    /// Parameter required by definition but given in the call.
109    #[error("Missing parameter: {0}")]
110    MissingParameter(Identifier),
111}