Skip to main content

rust_runes/
ast.rs

1use serde::{Deserialize, Serialize};
2
3/// Abstract Syntax Tree nodes for rule expressions
4#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
5#[non_exhaustive]
6pub enum Expression {
7    // Literals
8    /// A string literal.
9    String(String),
10    /// An integer literal.
11    Integer(i64),
12    /// A float literal.
13    Float(f64),
14    /// `true` or `false`.
15    Boolean(bool),
16    /// `nil`.
17    Nil,
18
19    // Variables, field access and indexing
20    /// A fact, by name.
21    Variable(String),
22    /// `base.field`.
23    FieldAccess(Box<Expression>, String),
24    /// `base[index]`: an array element by integer, or an object entry by key.
25    Index(Box<Expression>, Box<Expression>),
26
27    // Function calls: `Name(args)` and `target.Name(args)`
28    /// `Name(args)`: a built-in or registered function.
29    Call(String, Vec<Expression>),
30    /// `target.Name(args)`: a built-in or registered method.
31    MethodCall(Box<Expression>, String, Vec<Expression>),
32
33    // Binary operations
34    /// `left + right`: addition, or string concatenation.
35    Add(Box<Expression>, Box<Expression>),
36    /// `left - right`.
37    Subtract(Box<Expression>, Box<Expression>),
38    /// `left * right`.
39    Multiply(Box<Expression>, Box<Expression>),
40    /// `left / right`, always a float.
41    Divide(Box<Expression>, Box<Expression>),
42    /// `left % right`, on integers.
43    Modulo(Box<Expression>, Box<Expression>),
44    /// `left & right`, on integers.
45    BitAnd(Box<Expression>, Box<Expression>),
46    /// `left | right`, on integers.
47    BitOr(Box<Expression>, Box<Expression>),
48
49    // Comparison operations
50    /// `left == right`.
51    Equal(Box<Expression>, Box<Expression>),
52    /// `left != right`.
53    NotEqual(Box<Expression>, Box<Expression>),
54    /// `left < right`.
55    LessThan(Box<Expression>, Box<Expression>),
56    /// `left <= right`.
57    LessEqual(Box<Expression>, Box<Expression>),
58    /// `left > right`.
59    GreaterThan(Box<Expression>, Box<Expression>),
60    /// `left >= right`.
61    GreaterEqual(Box<Expression>, Box<Expression>),
62
63    // Logical operations
64    /// `left && right`, short-circuiting.
65    And(Box<Expression>, Box<Expression>),
66    /// `left || right`, short-circuiting.
67    Or(Box<Expression>, Box<Expression>),
68    /// `!operand`.
69    Not(Box<Expression>),
70
71    // Assignment: the target is a Variable, or a FieldAccess/Index chain rooted at one
72    /// `target = value`, as a rule action.
73    Assign(Box<Expression>, Box<Expression>),
74}
75
76impl Expression {
77    /// Builds an assignment to `path`, e.g. `Expression::assign(&["car", "Speed"], value)`.
78    pub fn assign(path: &[&str], value: Expression) -> Self {
79        let (root, fields) = path
80            .split_first()
81            .expect("assignment path must not be empty");
82        let target = fields
83            .iter()
84            .fold(Expression::Variable(root.to_string()), |obj, field| {
85                Expression::FieldAccess(Box::new(obj), field.to_string())
86            });
87        Expression::Assign(Box::new(target), Box::new(value))
88    }
89
90    /// Builds a call to a function, e.g. `Expression::call("Retract", vec![...])`.
91    pub fn call(name: &str, args: Vec<Expression>) -> Self {
92        Expression::Call(name.to_string(), args)
93    }
94
95    /// Whether this expression can be assigned to.
96    pub fn is_assignable(&self) -> bool {
97        match self {
98            Expression::Variable(_) => true,
99            Expression::FieldAccess(inner, _) | Expression::Index(inner, _) => {
100                inner.is_assignable()
101            }
102            _ => false,
103        }
104    }
105}