capnweb_core/
il_extended.rs

1use crate::CapId;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use serde_json::Value;
4
5/// Extended IL expressions for complete Cap'n Web protocol support
6/// Includes variable references, bindings, conditionals, and plans
7#[derive(Debug, Clone, PartialEq)]
8pub enum ILExpression {
9    /// Direct JSON value
10    Literal(Value),
11
12    /// Variable reference: ["var", index]
13    Variable { var_ref: u32 },
14
15    /// Plan execution: ["plan", ...operations]
16    Plan { plan: ILPlan },
17
18    /// Variable binding: ["bind", value, body]
19    Bind { bind: BindExpression },
20
21    /// Conditional: ["if", condition, then_expr, else_expr]
22    If { if_expr: Box<IfExpression> },
23
24    /// Property access: ["get", object, property]
25    Get { get: GetExpression },
26
27    /// Function call: ["call", target, method, ...args]
28    Call { call: CallExpression },
29
30    /// Array map operation: ["map", array, function]
31    MapOp { map: MapExpression },
32
33    /// Filter operation: ["filter", array, predicate]
34    FilterOp { filter: FilterExpression },
35
36    /// Reduce operation: ["reduce", array, function, initial]
37    ReduceOp { reduce: ReduceExpression },
38}
39
40// Custom serialization to match Cap'n Web protocol array notation
41impl Serialize for ILExpression {
42    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
43    where
44        S: Serializer,
45    {
46        use serde_json::json;
47
48        let value = match self {
49            ILExpression::Literal(v) => v.clone(),
50            ILExpression::Variable { var_ref } => json!(["var", var_ref]),
51            ILExpression::Plan { plan } => {
52                let mut arr = vec![json!("plan")];
53                // Add plan operations
54                arr.push(json!(plan.captures));
55                arr.push(json!(plan.operations));
56                arr.push(serde_json::to_value(&*plan.result).unwrap());
57                Value::Array(arr)
58            }
59            ILExpression::Bind { bind } => {
60                json!(["bind", bind.value, bind.body])
61            }
62            ILExpression::If { if_expr } => {
63                json!([
64                    "if",
65                    if_expr.condition,
66                    if_expr.then_branch,
67                    if_expr.else_branch
68                ])
69            }
70            ILExpression::Get { get } => {
71                json!(["get", get.object, get.property])
72            }
73            ILExpression::Call { call } => {
74                let mut arr = vec![
75                    json!("call"),
76                    serde_json::to_value(&*call.target).unwrap(),
77                    json!(call.method),
78                ];
79                for arg in &call.args {
80                    arr.push(serde_json::to_value(arg).unwrap());
81                }
82                Value::Array(arr)
83            }
84            ILExpression::MapOp { map } => {
85                json!(["map", map.array, map.function])
86            }
87            ILExpression::FilterOp { filter } => {
88                json!(["filter", filter.array, filter.predicate])
89            }
90            ILExpression::ReduceOp { reduce } => {
91                json!(["reduce", reduce.array, reduce.function, reduce.initial])
92            }
93        };
94
95        value.serialize(serializer)
96    }
97}
98
99// Custom deserialization to parse Cap'n Web protocol array notation
100impl<'de> Deserialize<'de> for ILExpression {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: Deserializer<'de>,
104    {
105        let value = Value::deserialize(deserializer)?;
106
107        // Check if this is an array with a type marker
108        if let Value::Array(arr) = &value {
109            if !arr.is_empty() {
110                if let Some(Value::String(type_str)) = arr.first() {
111                    return match type_str.as_str() {
112                        "var" => {
113                            if arr.len() != 2 {
114                                return Err(serde::de::Error::custom(
115                                    "var requires exactly 2 elements",
116                                ));
117                            }
118                            let index = arr[1].as_u64().ok_or_else(|| {
119                                serde::de::Error::custom("var index must be a number")
120                            })? as u32;
121                            Ok(ILExpression::Variable { var_ref: index })
122                        }
123                        "bind" => {
124                            if arr.len() != 3 {
125                                return Err(serde::de::Error::custom(
126                                    "bind requires exactly 3 elements",
127                                ));
128                            }
129                            let value = Box::new(
130                                serde_json::from_value(arr[1].clone())
131                                    .map_err(serde::de::Error::custom)?,
132                            );
133                            let body = Box::new(
134                                serde_json::from_value(arr[2].clone())
135                                    .map_err(serde::de::Error::custom)?,
136                            );
137                            Ok(ILExpression::Bind {
138                                bind: BindExpression { value, body },
139                            })
140                        }
141                        "if" => {
142                            if arr.len() != 4 {
143                                return Err(serde::de::Error::custom(
144                                    "if requires exactly 4 elements",
145                                ));
146                            }
147                            let condition = Box::new(
148                                serde_json::from_value(arr[1].clone())
149                                    .map_err(serde::de::Error::custom)?,
150                            );
151                            let then_branch = Box::new(
152                                serde_json::from_value(arr[2].clone())
153                                    .map_err(serde::de::Error::custom)?,
154                            );
155                            let else_branch = Box::new(
156                                serde_json::from_value(arr[3].clone())
157                                    .map_err(serde::de::Error::custom)?,
158                            );
159                            Ok(ILExpression::If {
160                                if_expr: Box::new(IfExpression {
161                                    condition,
162                                    then_branch,
163                                    else_branch,
164                                }),
165                            })
166                        }
167                        "get" => {
168                            if arr.len() != 3 {
169                                return Err(serde::de::Error::custom(
170                                    "get requires exactly 3 elements",
171                                ));
172                            }
173                            let object = Box::new(
174                                serde_json::from_value(arr[1].clone())
175                                    .map_err(serde::de::Error::custom)?,
176                            );
177                            let property = arr[2]
178                                .as_str()
179                                .ok_or_else(|| {
180                                    serde::de::Error::custom("get property must be a string")
181                                })?
182                                .to_string();
183                            Ok(ILExpression::Get {
184                                get: GetExpression { object, property },
185                            })
186                        }
187                        "call" => {
188                            if arr.len() < 3 {
189                                return Err(serde::de::Error::custom(
190                                    "call requires at least 3 elements",
191                                ));
192                            }
193                            let target = Box::new(
194                                serde_json::from_value(arr[1].clone())
195                                    .map_err(serde::de::Error::custom)?,
196                            );
197                            let method = arr[2]
198                                .as_str()
199                                .ok_or_else(|| {
200                                    serde::de::Error::custom("call method must be a string")
201                                })?
202                                .to_string();
203                            let args = arr[3..]
204                                .iter()
205                                .map(|v| serde_json::from_value(v.clone()))
206                                .collect::<Result<Vec<_>, _>>()
207                                .map_err(serde::de::Error::custom)?;
208                            Ok(ILExpression::Call {
209                                call: CallExpression {
210                                    target,
211                                    method,
212                                    args,
213                                },
214                            })
215                        }
216                        "map" => {
217                            if arr.len() != 3 {
218                                return Err(serde::de::Error::custom(
219                                    "map requires exactly 3 elements",
220                                ));
221                            }
222                            let array = Box::new(
223                                serde_json::from_value(arr[1].clone())
224                                    .map_err(serde::de::Error::custom)?,
225                            );
226                            let function = Box::new(
227                                serde_json::from_value(arr[2].clone())
228                                    .map_err(serde::de::Error::custom)?,
229                            );
230                            Ok(ILExpression::MapOp {
231                                map: MapExpression { array, function },
232                            })
233                        }
234                        "filter" => {
235                            if arr.len() != 3 {
236                                return Err(serde::de::Error::custom(
237                                    "filter requires exactly 3 elements",
238                                ));
239                            }
240                            let array = Box::new(
241                                serde_json::from_value(arr[1].clone())
242                                    .map_err(serde::de::Error::custom)?,
243                            );
244                            let predicate = Box::new(
245                                serde_json::from_value(arr[2].clone())
246                                    .map_err(serde::de::Error::custom)?,
247                            );
248                            Ok(ILExpression::FilterOp {
249                                filter: FilterExpression { array, predicate },
250                            })
251                        }
252                        "reduce" => {
253                            if arr.len() != 4 {
254                                return Err(serde::de::Error::custom(
255                                    "reduce requires exactly 4 elements",
256                                ));
257                            }
258                            let array = Box::new(
259                                serde_json::from_value(arr[1].clone())
260                                    .map_err(serde::de::Error::custom)?,
261                            );
262                            let function = Box::new(
263                                serde_json::from_value(arr[2].clone())
264                                    .map_err(serde::de::Error::custom)?,
265                            );
266                            let initial = Box::new(
267                                serde_json::from_value(arr[3].clone())
268                                    .map_err(serde::de::Error::custom)?,
269                            );
270                            Ok(ILExpression::ReduceOp {
271                                reduce: ReduceExpression {
272                                    array,
273                                    function,
274                                    initial,
275                                },
276                            })
277                        }
278                        "plan" => {
279                            if arr.len() != 4 {
280                                return Err(serde::de::Error::custom(
281                                    "plan requires exactly 4 elements",
282                                ));
283                            }
284                            let captures = serde_json::from_value(arr[1].clone())
285                                .map_err(serde::de::Error::custom)?;
286                            let operations = serde_json::from_value(arr[2].clone())
287                                .map_err(serde::de::Error::custom)?;
288                            let result = Box::new(
289                                serde_json::from_value(arr[3].clone())
290                                    .map_err(serde::de::Error::custom)?,
291                            );
292                            Ok(ILExpression::Plan {
293                                plan: ILPlan {
294                                    captures,
295                                    operations,
296                                    result,
297                                },
298                            })
299                        }
300                        _ => Ok(ILExpression::Literal(value)),
301                    };
302                }
303            }
304        }
305
306        // If not a special array form, treat as literal
307        Ok(ILExpression::Literal(value))
308    }
309}
310
311#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
312pub struct ILPlan {
313    pub captures: Vec<CapId>,
314    pub operations: Vec<ILOperation>,
315    pub result: Box<ILExpression>,
316}
317
318#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
319pub struct BindExpression {
320    pub value: Box<ILExpression>,
321    pub body: Box<ILExpression>,
322}
323
324#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
325pub struct IfExpression {
326    pub condition: Box<ILExpression>,
327    pub then_branch: Box<ILExpression>,
328    pub else_branch: Box<ILExpression>,
329}
330
331#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub struct GetExpression {
333    pub object: Box<ILExpression>,
334    pub property: String,
335}
336
337#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
338pub struct CallExpression {
339    pub target: Box<ILExpression>,
340    pub method: String,
341    pub args: Vec<ILExpression>,
342}
343
344#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
345pub struct MapExpression {
346    pub array: Box<ILExpression>,
347    pub function: Box<ILExpression>,
348}
349
350#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
351pub struct FilterExpression {
352    pub array: Box<ILExpression>,
353    pub predicate: Box<ILExpression>,
354}
355
356#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
357pub struct ReduceExpression {
358    pub array: Box<ILExpression>,
359    pub function: Box<ILExpression>,
360    pub initial: Box<ILExpression>,
361}
362
363/// IL operation within a plan
364#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
365#[serde(untagged)]
366pub enum ILOperation {
367    /// Store a value in a variable slot
368    Store { store: StoreOperation },
369
370    /// Execute an expression
371    Execute { execute: Box<ILExpression> },
372}
373
374#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
375pub struct StoreOperation {
376    pub slot: u32,
377    pub value: Box<ILExpression>,
378}
379
380/// Context for IL execution with variable bindings
381pub struct ILContext {
382    pub variables: Vec<Value>,
383    pub captures: Vec<CapId>,
384}
385
386impl ILContext {
387    pub fn new(captures: Vec<CapId>) -> Self {
388        Self {
389            variables: Vec::new(),
390            captures,
391        }
392    }
393
394    pub fn with_capacity(capacity: usize, captures: Vec<CapId>) -> Self {
395        Self {
396            variables: Vec::with_capacity(capacity),
397            captures,
398        }
399    }
400
401    pub fn get_variable(&self, index: u32) -> Option<&Value> {
402        self.variables.get(index as usize)
403    }
404
405    pub fn set_variable(&mut self, index: u32, value: Value) -> Result<(), ILError> {
406        let idx = index as usize;
407        if idx >= self.variables.len() {
408            self.variables.resize_with(idx + 1, || Value::Null);
409        }
410        self.variables[idx] = value;
411        Ok(())
412    }
413
414    pub fn push_variable(&mut self, value: Value) -> u32 {
415        let index = self.variables.len() as u32;
416        self.variables.push(value);
417        index
418    }
419
420    pub fn get_capture(&self, index: u32) -> Option<&CapId> {
421        self.captures.get(index as usize)
422    }
423}
424
425#[derive(Debug, thiserror::Error)]
426pub enum ILError {
427    #[error("Variable not found: {0}")]
428    VariableNotFound(u32),
429
430    #[error("Capture not found: {0}")]
431    CaptureNotFound(u32),
432
433    #[error("Type error: expected {expected}, got {actual}")]
434    TypeError { expected: String, actual: String },
435
436    #[error("Execution failed: {0}")]
437    ExecutionFailed(String),
438
439    #[error("Invalid operation: {0}")]
440    InvalidOperation(String),
441}
442
443impl ILExpression {
444    /// Create a variable reference
445    pub fn var(index: u32) -> Self {
446        ILExpression::Variable { var_ref: index }
447    }
448
449    /// Create a literal value
450    pub fn literal(value: Value) -> Self {
451        ILExpression::Literal(value)
452    }
453
454    /// Create a bind expression
455    pub fn bind(value: ILExpression, body: ILExpression) -> Self {
456        ILExpression::Bind {
457            bind: BindExpression {
458                value: Box::new(value),
459                body: Box::new(body),
460            },
461        }
462    }
463
464    /// Create an if expression
465    pub fn if_expr(
466        condition: ILExpression,
467        then_branch: ILExpression,
468        else_branch: ILExpression,
469    ) -> Self {
470        ILExpression::If {
471            if_expr: Box::new(IfExpression {
472                condition: Box::new(condition),
473                then_branch: Box::new(then_branch),
474                else_branch: Box::new(else_branch),
475            }),
476        }
477    }
478
479    /// Create a property get expression
480    pub fn get(object: ILExpression, property: String) -> Self {
481        ILExpression::Get {
482            get: GetExpression {
483                object: Box::new(object),
484                property,
485            },
486        }
487    }
488
489    /// Create a method call expression
490    pub fn call(target: ILExpression, method: String, args: Vec<ILExpression>) -> Self {
491        ILExpression::Call {
492            call: CallExpression {
493                target: Box::new(target),
494                method,
495                args,
496            },
497        }
498    }
499
500    /// Create a map expression for array transformation
501    pub fn map(array: ILExpression, function: ILExpression) -> Self {
502        ILExpression::MapOp {
503            map: MapExpression {
504                array: Box::new(array),
505                function: Box::new(function),
506            },
507        }
508    }
509}
510
511#[cfg(test)]
512mod tests {
513    use super::*;
514    use serde_json::json;
515
516    #[test]
517    fn test_variable_expression() {
518        let expr = ILExpression::var(0);
519        let json = serde_json::to_value(&expr).unwrap();
520        assert_eq!(json, json!(["var", 0]));
521
522        let deserialized: ILExpression = serde_json::from_value(json).unwrap();
523        assert_eq!(expr, deserialized);
524    }
525
526    #[test]
527    fn test_bind_expression() {
528        let expr = ILExpression::bind(ILExpression::literal(json!(42)), ILExpression::var(0));
529
530        let json = serde_json::to_value(&expr).unwrap();
531        let deserialized: ILExpression = serde_json::from_value(json).unwrap();
532        assert_eq!(expr, deserialized);
533    }
534
535    #[test]
536    fn test_if_expression() {
537        let expr = ILExpression::if_expr(
538            ILExpression::var(0),
539            ILExpression::literal(json!("true branch")),
540            ILExpression::literal(json!("false branch")),
541        );
542
543        let json = serde_json::to_value(&expr).unwrap();
544        let deserialized: ILExpression = serde_json::from_value(json).unwrap();
545        assert_eq!(expr, deserialized);
546    }
547
548    #[test]
549    fn test_map_expression() {
550        let expr = ILExpression::map(
551            ILExpression::var(0),
552            ILExpression::bind(
553                ILExpression::var(1),
554                ILExpression::call(ILExpression::var(1), "toString".to_string(), vec![]),
555            ),
556        );
557
558        let json = serde_json::to_value(&expr).unwrap();
559        let deserialized: ILExpression = serde_json::from_value(json).unwrap();
560        assert_eq!(expr, deserialized);
561    }
562
563    #[test]
564    fn test_il_context() {
565        let mut context = ILContext::new(vec![CapId::new(1)]);
566
567        context.set_variable(0, json!("first")).unwrap();
568        context.set_variable(2, json!("third")).unwrap();
569
570        assert_eq!(context.get_variable(0), Some(&json!("first")));
571        assert_eq!(context.get_variable(1), Some(&Value::Null));
572        assert_eq!(context.get_variable(2), Some(&json!("third")));
573
574        let index = context.push_variable(json!("pushed"));
575        assert_eq!(index, 3);
576        assert_eq!(context.get_variable(3), Some(&json!("pushed")));
577    }
578}