use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Stage {
Filter(Box<AstNode>),
Index(String),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PathStep {
pub node: AstNode,
pub stages: Vec<Stage>,
pub focus: Option<String>,
pub index_var: Option<String>,
pub ancestor_label: Option<String>,
pub is_tuple: bool,
}
#[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,
Parent(String),
Predicate(Box<AstNode>),
ArrayGroup(Vec<AstNode>),
FunctionApplication(Box<AstNode>),
Sort {
input: Box<AstNode>,
terms: Vec<(AstNode, bool)>,
},
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,
FocusBind,
IndexBind,
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(),
focus: None,
index_var: None,
ancestor_label: None,
is_tuple: false,
}
}
pub fn with_stages(node: AstNode, stages: Vec<Stage>) -> Self {
PathStep {
node,
stages,
focus: None,
index_var: None,
ancestor_label: None,
is_tuple: false,
}
}
}
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 { .. }));
}
#[test]
fn test_path_step_new_defaults_new_fields_to_none() {
let step = PathStep::new(AstNode::Name("foo".to_string()));
assert_eq!(step.focus, None);
assert_eq!(step.index_var, None);
assert_eq!(step.ancestor_label, None);
assert!(!step.is_tuple);
}
}