capnweb_core/
il_executor.rs

1use crate::il_extended::{ILContext, ILError, ILExpression, ILOperation, ILPlan};
2use crate::RpcTarget;
3use serde_json::Value;
4use std::future::Future;
5use std::pin::Pin;
6use std::sync::Arc;
7
8/// Executor for IL expressions with async support
9pub struct ILExecutor {
10    #[allow(dead_code)] // Will be used when capability operations are implemented
11    capabilities: Vec<Arc<dyn RpcTarget>>,
12}
13
14impl ILExecutor {
15    pub fn new() -> Self {
16        Self {
17            capabilities: Vec::new(),
18        }
19    }
20
21    pub fn with_capabilities(capabilities: Vec<Arc<dyn RpcTarget>>) -> Self {
22        Self { capabilities }
23    }
24
25    /// Execute an IL expression
26    pub fn execute<'a>(
27        &'a self,
28        expr: &'a ILExpression,
29        context: &'a mut ILContext,
30    ) -> Pin<Box<dyn Future<Output = Result<Value, ILError>> + 'a>> {
31        Box::pin(self.execute_impl(expr, context))
32    }
33
34    /// Internal implementation of execute
35    async fn execute_impl(
36        &self,
37        expr: &ILExpression,
38        context: &mut ILContext,
39    ) -> Result<Value, ILError> {
40        match expr {
41            ILExpression::Literal(value) => Ok(value.clone()),
42
43            ILExpression::Variable { var_ref } => context
44                .get_variable(*var_ref)
45                .cloned()
46                .ok_or(ILError::VariableNotFound(*var_ref)),
47
48            ILExpression::Plan { plan } => self.execute_plan(plan, context).await,
49
50            ILExpression::Bind { bind } => {
51                let value = self.execute(&bind.value, context).await?;
52                let _var_index = context.push_variable(value);
53                let result = self.execute(&bind.body, context).await?;
54                // Clean up the variable (optional, for stack-like behavior)
55                Ok(result)
56            }
57
58            ILExpression::If { if_expr } => {
59                let condition = self.execute(&if_expr.condition, context).await?;
60                if self.is_truthy(&condition) {
61                    self.execute(&if_expr.then_branch, context).await
62                } else {
63                    self.execute(&if_expr.else_branch, context).await
64                }
65            }
66
67            ILExpression::Get { get } => {
68                let object = self.execute(&get.object, context).await?;
69                self.get_property(&object, &get.property)
70            }
71
72            ILExpression::Call { call } => {
73                let target = self.execute(&call.target, context).await?;
74                let mut args = Vec::new();
75                for arg_expr in &call.args {
76                    args.push(self.execute(arg_expr, context).await?);
77                }
78                self.call_method(target, &call.method, args).await
79            }
80
81            ILExpression::MapOp { map } => {
82                let array = self.execute(&map.array, context).await?;
83                self.execute_map(array, &map.function, context).await
84            }
85
86            ILExpression::FilterOp { filter } => {
87                let array = self.execute(&filter.array, context).await?;
88                self.execute_filter(array, &filter.predicate, context).await
89            }
90
91            ILExpression::ReduceOp { reduce } => {
92                let array = self.execute(&reduce.array, context).await?;
93                let initial = self.execute(&reduce.initial, context).await?;
94                self.execute_reduce(array, &reduce.function, initial, context)
95                    .await
96            }
97        }
98    }
99
100    /// Execute an IL plan
101    async fn execute_plan(&self, plan: &ILPlan, context: &mut ILContext) -> Result<Value, ILError> {
102        // Execute all operations in sequence
103        for operation in &plan.operations {
104            match operation {
105                ILOperation::Store { store } => {
106                    let value = self.execute(&store.value, context).await?;
107                    context.set_variable(store.slot, value)?;
108                }
109                ILOperation::Execute { execute } => {
110                    // Execute but discard result
111                    self.execute(execute, context).await?;
112                }
113            }
114        }
115
116        // Return the final result
117        self.execute(&plan.result, context).await
118    }
119
120    /// Execute a map operation on an array
121    async fn execute_map(
122        &self,
123        array: Value,
124        function: &ILExpression,
125        context: &mut ILContext,
126    ) -> Result<Value, ILError> {
127        match array {
128            Value::Array(items) => {
129                let mut results = Vec::new();
130                for item in items {
131                    // Create a new binding for the current item
132                    let var_index = context.push_variable(item);
133
134                    // Apply the function with the item bound as a variable
135                    let result = if let ILExpression::Bind { .. } = function {
136                        // If the function is already a bind, execute it directly
137                        self.execute(function, context).await?
138                    } else {
139                        // Otherwise wrap it in a bind with the current item
140                        let bind_expr =
141                            ILExpression::bind(ILExpression::var(var_index), function.clone());
142                        self.execute(&bind_expr, context).await?
143                    };
144
145                    results.push(result);
146                }
147                Ok(Value::Array(results))
148            }
149            _ => Err(ILError::TypeError {
150                expected: "array".to_string(),
151                actual: self.value_type_name(&array),
152            }),
153        }
154    }
155
156    /// Execute a filter operation on an array
157    async fn execute_filter(
158        &self,
159        array: Value,
160        predicate: &ILExpression,
161        context: &mut ILContext,
162    ) -> Result<Value, ILError> {
163        match array {
164            Value::Array(items) => {
165                let mut results = Vec::new();
166                for item in items {
167                    // Create a new context for evaluating the predicate
168                    // The item becomes variable 0 in this new context
169                    let mut predicate_context = ILContext::new(context.captures.clone());
170                    predicate_context.set_variable(0, item.clone()).unwrap();
171
172                    // Execute the predicate with the item as variable 0
173                    let condition = self.execute(predicate, &mut predicate_context).await?;
174
175                    if self.is_truthy(&condition) {
176                        results.push(item);
177                    }
178                }
179                Ok(Value::Array(results))
180            }
181            _ => Err(ILError::TypeError {
182                expected: "array".to_string(),
183                actual: self.value_type_name(&array),
184            }),
185        }
186    }
187
188    /// Execute a reduce operation on an array
189    async fn execute_reduce(
190        &self,
191        array: Value,
192        function: &ILExpression,
193        mut accumulator: Value,
194        context: &mut ILContext,
195    ) -> Result<Value, ILError> {
196        match array {
197            Value::Array(items) => {
198                for item in items {
199                    // Bind both accumulator and current item
200                    let acc_index = context.push_variable(accumulator.clone());
201                    let item_index = context.push_variable(item);
202
203                    // Create a nested bind for both parameters
204                    let bind_expr = ILExpression::bind(
205                        ILExpression::var(acc_index),
206                        ILExpression::bind(ILExpression::var(item_index), function.clone()),
207                    );
208
209                    accumulator = self.execute(&bind_expr, context).await?;
210                }
211                Ok(accumulator)
212            }
213            _ => Err(ILError::TypeError {
214                expected: "array".to_string(),
215                actual: self.value_type_name(&array),
216            }),
217        }
218    }
219
220    /// Get a property from a value
221    fn get_property(&self, object: &Value, property: &str) -> Result<Value, ILError> {
222        match object {
223            Value::Object(map) => map.get(property).cloned().ok_or_else(|| {
224                ILError::ExecutionFailed(format!("Property '{}' not found", property))
225            }),
226            _ => Err(ILError::TypeError {
227                expected: "object".to_string(),
228                actual: self.value_type_name(object),
229            }),
230        }
231    }
232
233    /// Call a method on a capability
234    async fn call_method(
235        &self,
236        _target: Value,
237        _method: &str,
238        _args: Vec<Value>,
239    ) -> Result<Value, ILError> {
240        // This would need to resolve the target to a capability
241        // For now, return a placeholder
242        Err(ILError::ExecutionFailed(
243            "Method calls require capability resolution (not yet implemented)".to_string(),
244        ))
245    }
246
247    /// Check if a value is truthy
248    fn is_truthy(&self, value: &Value) -> bool {
249        match value {
250            Value::Null => false,
251            Value::Bool(b) => *b,
252            Value::Number(n) => n.as_f64().map(|f| f != 0.0).unwrap_or(false),
253            Value::String(s) => !s.is_empty(),
254            Value::Array(a) => !a.is_empty(),
255            Value::Object(_) => true,
256        }
257    }
258
259    /// Get a human-readable type name for a value
260    fn value_type_name(&self, value: &Value) -> String {
261        match value {
262            Value::Null => "null".to_string(),
263            Value::Bool(_) => "boolean".to_string(),
264            Value::Number(_) => "number".to_string(),
265            Value::String(_) => "string".to_string(),
266            Value::Array(_) => "array".to_string(),
267            Value::Object(_) => "object".to_string(),
268        }
269    }
270}
271
272impl Default for ILExecutor {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use crate::il_extended::FilterExpression;
282    use serde_json::json;
283
284    #[tokio::test]
285    async fn test_execute_literal() {
286        let executor = ILExecutor::new();
287        let mut context = ILContext::new(vec![]);
288
289        let expr = ILExpression::literal(json!(42));
290        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
291        assert_eq!(result, json!(42));
292    }
293
294    #[tokio::test]
295    async fn test_execute_variable() {
296        let executor = ILExecutor::new();
297        let mut context = ILContext::new(vec![]);
298        context.set_variable(0, json!("hello")).unwrap();
299
300        let expr = ILExpression::var(0);
301        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
302        assert_eq!(result, json!("hello"));
303    }
304
305    #[tokio::test]
306    async fn test_execute_bind() {
307        let executor = ILExecutor::new();
308        let mut context = ILContext::new(vec![]);
309
310        let expr = ILExpression::bind(
311            ILExpression::literal(json!(100)),
312            ILExpression::var(0), // Reference the bound variable
313        );
314
315        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
316        assert_eq!(result, json!(100));
317    }
318
319    #[tokio::test]
320    async fn test_execute_if() {
321        let executor = ILExecutor::new();
322        let mut context = ILContext::new(vec![]);
323
324        // Test true condition
325        let expr = ILExpression::if_expr(
326            ILExpression::literal(json!(true)),
327            ILExpression::literal(json!("then")),
328            ILExpression::literal(json!("else")),
329        );
330        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
331        assert_eq!(result, json!("then"));
332
333        // Test false condition
334        let expr = ILExpression::if_expr(
335            ILExpression::literal(json!(false)),
336            ILExpression::literal(json!("then")),
337            ILExpression::literal(json!("else")),
338        );
339        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
340        assert_eq!(result, json!("else"));
341    }
342
343    #[tokio::test]
344    async fn test_execute_get() {
345        let executor = ILExecutor::new();
346        let mut context = ILContext::new(vec![]);
347
348        let expr = ILExpression::get(
349            ILExpression::literal(json!({
350                "name": "John",
351                "age": 30
352            })),
353            "name".to_string(),
354        );
355
356        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
357        assert_eq!(result, json!("John"));
358    }
359
360    #[tokio::test]
361    async fn test_execute_map() {
362        let executor = ILExecutor::new();
363        let mut context = ILContext::new(vec![]);
364
365        // Map that doubles each number
366        let expr = ILExpression::map(
367            ILExpression::literal(json!([1, 2, 3])),
368            // This would normally be a more complex expression
369            // For testing, we'll just return the item as-is
370            ILExpression::var(0),
371        );
372
373        // Note: This test is simplified - in reality, the map function
374        // would need proper transformation logic
375        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
376        assert!(matches!(result, Value::Array(_)));
377    }
378
379    #[tokio::test]
380    async fn test_execute_filter() {
381        let executor = ILExecutor::new();
382        let mut context = ILContext::new(vec![]);
383
384        // Filter to keep only true values
385        let expr = ILExpression::FilterOp {
386            filter: FilterExpression {
387                array: Box::new(ILExpression::literal(json!([true, false, true]))),
388                predicate: Box::new(ILExpression::var(0)), // Use the item itself as the predicate
389            },
390        };
391
392        let result = executor.execute_impl(&expr, &mut context).await.unwrap();
393        match result {
394            Value::Array(items) => {
395                assert_eq!(items.len(), 2);
396                assert_eq!(items[0], json!(true));
397                assert_eq!(items[1], json!(true));
398            }
399            _ => panic!("Expected array result"),
400        }
401    }
402}