1use microcad_lang_base::{PushDiag, SrcRef};
7
8use crate::{eval::*, model::Model, symbol::SymbolDef, syntax::*};
9
10pub trait CallMethod<T = Value> {
12 fn call_method(
18 &self,
19 id: &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: &QualifiedName,
29 _: &ArgumentValueList,
30 context: &mut EvalContext,
31 ) -> EvalResult<Value> {
32 Ok(
33 match id.single_identifier().expect("Single id").id().as_str() {
34 "count" => self.len().into(),
35 "first" | "head" => self.first(), "last" => self.last(),
37 "tail" => self.tail().into(),
38 "rev" => self.rev().into(),
39 "sorted" => self.sorted().into(),
40 "all_equal" => self.all_equal().into(),
41 "is_ascending" => self.is_ascending().into(),
42 "is_descending" => self.is_descending().into(),
43 _ => {
44 context.error(id, EvalError::UnknownMethod(id.clone()))?;
45 Value::None
46 }
47 },
48 )
49 }
50}
51
52impl CallMethod<Option<Model>> for Model {
53 fn call_method(
54 &self,
55 name: &QualifiedName,
56 args: &ArgumentValueList,
57 context: &mut EvalContext,
58 ) -> EvalResult<Option<Model>> {
59 match context.lookup(name, LookupTarget::Method) {
60 Ok(symbol) => context.scope(
61 StackFrame::Call {
62 symbol: symbol.clone(),
63 args: args.clone(),
64 src_ref: SrcRef::merge(name, args),
65 },
66 |context| {
67 symbol.with_def(|def| match def {
68 SymbolDef::Workbench(workbench_definition) => {
69 let model = workbench_definition.call(
70 SrcRef::merge(name, args),
71 symbol.clone(),
72 args,
73 context,
74 )?;
75
76 Ok::<_, EvalError>(Some(model.replace_input_placeholders(self)))
77 }
78 SymbolDef::Builtin(builtin) => match builtin.call(args, context)? {
79 Value::Model(model) => Ok(Some(model.replace_input_placeholders(self))),
80 value => panic!("Builtin call returned {value} but no models."),
81 },
82 _ => {
83 context.error(name, EvalError::SymbolCannotBeCalled(name.clone()))?;
84 Ok(None)
85 }
86 })
87 },
88 ),
89 Err(err) => {
90 context.error(name, err)?;
91 Ok(None)
92 }
93 }
94 }
95}
96
97impl CallMethod for Value {
98 fn call_method(
99 &self,
100 id: &QualifiedName,
101 args: &ArgumentValueList,
102 context: &mut EvalContext,
103 ) -> EvalResult<Value> {
104 match self {
105 Value::Integer(_) => eval_todo!(context, id, "call_method for Integer"),
106 Value::Quantity(_) => eval_todo!(context, id, "call_method for Quantity"),
107 Value::Bool(_) => eval_todo!(context, id, "call_method for Bool"),
108 Value::String(_) => eval_todo!(context, id, "call_method for String"),
109 Value::Tuple(_) => eval_todo!(context, id, "call_method for Tuple"),
110 Value::Matrix(_) => eval_todo!(context, id, "call_method for Matrix"),
111 Value::Array(array) => array.call_method(id, args, context),
112 Value::Model(model) => Ok(model
113 .call_method(id, args, context)?
114 .map(Value::Model)
115 .unwrap_or_default()),
116 _ => {
117 context.error(id, EvalError::UnknownMethod(id.clone()))?;
118 Ok(Value::None)
119 }
120 }
121 }
122}
123
124#[test]
125fn call_list_method() {
126 let list = Array::from_values(
127 ValueList::new(vec![
128 Value::Quantity(Quantity::new(3.0, QuantityType::Scalar)),
129 Value::Quantity(Quantity::new(3.0, QuantityType::Scalar)),
130 Value::Quantity(Quantity::new(3.0, QuantityType::Scalar)),
131 ]),
132 crate::ty::Type::Quantity(QuantityType::Scalar),
133 );
134
135 if let Value::Bool(result) = list
136 .call_method(
137 &"all_equal".into(),
138 &ArgumentValueList::default(),
139 &mut EvalContext::default(),
140 )
141 .expect("test error")
142 {
143 assert!(result);
144 } else {
145 panic!("Test failed");
146 }
147}