use winnow::{
ascii::{alpha1, digit1},
combinator::{alt, delimited, empty, repeat},
token::{take_until, take_while},
PResult, Parser,
};
use crate::parser::{
ast::{ComparisonOp, LogicalOp, PathExpression},
path::{ParseError, ParseResult, PathSegment},
};
use serde_json::Value;
pub struct ExpressionParser;
impl ExpressionParser {
pub fn parse_path_expression(input: &str) -> ParseResult<PathExpression> {
let mut input_ref = input;
match Self::parse_comma_expression.parse_next(&mut input_ref) {
Ok(expr) => {
match Self::skip_whitespace.parse_next(&mut input_ref) {
Ok(_) => {
if input_ref.is_empty() {
Ok(expr)
} else {
Err(ParseError {
message: format!(
"Unexpected characters: '{input_ref}'"
),
position: input.len() - input_ref.len(),
})
}
}
Err(_) => Err(ParseError {
message: "Failed to skip whitespace".to_string(),
position: input.len() - input_ref.len(),
}),
}
}
Err(e) => Err(ParseError {
message: format!("Failed to parse expression: {e:?}"),
position: input.len() - input_ref.len(),
}),
}
}
fn parse_comma_expression(input: &mut &str) -> PResult<PathExpression> {
let first = Self::parse_conditional_expression.parse_next(input)?;
let mut expressions = vec![first];
while Self::try_parse_comma.parse_next(input).is_ok() {
let next = Self::parse_conditional_expression.parse_next(input)?;
expressions.push(next);
}
Ok(if expressions.len() == 1 {
expressions.into_iter().next().unwrap()
} else {
PathExpression::Comma(expressions)
})
}
fn parse_conditional_expression(
input: &mut &str,
) -> PResult<PathExpression> {
let _ = Self::skip_whitespace.parse_next(input);
if Self::try_parse_try.parse_next(input).is_ok() {
let try_expr =
Self::parse_logical_or_expression.parse_next(input)?;
let catch_expr = if Self::try_parse_catch.parse_next(input).is_ok()
{
Some(Box::new(
Self::parse_logical_or_expression.parse_next(input)?,
))
} else {
None
};
return Ok(PathExpression::TryCatch {
try_expr: Box::new(try_expr),
catch_expr,
});
}
if Self::try_parse_if.parse_next(input).is_ok() {
let condition =
Self::parse_logical_or_expression.parse_next(input)?;
Self::parse_then.parse_next(input)?;
let then_expr =
Self::parse_logical_or_expression.parse_next(input)?;
let else_expr = if Self::try_parse_else.parse_next(input).is_ok() {
Some(Box::new(
Self::parse_logical_or_expression.parse_next(input)?,
))
} else {
None
};
Self::parse_end.parse_next(input)?;
Ok(PathExpression::Conditional {
condition: Box::new(condition),
then_expr: Box::new(then_expr),
else_expr,
})
} else {
Self::parse_logical_or_expression.parse_next(input)
}
}
fn parse_logical_or_expression(
input: &mut &str,
) -> PResult<PathExpression> {
let mut left = Self::parse_logical_and_expression.parse_next(input)?;
while Self::try_parse_or.parse_next(input).is_ok() {
let right = Self::parse_logical_and_expression.parse_next(input)?;
left = PathExpression::Logical {
op: LogicalOp::Or,
operands: vec![left, right],
};
}
Ok(left)
}
fn parse_logical_and_expression(
input: &mut &str,
) -> PResult<PathExpression> {
let mut left = Self::parse_logical_not_expression.parse_next(input)?;
while Self::try_parse_and.parse_next(input).is_ok() {
let right = Self::parse_logical_not_expression.parse_next(input)?;
left = PathExpression::Logical {
op: LogicalOp::And,
operands: vec![left, right],
};
}
Ok(left)
}
fn parse_logical_not_expression(
input: &mut &str,
) -> PResult<PathExpression> {
let _ = Self::skip_whitespace.parse_next(input);
if Self::try_parse_not.parse_next(input).is_ok() {
let operand =
Self::parse_comparison_expression.parse_next(input)?;
Ok(PathExpression::Logical {
op: LogicalOp::Not,
operands: vec![operand],
})
} else {
Self::parse_comparison_expression.parse_next(input)
}
}
fn parse_comparison_expression(
input: &mut &str,
) -> PResult<PathExpression> {
let mut left = Self::parse_pipe_expression.parse_next(input)?;
loop {
let _ = Self::skip_whitespace.parse_next(input);
let op = if Self::try_parse_lte.parse_next(input).is_ok() {
ComparisonOp::LessThanOrEqual
} else if Self::try_parse_gte.parse_next(input).is_ok() {
ComparisonOp::GreaterThanOrEqual
} else if Self::try_parse_eq.parse_next(input).is_ok() {
ComparisonOp::Equal
} else if Self::try_parse_ne.parse_next(input).is_ok() {
ComparisonOp::NotEqual
} else if Self::try_parse_lt.parse_next(input).is_ok() {
ComparisonOp::LessThan
} else if Self::try_parse_gt.parse_next(input).is_ok() {
ComparisonOp::GreaterThan
} else {
break;
};
let right = Self::parse_pipe_expression.parse_next(input)?;
left = PathExpression::Comparison {
left: Box::new(left),
op,
right: Box::new(right),
};
}
Ok(left)
}
fn parse_pipe_expression(input: &mut &str) -> PResult<PathExpression> {
let mut left = Self::parse_primary_expression.parse_next(input)?;
while Self::try_parse_pipe.parse_next(input).is_ok() {
let right = Self::parse_primary_expression.parse_next(input)?;
left = PathExpression::pipe(left, right);
}
let _ = Self::skip_whitespace.parse_next(input);
if input.starts_with('?') {
'?'.parse_next(input)?;
left = PathExpression::Optional(Box::new(left));
}
Ok(left)
}
fn parse_primary_expression(input: &mut &str) -> PResult<PathExpression> {
let _ = Self::skip_whitespace.parse_next(input);
let mut expr = alt((
Self::parse_literal,
Self::parse_parenthesized,
Self::parse_function_call,
Self::parse_path_or_identity,
))
.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
if input.starts_with('?') {
'?'.parse_next(input)?;
expr = PathExpression::Optional(Box::new(expr));
}
Ok(expr)
}
fn parse_path_or_identity(input: &mut &str) -> PResult<PathExpression> {
let segments = Self::parse_path_segments(input)?;
if segments.is_empty() {
if input.starts_with(".") {
'.'.value(PathExpression::Identity).parse_next(input)
} else {
Err(winnow::error::ErrMode::Backtrack(
winnow::error::ParserError::from_error_kind(
input,
winnow::error::ErrorKind::Verify,
),
))
}
} else {
Ok(PathExpression::Segments(segments))
}
}
fn parse_function_call(input: &mut &str) -> PResult<PathExpression> {
let function_name = (
alpha1,
take_while(0.., |c: char| c.is_alphanumeric() || c == '_'),
)
.recognize()
.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
if !input.starts_with('(') {
return Err(winnow::error::ErrMode::Backtrack(
winnow::error::ParserError::from_error_kind(
input,
winnow::error::ErrorKind::Verify,
),
));
}
'('.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
let mut args = Vec::new();
if !input.starts_with(')') {
args.push(Self::parse_comma_expression.parse_next(input)?);
let _ = Self::skip_whitespace.parse_next(input);
while input.starts_with(',') {
','.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
args.push(Self::parse_comma_expression.parse_next(input)?);
let _ = Self::skip_whitespace.parse_next(input);
}
}
')'.parse_next(input)?;
Ok(PathExpression::FunctionCall {
name: function_name.to_string(),
args,
})
}
fn parse_literal(input: &mut &str) -> PResult<PathExpression> {
alt((
Self::parse_array_literal,
Self::parse_object_literal,
Self::parse_string_literal,
Self::parse_number_literal,
Self::parse_boolean_literal,
Self::parse_null_literal,
))
.parse_next(input)
}
fn parse_array_literal(input: &mut &str) -> PResult<PathExpression> {
let _ = Self::skip_whitespace.parse_next(input);
'['.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
let mut elements = Vec::new();
if !input.starts_with(']') {
if let Ok(literal) = Self::parse_simple_literal(input) {
elements.push(literal);
let _ = Self::skip_whitespace.parse_next(input);
while input.starts_with(',') {
','.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
let literal = Self::parse_simple_literal(input)?;
elements.push(literal);
let _ = Self::skip_whitespace.parse_next(input);
}
}
}
']'.parse_next(input)?;
Ok(PathExpression::Literal(Value::Array(elements)))
}
fn parse_object_literal(input: &mut &str) -> PResult<PathExpression> {
let _ = Self::skip_whitespace.parse_next(input);
'{'.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
let mut object = serde_json::Map::new();
if !input.starts_with('}') {
loop {
let key = delimited('"', take_until(0.., "\""), '"')
.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
':'.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
let value = Self::parse_simple_literal(input)?;
object.insert(key.to_string(), value);
let _ = Self::skip_whitespace.parse_next(input);
if input.starts_with(',') {
','.parse_next(input)?;
let _ = Self::skip_whitespace.parse_next(input);
} else {
break;
}
}
}
'}'.parse_next(input)?;
Ok(PathExpression::Literal(Value::Object(object)))
}
fn parse_simple_literal(input: &mut &str) -> PResult<Value> {
let _ = Self::skip_whitespace.parse_next(input);
alt((
delimited('"', take_until(0.., "\""), '"')
.map(|s: &str| Value::String(s.to_string())),
digit1
.try_map(|s: &str| s.parse::<i64>())
.map(|n| Value::Number(serde_json::Number::from(n))),
alt((
"true".value(Value::Bool(true)),
"false".value(Value::Bool(false)),
)),
"null".value(Value::Null),
))
.parse_next(input)
}
fn parse_string_literal(input: &mut &str) -> PResult<PathExpression> {
delimited('"', take_until(0.., "\""), '"')
.map(|s: &str| {
PathExpression::Literal(Value::String(s.to_string()))
})
.parse_next(input)
}
fn parse_number_literal(input: &mut &str) -> PResult<PathExpression> {
digit1
.try_map(|s: &str| s.parse::<i64>())
.map(|n| {
PathExpression::Literal(Value::Number(
serde_json::Number::from(n),
))
})
.parse_next(input)
}
fn parse_boolean_literal(input: &mut &str) -> PResult<PathExpression> {
alt((
"true".value(PathExpression::Literal(Value::Bool(true))),
"false".value(PathExpression::Literal(Value::Bool(false))),
))
.parse_next(input)
}
fn parse_null_literal(input: &mut &str) -> PResult<PathExpression> {
"null"
.value(PathExpression::Literal(Value::Null))
.parse_next(input)
}
fn parse_parenthesized(input: &mut &str) -> PResult<PathExpression> {
delimited(
('(', Self::skip_whitespace),
Self::parse_comma_expression,
(Self::skip_whitespace, ')'),
)
.parse_next(input)
}
fn parse_path_segments(input: &mut &str) -> PResult<Vec<PathSegment>> {
if input.starts_with(".")
&& (input.len() == 1
|| input
.chars()
.nth(1)
.is_none_or(|c| c.is_whitespace() || ")|,".contains(c)))
{
return Ok(vec![]);
}
repeat(1.., Self::parse_segment).parse_next(input)
}
fn parse_segment(input: &mut &str) -> PResult<PathSegment> {
alt((
Self::parse_recursive_wildcard,
Self::parse_field,
Self::parse_index,
Self::parse_wildcard,
))
.parse_next(input)
}
fn parse_field(input: &mut &str) -> PResult<PathSegment> {
alt((
('.', Self::parse_identifier)
.map(|(_, name)| PathSegment::Field(name)),
Self::parse_identifier.map(PathSegment::Field),
))
.parse_next(input)
}
fn parse_index(input: &mut &str) -> PResult<PathSegment> {
delimited(
'[',
alt((
'*'.value(PathSegment::Wildcard),
Self::parse_number.map(PathSegment::Index),
empty.value(PathSegment::Wildcard),
)),
']',
)
.parse_next(input)
}
fn parse_wildcard(input: &mut &str) -> PResult<PathSegment> {
if input.starts_with("**") {
return Err(winnow::error::ErrMode::Backtrack(
winnow::error::ParserError::from_error_kind(
input,
winnow::error::ErrorKind::Verify,
),
));
}
'*'.value(PathSegment::Wildcard).parse_next(input)
}
fn parse_recursive_wildcard(input: &mut &str) -> PResult<PathSegment> {
"**".value(PathSegment::RecursiveWildcard).parse_next(input)
}
#[allow(dead_code)]
fn parse_type_filter(input: &mut &str) -> PResult<PathSegment> {
(
Self::skip_whitespace,
'|',
Self::skip_whitespace,
Self::parse_identifier,
)
.map(|(_, _, _, type_name)| PathSegment::TypeFilter(type_name))
.parse_next(input)
}
fn parse_identifier(input: &mut &str) -> PResult<String> {
(
alpha1,
take_while(0.., |c: char| c.is_alphanumeric() || c == '_'),
)
.recognize()
.map(|s: &str| s.to_string())
.parse_next(input)
}
fn parse_number(input: &mut &str) -> PResult<usize> {
digit1.try_map(|s: &str| s.parse()).parse_next(input)
}
fn skip_whitespace(input: &mut &str) -> PResult<()> {
take_while(0.., |c: char| {
c == ' ' || c == '\t' || c == '\n' || c == '\r'
})
.void()
.parse_next(input)
}
fn try_parse_comma(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, ',', Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_pipe(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, '|', Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_if(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "if", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn parse_then(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "then", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_else(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "else", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn parse_end(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "end", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_try(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "try", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_catch(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "catch", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_or(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "or", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_and(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "and", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_not(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "not", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_lte(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "<=", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_gte(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, ">=", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_eq(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "==", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_ne(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "!=", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_lt(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, "<", Self::skip_whitespace)
.void()
.parse_next(input)
}
fn try_parse_gt(input: &mut &str) -> PResult<()> {
(Self::skip_whitespace, ">", Self::skip_whitespace)
.void()
.parse_next(input)
}
}
pub fn parse_path_expression(input: &str) -> ParseResult<PathExpression> {
ExpressionParser::parse_path_expression(input)
}