diff --git a/src/value/attr_value/attr_value_expr.rs b/src/value/attr_value/attr_value_expr.rs index add49e4..90320dd 100644 --- a/src/value/attr_value/attr_value_expr.rs +++ b/src/value/attr_value/attr_value_expr.rs @@ -1,5 +1,6 @@ use super::super::*; use anyhow::*; +use itertools::Itertools; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -61,6 +62,7 @@ pub enum AttrValExpr { UnaryOp(UnaryOp, Box), IfElse(Box, Box, Box), JsonAccess(Box, Box), + FunctionCall(String, Vec), } impl std::fmt::Display for AttrValExpr { @@ -72,6 +74,7 @@ impl std::fmt::Display for AttrValExpr { AttrValExpr::UnaryOp(op, x) => write!(f, "{}{}", op, x), AttrValExpr::IfElse(a, b, c) => write!(f, "(if {} then {} else {})", a, b, c), AttrValExpr::JsonAccess(value, index) => write!(f, "{}[{}]", value, index), + AttrValExpr::FunctionCall(function_name, args) => write!(f, "{}({})", function_name, args.iter().join(", ")), } } } @@ -106,6 +109,9 @@ impl AttrValExpr { Ok(IfElse(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?, box c.resolve_refs(variables)?)) } JsonAccess(box a, box b) => Ok(JsonAccess(box a.resolve_refs(variables)?, box b.resolve_refs(variables)?)), + FunctionCall(function_name, args) => { + Ok(FunctionCall(function_name, args.into_iter().map(|a| a.resolve_refs(variables)).collect::>()?)) + } } } @@ -131,6 +137,7 @@ impl AttrValExpr { refs.append(&mut b.var_refs()); refs } + FunctionCall(_, args) => args.iter().flat_map(|a| a.var_refs()).collect_vec(), } } @@ -196,6 +203,10 @@ impl AttrValExpr { _ => bail!("Unable to index into value {}", val), } } + AttrValExpr::FunctionCall(function_name, args) => { + let args = args.into_iter().map(|a| a.eval(values)).collect::>()?; + call_expr_function(&function_name, args) + } } } @@ -208,3 +219,17 @@ impl AttrValExpr { parsed.context("Failed to parse expression") } } + +fn call_expr_function(name: &str, args: Vec) -> Result { + match name { + "round" => match args.as_slice() { + [num, digits] => { + let num = num.as_f64()?; + let digits = digits.as_i32()?; + Ok(PrimVal::from(format!("{:.1$}", num, digits as usize))) + } + _ => Err(anyhow!("Incorrect number of arguments given to {}", name)), + }, + _ => Err(anyhow!("Unknown function {}", name)), + } +} diff --git a/src/value/attr_value/parser.rs b/src/value/attr_value/parser.rs index efe496e..6ace5c2 100644 --- a/src/value/attr_value/parser.rs +++ b/src/value/attr_value/parser.rs @@ -6,7 +6,7 @@ use nom::{ character::complete::{multispace0 as multispace, *}, combinator::{map, map_res, *}, error::{context, ParseError, VerboseError}, - multi::many0, + multi::{many0, separated_list0}, sequence::{delimited, preceded, *}, IResult, Parser, }; @@ -49,6 +49,12 @@ fn parse_unary_op(i: &str) -> IResult<&str, UnaryOp, VerboseError<&str>> { value(UnaryOp::Not, tag("!"))(i) } +fn parse_function_call(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { + let (i, name) = take_while(|c: char| c.is_ascii_alphanumeric() || c == '_')(i)?; + let (i, args) = delimited(tag("("), separated_list0(tag(","), ws(parse_factor)), tag(")"))(i)?; + Ok((i, AttrValExpr::FunctionCall(name.to_string(), args))) +} + ///////////////// // actual tree // ///////////////// @@ -58,6 +64,7 @@ fn parse_factor(i: &str) -> IResult<&str, AttrValExpr, VerboseError<&str>> { let (i, factor) = alt(( context("expression", ws(delimited(tag("("), parse_expr, tag(")")))), context("if-expression", ws(parse_ifelse)), + context("function-call", ws(parse_function_call)), context("literal", map(ws(parse_literal), |x| AttrValExpr::Literal(AttrVal::parse_string(x)))), context("identifier", map(ws(parse_identifier), |x| AttrValExpr::VarRef(VarName(x.to_string())))), ))(i)?;