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