Skip to content

Commit

Permalink
Add support for variants of function names and implement floor / ceil…
Browse files Browse the repository at this point in the history
… with arguments
  • Loading branch information
nudded committed Nov 1, 2024
1 parent a096edf commit 83b448b
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 33 deletions.
75 changes: 71 additions & 4 deletions expression-core/src/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,18 @@ impl Function {

Ok(ExpressionValue::String(evaluated_args.concat()))
}
Function::Ceil(expr) => {
Function::Ceil(expr, None) => {
let evaluated_decimal = expr.evaluate(event)?.to_decimal()?;
Ok(ExpressionValue::Number(
evaluated_decimal.with_scale_round(0, RoundingMode::Ceiling),
))
}
Function::Floor(expr, None) => {
let evaluated_decimal = expr.evaluate(event)?.to_decimal()?;
Ok(ExpressionValue::Number(
evaluated_decimal.with_scale_round(0, RoundingMode::Floor),
))
}
Function::Round(expr, None) => {
let evaluated_decimal = expr.evaluate(event)?.to_decimal()?;
Ok(ExpressionValue::Number(
Expand All @@ -110,6 +116,28 @@ impl Function {
evaluated_decimal.with_scale_round(round_digits, RoundingMode::HalfUp),
))
}
Function::Ceil(expr, Some(digit_expr)) => {
let evaluated_decimal = expr.evaluate(event)?.to_decimal()?;
let round_digits = digit_expr
.evaluate(event)?
.to_decimal()?
.to_i64()
.ok_or(ExpressionError::ExpectedDecimal)?;
Ok(ExpressionValue::Number(
evaluated_decimal.with_scale_round(round_digits, RoundingMode::Ceiling),
))
}
Function::Floor(expr, Some(digit_expr)) => {
let evaluated_decimal = expr.evaluate(event)?.to_decimal()?;
let round_digits = digit_expr
.evaluate(event)?
.to_decimal()?
.to_i64()
.ok_or(ExpressionError::ExpectedDecimal)?;
Ok(ExpressionValue::Number(
evaluated_decimal.with_scale_round(round_digits, RoundingMode::Floor),
))
}
}
}
}
Expand Down Expand Up @@ -303,13 +331,52 @@ mod tests {

#[test]
fn test_evaluate_ceil() {
let expr = Expression::Function(Function::Ceil(Box::new(Expression::Decimal(
"12.3".parse::<BigDecimal>().unwrap(),
))));
let expr = Expression::Function(Function::Ceil(
Box::new(Expression::Decimal("12.3".parse::<BigDecimal>().unwrap())),
None,
));
let event = Default::default();
evaluate_and_compare(expr, &event, ExpressionValue::Number(13.into()));
}

#[test]
fn test_evaluate_ceil_with_arg() {
let expr = Expression::Function(Function::Ceil(
Box::new(Expression::Decimal("12.351".parse::<BigDecimal>().unwrap())),
Some(Box::new(Expression::Decimal(1.into()))),
));
let event = Default::default();
evaluate_and_compare(
expr,
&event,
ExpressionValue::Number("12.4".parse::<BigDecimal>().unwrap()),
);
}

#[test]
fn test_evaluate_floor() {
let expr = Expression::Function(Function::Floor(
Box::new(Expression::Decimal("12.3".parse::<BigDecimal>().unwrap())),
None,
));
let event = Default::default();
evaluate_and_compare(expr, &event, ExpressionValue::Number(12.into()));
}

#[test]
fn test_evaluate_floor_with_arg() {
let expr = Expression::Function(Function::Floor(
Box::new(Expression::Decimal("12.351".parse::<BigDecimal>().unwrap())),
Some(Box::new(Expression::Decimal(1.into()))),
));
let event = Default::default();
evaluate_and_compare(
expr,
&event,
ExpressionValue::Number("12.3".parse::<BigDecimal>().unwrap()),
);
}

#[test]
fn test_evaluate_concat() {
let expr = Expression::Function(Function::Concat(vec![
Expand Down
9 changes: 5 additions & 4 deletions expression-core/src/grammar.pest
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
function = { function_name ~ "(" ~ function_args ~ ")" }

function_name = _{ ceil | concat | round }
ceil = { "ceil" }
concat = { "concat" }
round = { "round" }
function_name = _{ ceil | concat | round | floor }
ceil = { "ceil" | "CEIL" | "Ceil" }
concat = { "concat" | "CONCAT" | "Concat" }
round = { "round" | "ROUND" | "Round" }
floor = { "floor" | "FLOOR" | "Floor" }

function_args = _{ expr ~ ("," ~ expr)* }

Expand Down
114 changes: 89 additions & 25 deletions expression-core/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ pub type ParseResult<T> = Result<T, ParseError>;
#[derive(Debug, PartialEq)]
pub enum Function {
Concat(Vec<Expression>),
Ceil(Box<Expression>),
Ceil(Box<Expression>, Option<Box<Expression>>),
Round(Box<Expression>, Option<Box<Expression>>),
Floor(Box<Expression>, Option<Box<Expression>>),
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -72,39 +73,43 @@ fn parse_function(pairs: Pairs<Rule>) -> ParseResult<Function> {
let mut iter = pairs.into_iter();
let name = iter.next().unwrap();
let function = match name.as_rule() {
Rule::ceil => Function::Ceil(Box::new(parse_expr(iter)?)),
Rule::concat => {
let args = iter
.map(|r| parse_expr(r.into_inner()))
.collect::<ParseResult<Vec<Expression>>>()?;

Function::Concat(args)
}
Rule::round => {
let mut args = iter
.map(|r| {
let expr = parse_expr(r.into_inner())?;
Ok(Box::new(expr))
})
.collect::<Vec<ParseResult<Box<Expression>>>>();

match args.len() {
1 => Function::Round(args.remove(0)?, None),
2 => Function::Round(args.remove(0)?, Some(args.remove(0)?)),
n => {
return Err(ParseError::WrongNumberOfArguments(
"round".to_owned(),
"1..2".to_owned(),
n,
))
}
}
}
Rule::ceil => parse_function_with_args(Function::Ceil, iter)?,
Rule::round => parse_function_with_args(Function::Round, iter)?,
Rule::floor => parse_function_with_args(Function::Floor, iter)?,
rule => unreachable!("Expected function name, got :{:?}", rule),
};
Ok(function)
}

fn parse_function_with_args<F>(f: F, iter: Pairs<Rule>) -> ParseResult<Function>
where
F: Fn(Box<Expression>, Option<Box<Expression>>) -> Function,
{
let mut args = iter
.map(|r| {
let expr = parse_expr(r.into_inner())?;
Ok(Box::new(expr))
})
.collect::<Vec<ParseResult<Box<Expression>>>>();

match args.len() {
1 => Ok(f(args.remove(0)?, None)),
2 => Ok(f(args.remove(0)?, Some(args.remove(0)?))),
n => Err(ParseError::WrongNumberOfArguments(
"round".to_owned(),
"1..2".to_owned(),
n,
)),
}
}

fn parse_event_attribute(mut pairs: Pairs<Rule>) -> EventAttribute {
let mut inner = pairs.next().unwrap().into_inner();
match inner.next().unwrap().as_rule() {
Expand Down Expand Up @@ -217,16 +222,54 @@ mod tests {
);
}

#[test]
fn test_parse_concat_uppercase() {
parse_and_compare(
"CONCAT('a', 'b')",
Expression::Function(Function::Concat(vec![
Expression::String("a".to_owned()),
Expression::String("b".to_owned()),
])),
);
}

#[test]
fn test_parse_concat_capitalized() {
parse_and_compare(
"Concat('a', 'b')",
Expression::Function(Function::Concat(vec![
Expression::String("a".to_owned()),
Expression::String("b".to_owned()),
])),
);
}

#[test]
fn test_parse_ceil() {
parse_and_compare(
"ceil(123)",
Expression::Function(Function::Ceil(Box::new(Expression::Decimal(123.into())))),
Expression::Function(Function::Ceil(
Box::new(Expression::Decimal(123.into())),
None,
)),
);
}

#[test]
fn test_parse_round() {
fn test_parse_ceil_one_arg() {
parse_and_compare(
"ceil(123, -1)",
Expression::Function(Function::Ceil(
Box::new(Expression::Decimal(123.into())),
Some(Box::new(Expression::UnaryMinus(Box::new(
Expression::Decimal(1.into()),
)))),
)),
);
}

#[test]
fn test_parse_round_one_arg() {
parse_and_compare(
"round(123, 1)",
Expression::Function(Function::Round(
Expand All @@ -236,7 +279,7 @@ mod tests {
);
}
#[test]
fn test_parse_round_one_arg() {
fn test_parse_round() {
parse_and_compare(
"round(123)",
Expression::Function(Function::Round(
Expand All @@ -245,4 +288,25 @@ mod tests {
)),
);
}

#[test]
fn test_parse_floor_one_arg() {
parse_and_compare(
"floor(123, 1)",
Expression::Function(Function::Floor(
Box::new(Expression::Decimal(123.into())),
Some(Box::new(Expression::Decimal(1.into()))),
)),
);
}
#[test]
fn test_parse_floor() {
parse_and_compare(
"floor(123)",
Expression::Function(Function::Floor(
Box::new(Expression::Decimal(123.into())),
None,
)),
);
}
}

0 comments on commit 83b448b

Please sign in to comment.