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