Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add arithmatic expression support #71

Merged
merged 3 commits into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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