Skip to content

Commit

Permalink
Merge pull request #71 from notauserx/add-arithmatic-expressions
Browse files Browse the repository at this point in the history
feat: add arithmatic expression support
  • Loading branch information
b41sh authored Dec 10, 2024
2 parents 6ff70aa + fc73c99 commit ace7386
Show file tree
Hide file tree
Showing 4 changed files with 415 additions and 1 deletion.
44 changes: 43 additions & 1 deletion src/jsonpath/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,23 @@ fn op(input: &[u8]) -> IResult<&[u8], BinaryOperator> {
))(input)
}

fn unary_arith_op(input: &[u8]) -> IResult<&[u8], UnaryArithmeticOperator> {
alt((
value(UnaryArithmeticOperator::Add, char('+')),
value(UnaryArithmeticOperator::Subtract, char('-')),
))(input)
}

fn binary_arith_op(input: &[u8]) -> IResult<&[u8], BinaryArithmeticOperator> {
alt((
value(BinaryArithmeticOperator::Add, char('+')),
value(BinaryArithmeticOperator::Subtract, char('-')),
value(BinaryArithmeticOperator::Multiply, char('*')),
value(BinaryArithmeticOperator::Divide, char('/')),
value(BinaryArithmeticOperator::Modulus, char('%')),
))(input)
}

fn path_value(input: &[u8]) -> IResult<&[u8], PathValue<'_>> {
alt((
value(PathValue::Null, tag("null")),
Expand All @@ -339,8 +356,33 @@ fn inner_expr(input: &[u8], root_predicate: bool) -> IResult<&[u8], Expr<'_>> {
}

fn expr_atom(input: &[u8], root_predicate: bool) -> IResult<&[u8], Expr<'_>> {
// TODO, support arithmetic expressions.
alt((
map(
tuple((
delimited(multispace0, |i| inner_expr(i, root_predicate), multispace0),
binary_arith_op,
delimited(multispace0, |i| inner_expr(i, root_predicate), multispace0),
)),
|(left, op, right)| {
Expr::ArithmeticFunc(ArithmeticFunc::Binary {
op,
left: Box::new(left),
right: Box::new(right),
})
},
),
map(
tuple((
unary_arith_op,
delimited(multispace0, |i| inner_expr(i, root_predicate), multispace0),
)),
|(op, operand)| {
Expr::ArithmeticFunc(ArithmeticFunc::Unary {
op,
operand: Box::new(operand),
})
},
),
map(
tuple((
delimited(multispace0, |i| inner_expr(i, root_predicate), multispace0),
Expand Down
73 changes: 73 additions & 0 deletions src/jsonpath/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub enum Path<'a> {
/// There can be more than one index, e.g. `$[0, last-1 to last, 5]` represents the first,
/// the last two, and the sixth element in an Array.
ArrayIndices(Vec<ArrayIndex>),
/// `<expression>` standalone unary or binary arithmetic expression, like '-$.a[*]' or '$.a + 3'
ArithmeticExpr(Box<Expr<'a>>),
/// `?(<expression>)` represents selecting all elements in an object or array that match the filter expression, like `$.book[?(@.price < 10)]`.
FilterExpr(Box<Expr<'a>>),
/// `<expression>` standalone filter expression, like `$.book[*].price > 10`.
Expand Down Expand Up @@ -120,6 +122,41 @@ pub enum BinaryOperator {
Gte,
}

#[derive(Debug, Clone, PartialEq)]
pub enum UnaryArithmeticOperator {
/// `Add` represents unary arithmetic + operation.
Add,
/// `Subtract` represents unary arithmetic - operation.
Subtract,
}

#[derive(Debug, Clone, PartialEq)]
pub enum BinaryArithmeticOperator {
/// `Add` represents binary arithmetic + operation.
Add,
/// `Subtract` represents binary arithmetic - operation.
Subtract,
/// `Multiply` represents binary arithmetic * operation.
Multiply,
/// `Divide` represents binary arithmetic / operation.
Divide,
/// `Modulus` represents binary arithmetic % operation.
Modulus,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ArithmeticFunc<'a> {
Unary {
op: UnaryArithmeticOperator,
operand: Box<Expr<'a>>,
},
Binary {
op: BinaryArithmeticOperator,
left: Box<Expr<'a>>,
right: Box<Expr<'a>>,
},
}

/// Represents a filter expression used to filter Array or Object.
#[derive(Debug, Clone, PartialEq)]
pub enum Expr<'a> {
Expand All @@ -133,6 +170,8 @@ pub enum Expr<'a> {
left: Box<Expr<'a>>,
right: Box<Expr<'a>>,
},
/// Arithmetic expression that performs an arithmetic operation, returns a number value.
ArithmeticFunc(ArithmeticFunc<'a>),
/// Filter function, returns a boolean value.
FilterFunc(FilterFunc<'a>),
}
Expand Down Expand Up @@ -223,6 +262,9 @@ impl<'a> Display for Path<'a> {
}
write!(f, "]")?;
}
Path::ArithmeticExpr(expr) => {
write!(f, "?({expr})")?;
}
Path::FilterExpr(expr) => {
write!(f, "?({expr})")?;
}
Expand Down Expand Up @@ -288,6 +330,29 @@ impl Display for BinaryOperator {
}
}

impl Display for UnaryArithmeticOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let symbol = match self {
UnaryArithmeticOperator::Add => "+",
UnaryArithmeticOperator::Subtract => "-",
};
write!(f, "{}", symbol)
}
}

impl Display for BinaryArithmeticOperator {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let symbol = match self {
BinaryArithmeticOperator::Add => "+",
BinaryArithmeticOperator::Subtract => "-",
BinaryArithmeticOperator::Multiply => "*",
BinaryArithmeticOperator::Divide => "/",
BinaryArithmeticOperator::Modulus => "%",
};
write!(f, "{}", symbol)
}
}

impl<'a> Display for Expr<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -320,6 +385,14 @@ impl<'a> Display for Expr<'a> {
write!(f, "{right}")?;
}
}
Expr::ArithmeticFunc(expr) => match expr {
ArithmeticFunc::Unary { op, operand } => {
write!(f, "{}{}", op, operand)?;
}
ArithmeticFunc::Binary { op, left, right } => {
write!(f, "{} {} {}", left, op, right)?;
}
},
Expr::FilterFunc(func) => match func {
FilterFunc::Exists(paths) => {
f.write_str("exists(")?;
Expand Down
9 changes: 9 additions & 0 deletions tests/it/jsonpath_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,17 @@ fn test_json_path() {
r#"$"#,
r#"$.*"#,
r#"$[*]"#,
r#"5 + 5"#,
r#"10 - 5"#,
r#"10 * 5"#,
r#"10 / 5"#,
r#"10 % 5"#,
r#"$.store.book[*].*"#,
// r#"$.store.book[*].* + 5"#,
r#"$.store.book[0].price"#,
r#"+$.store.book[0].price"#,
r#"-$.store.book[0].price"#,
r#"$.store.book[0].price + 5"#,
r#"$.store.book[last].isbn"#,
r"$.store.book[last].test_key\uD83D\uDC8E测试",
r#"$.store.book[0,1, last - 2].price"#,
Expand Down
Loading

0 comments on commit ace7386

Please sign in to comment.