Skip to main content

microcad_lang/eval/call/
call_method.rs

1// Copyright © 2025-2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! Argument value evaluation entity
5
6use microcad_lang_base::{PushDiag, SrcRef};
7
8use crate::{eval::*, lower::ir, model::Model, symbol::SymbolDef};
9
10/// Trait for calling methods of values
11pub trait CallMethod<T = Value> {
12    /// Evaluate method call into a value (if possible)
13    ///
14    /// - `name`: Name of the method
15    /// - `args`: Arguments for the method
16    /// - `context`: Evaluation context
17    fn call_method(
18        &self,
19        id: &ir::QualifiedName,
20        args: &ArgumentValueList,
21        context: &mut EvalContext,
22    ) -> EvalResult<T>;
23}
24
25impl CallMethod for Array {
26    fn call_method(
27        &self,
28        id: &ir::QualifiedName,
29        _: &ArgumentValueList,
30        context: &mut EvalContext,
31    ) -> EvalResult<Value> {
32        use crate::lower::SingleIdentifier;
33
34        Ok(
35            match id.single_identifier().expect("Single id").id().as_str() {
36                "count" => self.len().into(),
37                "first" | "head" => self.first(), // Keep head method as deprecated method.
38                "last" => self.last(),
39                "tail" => self.tail().into(),
40                "rev" => self.rev().into(),
41                "sorted" => self.sorted().into(),
42                "all_equal" => self.all_equal().into(),
43                "is_ascending" => self.is_ascending().into(),
44                "is_descending" => self.is_descending().into(),
45                _ => {
46                    context.error(id, EvalError::UnknownMethod(id.clone()))?;
47                    Value::None
48                }
49            },
50        )
51    }
52}
53
54impl CallMethod<Option<Model>> for Model {
55    fn call_method(
56        &self,
57        name: &ir::QualifiedName,
58        args: &ArgumentValueList,
59        context: &mut EvalContext,
60    ) -> EvalResult<Option<Model>> {
61        match context.lookup(name, LookupTarget::Method) {
62            Ok(symbol) => context.scope(
63                StackFrame::Call {
64                    symbol: symbol.clone(),
65                    args: args.clone(),
66                    src_ref: SrcRef::merge(name, args),
67                },
68                |context| {
69                    symbol.with_def(|def| match def {
70                        SymbolDef::Workbench(workbench_definition) => {
71                            let model = workbench_definition.call(
72                                SrcRef::merge(name, args),
73                                symbol.clone(),
74                                args,
75                                context,
76                            )?;
77
78                            Ok::<_, Box<EvalError>>(Some(model.replace_input_placeholders(self)))
79                        }
80                        SymbolDef::Builtin(builtin) => match builtin.call(args, context)? {
81                            Value::Model(model) => Ok(Some(model.replace_input_placeholders(self))),
82                            value => panic!("Builtin call returned {value} but no models."),
83                        },
84                        _ => {
85                            context.error(name, EvalError::SymbolCannotBeCalled(name.clone()))?;
86                            Ok(None)
87                        }
88                    })
89                },
90            ),
91            Err(err) => {
92                context.error(name, err)?;
93                Ok(None)
94            }
95        }
96    }
97}
98
99impl CallMethod for Value {
100    fn call_method(
101        &self,
102        id: &ir::QualifiedName,
103        args: &ArgumentValueList,
104        context: &mut EvalContext,
105    ) -> EvalResult<Value> {
106        match self {
107            Value::Integer(_) => eval_todo!(context, id, "call_method for Integer"),
108            Value::Quantity(_) => eval_todo!(context, id, "call_method for Quantity"),
109            Value::Bool(_) => eval_todo!(context, id, "call_method for Bool"),
110            Value::String(_) => eval_todo!(context, id, "call_method for String"),
111            Value::Tuple(_) => eval_todo!(context, id, "call_method for Tuple"),
112            Value::Matrix(_) => eval_todo!(context, id, "call_method for Matrix"),
113            Value::Array(array) => array.call_method(id, args, context),
114            Value::Model(model) => Ok(model
115                .call_method(id, args, context)?
116                .map(Value::Model)
117                .unwrap_or_default()),
118            _ => {
119                context.error(id, EvalError::UnknownMethod(id.clone()))?;
120                Ok(Value::None)
121            }
122        }
123    }
124}