use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Stage {
Filter(Box<AstNode>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PathStep {
pub node: AstNode,
pub stages: Vec<Stage>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AstNode {
String(String),
Name(String),
Number(f64),
Boolean(bool),
Null,
Undefined,
Placeholder,
Regex { pattern: String, flags: String },
Variable(String),
ParentVariable(String),
Path { steps: Vec<PathStep> },
Binary {
op: BinaryOp,
lhs: Box<AstNode>,
rhs: Box<AstNode>,
},
Unary { op: UnaryOp, operand: Box<AstNode> },
Function {
name: String,
args: Vec<AstNode>,
is_builtin: bool,
},
Call {
procedure: Box<AstNode>,
args: Vec<AstNode>,
},
Lambda {
params: Vec<String>,
body: Box<AstNode>,
signature: Option<String>,
#[serde(default)]
thunk: bool,
},
Array(Vec<AstNode>),
Object(Vec<(AstNode, AstNode)>),
ObjectTransform {
input: Box<AstNode>,
pattern: Vec<(AstNode, AstNode)>,
},
Block(Vec<AstNode>),
Conditional {
condition: Box<AstNode>,
then_branch: Box<AstNode>,
else_branch: Option<Box<AstNode>>,
},
Wildcard,
Descendant,
Predicate(Box<AstNode>),
ArrayGroup(Vec<AstNode>),
FunctionApplication(Box<AstNode>),
Sort {
input: Box<AstNode>,
terms: Vec<(AstNode, bool)>,
},
IndexBind {
input: Box<AstNode>,
variable: String,
},
Transform {
location: Box<AstNode>,
update: Box<AstNode>,
delete: Option<Box<AstNode>>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum BinaryOp {
Add,
Subtract,
Multiply,
Divide,
Modulo,
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
And,
Or,
Concatenate,
Range,
In,
ColonEqual,
Coalesce,
Default,
ChainPipe, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum UnaryOp {
Negate,
Not,
}
impl PathStep {
pub fn new(node: AstNode) -> Self {
PathStep {
node,
stages: Vec::new(),
}
}
pub fn with_stages(node: AstNode, stages: Vec<Stage>) -> Self {
PathStep { node, stages }
}
}
impl AstNode {
pub fn string(s: impl Into<String>) -> Self {
AstNode::String(s.into())
}
pub fn number(n: f64) -> Self {
AstNode::Number(n)
}
pub fn boolean(b: bool) -> Self {
AstNode::Boolean(b)
}
pub fn null() -> Self {
AstNode::Null
}
pub fn undefined() -> Self {
AstNode::Undefined
}
pub fn variable(name: impl Into<String>) -> Self {
AstNode::Variable(name.into())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ast_node_creation() {
let str_node = AstNode::string("hello");
assert!(matches!(str_node, AstNode::String(_)));
let num_node = AstNode::number(42.0);
assert!(matches!(num_node, AstNode::Number(_)));
let bool_node = AstNode::boolean(true);
assert!(matches!(bool_node, AstNode::Boolean(_)));
let null_node = AstNode::null();
assert!(matches!(null_node, AstNode::Null));
}
#[test]
fn test_binary_op() {
let node = AstNode::Binary {
op: BinaryOp::Add,
lhs: Box::new(AstNode::number(1.0)),
rhs: Box::new(AstNode::number(2.0)),
};
assert!(matches!(node, AstNode::Binary { .. }));
}
}