From 3dd784fb64094e53aa291a2edf56ddf266e16255 Mon Sep 17 00:00:00 2001 From: Mike Taghavi Date: Sun, 12 Mar 2023 13:07:02 +0100 Subject: [PATCH] Implement formats function using function generalization Remove initial formats from parser --- src/context.rs | 24 ++----- src/fmt.rs | 4 +- src/func.rs | 52 ++++++++++++++- src/grammar.pest | 8 +-- src/parser.rs | 165 ++++++++++++++++++++++++++++------------------- 5 files changed, 161 insertions(+), 92 deletions(-) diff --git a/src/context.rs b/src/context.rs index b772f69..7fa280b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,7 +1,6 @@ //! Module containing types and functionalities for //! evaluating jetro paths. -use crate::fmt as jetrofmt; use crate::parser; #[allow(unused_imports)] use serde::{Deserialize, Serialize}; @@ -207,7 +206,6 @@ pub enum Filter { Child(String), Descendant(String), Pick(Vec), - Format(FormatOp), ArrayIndex(usize), ArrayFrom(usize), ArrayTo(usize), @@ -231,7 +229,6 @@ struct StackItem<'a> { struct Context<'a> { root: Rc, stack: Rc>>>, - formater: Box, registry: Box, pub results: Rc>>>, } @@ -257,15 +254,17 @@ pub enum FuncArg { pub struct Func { pub name: String, pub args: Vec, + pub alias: Option, + pub should_deref: bool, } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::EmptyQuery => write!(f, "query is empty"), - Error::Parse(ref msg) => write!(f, "error while parsing query: {}", &msg), - Error::Eval(ref msg) => write!(f, "error while evaluating query: {}", &msg), - Error::FuncEval(ref msg) => write!(f, "error while evaluating function: {}", &msg), + Error::Parse(ref msg) => write!(f, "parse: {}", &msg), + Error::Eval(ref msg) => write!(f, "eval: {}", &msg), + Error::FuncEval(ref msg) => write!(f, "func_eval: {}", &msg), } } } @@ -386,6 +385,8 @@ impl Func { Self { name: String::new(), args: Vec::new(), + alias: None, + should_deref: false, } } } @@ -660,7 +661,6 @@ impl<'a> Context<'a> { let results: Rc>>> = Rc::new(RefCell::new(Vec::new())); let stack: Rc>>> = Rc::new(RefCell::new(Vec::new())); let rv: Rc = Rc::new(value); - let formater: Box = jetrofmt::default(); let registry: Box = crate::func::default_registry(); stack .borrow_mut() @@ -669,7 +669,6 @@ impl<'a> Context<'a> { Self { root: rv.clone(), stack, - formater, registry, results, } @@ -934,15 +933,6 @@ impl<'a> Context<'a> { _ => {} }, - (Filter::Format(ref target_format), Some(tail)) => { - match self.formater.eval(&target_format, ¤t.value) { - Some(output) => { - push_to_stack_or_produce!(self.results, self.stack, tail, output); - } - _ => {} - }; - } - (Filter::ArrayIndex(ref index), Some(tail)) => match *current.value { Value::Array(ref array) => { if *index < array.len() { diff --git a/src/fmt.rs b/src/fmt.rs index cba22e8..b454242 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -9,7 +9,7 @@ pub(crate) trait KeyFormater { fn eval(&self, target_format: &FormatOp, value: &Value) -> Option; } -struct FormatImpl; +pub(crate) struct FormatImpl; impl FormatImpl { fn format(&self, format: &str, value: &Value, keys: &Vec) -> Option { @@ -60,7 +60,7 @@ impl KeyFormater for FormatImpl { } } -pub(crate) fn default() -> Box { +pub(crate) fn default_formater() -> Box { Box::new(FormatImpl) } diff --git a/src/func.rs b/src/func.rs index 44a863c..a2e0677 100644 --- a/src/func.rs +++ b/src/func.rs @@ -1,6 +1,6 @@ //! Module func provides abstraction for jetro functions. -use crate::context::{Error, Func}; +use crate::context::{Error, Func, FuncArg}; use serde_json::Value; use std::collections::BTreeMap; @@ -62,10 +62,60 @@ impl Callable for Reverse { } } +struct Formats; + +impl Formats { + fn ensure_str(&mut self, value: &Value) -> Result<(), Error> { + match &value { + Value::String(_) => Ok(()), + _ => Err(Error::FuncEval("invalid type, expected string".to_string())), + } + } +} + +impl Callable for Formats { + fn call(&mut self, func: &Func, value: &Value) -> Result { + if func.args.len() < 2 { + return Err(Error::FuncEval("deficit number of arguments".to_string())); + } + let format = match func.args.get(0).unwrap() { + FuncArg::Key(some_key) => some_key, + _ => { + return Err(Error::FuncEval("invalid type, expected string".to_string())); + } + }; + let mut args: Vec = vec![]; + for v in func.args[1..].iter() { + match &v { + FuncArg::Key(some_key) => { + args.push(some_key.clone()); + } + _ => { + return Err(Error::FuncEval("invalid type, expected string".to_string())); + } + }; + } + + let formater: Box = crate::fmt::default_formater(); + match formater.eval( + &crate::context::FormatOp::FormatString { + format: format.to_string(), + arguments: args, + alias: func.alias.clone().unwrap(), + }, + &value, + ) { + Some(output) => Ok(output), + _ => Err(Error::FuncEval("format failed".to_string())), + } + } +} + impl Default for FuncRegistry { fn default() -> Self { let mut output = FuncRegistry::new(); output.register("reverse", Box::new(Reverse)); + output.register("formats", Box::new(Formats)); output } } diff --git a/src/grammar.pest b/src/grammar.pest index d37069a..7d7f2fd 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -51,8 +51,8 @@ pick = { ((",") ~ (" ")* ~ (literal_keyed | (sub_expression_keyed | sub_expression_keyed_reversed) ))* ~ (" ")* ~ ")" } -sub_expression = { path ~ (formatsFn | lenFn | allFn | pickFn | sumFn | filterFn | child | any_child | grouped_any | descendant_child | array_index | slice | array_to | array_from | fn)* } -sub_expression_reversed = { reverse_path ~ (formatsFn | lenFn | allFn | pickFn | sumFn | filterFn | grouped_any | child | any_child | descendant_child | array_index | slice | array_to | array_from | fn)* } +sub_expression = { path ~ (lenFn | allFn | pickFn | sumFn | filterFn | child | any_child | grouped_any | descendant_child | array_index | slice | array_to | array_from | fn)* } +sub_expression_reversed = { reverse_path ~ (lenFn | allFn | pickFn | sumFn | filterFn | grouped_any | child | any_child | descendant_child | array_index | slice | array_to | array_from | fn)* } sub_expression_keyed = { sub_expression ~ as? } sub_expression_keyed_reversed = { sub_expression_reversed ~ as? } @@ -65,8 +65,6 @@ lenFn = { slash ~ len } sumFn = { slash ~ sum } squashFn = { slash ~ squash } pickFn = { slash ~ pick } -formats = { sharp ~ "formats" ~ whitespace ~ lparen ~ literal ~ ((",")* ~ whitespace ~ literal)+ ~ whitespace ~ rparen ~ whitespace ~ (as | asDeref)} -formatsFn = { slash ~ formats } fnLit = { literal } fnExpr = { sub_expression } fnCall = { sharp ~ ident ~ whitespace ~ lparen ~ ((filterStmtCollection|fnLit|fnExpr ) ~ whitespace ~ ((",")* ~ whitespace ~ (filterStmtCollection|fnLit|fnExpr))*)? ~ whitespace ~ rparen ~ whitespace ~ (as | asDeref)? } @@ -79,6 +77,6 @@ filterFn = { slash ~ filter } expression = { (path|reverse_path) ~ - (formatsFn | lenFn | allFn | pickFn | sumFn | filterFn | grouped_any | child | any_child | descendant_child | array_index | slice | array_to | array_from | fn)* ~ + (lenFn | allFn | pickFn | sumFn | filterFn | grouped_any | child | any_child | descendant_child | array_index | slice | array_to | array_from | fn)* ~ EOI } diff --git a/src/parser.rs b/src/parser.rs index 522f718..dfde730 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -6,8 +6,8 @@ use std::rc::Rc; use pest::iterators::Pair; use crate::context::{ - Filter, FilterAST, FilterInner, FilterInnerRighthand, FilterLogicalOp, FilterOp, FormatOp, - Func, FuncArg, PickFilterInner, + Filter, FilterAST, FilterInner, FilterInnerRighthand, FilterLogicalOp, FilterOp, Func, FuncArg, + PickFilterInner, }; use crate::*; @@ -31,6 +31,7 @@ pub(crate) fn parse<'a>(input: &'a str) -> Result, pest::error::Erro Rule::r#fn => { let inner: Pair = token.clone().into_inner().nth(1).unwrap(); let mut func = Func::new(); + let mut should_deref = false; func.name .insert_str(0, inner.clone().into_inner().next().unwrap().as_str()); @@ -63,13 +64,53 @@ pub(crate) fn parse<'a>(input: &'a str) -> Result, pest::error::Erro Rule::filterStmtCollection => { todo!("handle filter statements"); } + Rule::r#as => { + func.alias = Some( + value + .clone() + .into_inner() + .next() + .unwrap() + .into_inner() + .as_str() + .to_string(), + ); + } + Rule::asDeref => { + func.alias = Some( + value + .clone() + .into_inner() + .next() + .unwrap() + .into_inner() + .as_str() + .to_string(), + ); + func.should_deref = true; + should_deref = true; + } _ => { todo!("handle unmatched arm of function generalization",); } } } + let alias = if func.alias.is_some() { + func.alias.clone() + } else { + None + }; actions.push(Filter::Function(func)); + if should_deref { + alias.is_some().then(|| { + let alias = alias.unwrap(); + actions.push(Filter::Pick(vec![PickFilterInner::KeyedStr { + key: alias.clone(), + alias: alias.clone(), + }])); + }); + } } Rule::path | Rule::reverse_path => actions.push(Filter::Root), Rule::allFn => actions.push(Filter::All), @@ -183,52 +224,6 @@ pub(crate) fn parse<'a>(input: &'a str) -> Result, pest::error::Erro } actions.push(Filter::MultiFilter(fast_root.unwrap())); } - Rule::formatsFn => { - let mut arguments: Vec = vec![]; - let mut elem = token.into_inner().nth(1).unwrap().into_inner(); - let format = elem.next().unwrap().into_inner().as_str().to_string(); - - // default alias for keyed subpath - let mut alias = "unknown".to_string(); - // sugar to return object containing only '{alias: eval expr}' - let mut should_deref = false; - - for e in elem { - match e.as_rule() { - Rule::literal => { - let name: String = e.into_inner().as_str().to_string(); - arguments.push(name); - } - Rule::r#as | Rule::asDeref => { - match e.as_rule() { - Rule::asDeref => should_deref = true, - _ => {} - }; - alias = e - .into_inner() - .next() - .unwrap() - .into_inner() - .as_str() - .to_string(); - } - _ => { - todo!("handle unmatched arm"); - } - } - } - actions.push(Filter::Format(FormatOp::FormatString { - format, - arguments, - alias: alias.clone(), - })); - if should_deref { - actions.push(Filter::Pick(vec![PickFilterInner::KeyedStr { - key: alias.clone(), - alias: alias.clone(), - }])); - } - } Rule::any_child => actions.push(Filter::AnyChild), Rule::array_index => { let elem = token.into_inner().nth(1).unwrap().as_span(); @@ -514,11 +509,16 @@ mod test { actions, vec![ Filter::Root, - Filter::Format(FormatOp::FormatString { - format: "{}{}".to_string(), - arguments: vec!["name".to_string(), "alias".to_string()], - alias: "some_key".to_string(), - }) + Filter::Function(Func { + name: "formats".to_string(), + args: vec![ + FuncArg::Key("{}{}".to_string()), + FuncArg::Key("name".to_string()), + FuncArg::Key("alias".to_string()), + ], + alias: Some("some_key".to_string()), + should_deref: false, + }), ] ); } @@ -701,22 +701,22 @@ mod test { } #[test] - fn formats_deref() { - let actions = parse(">/foo/#formats('Msg {}', 'msg') as* 'message'").unwrap(); + fn formats_without_deref() { + let actions = parse(">/foo/#formats('Msg {}', 'msg') as 'message'").unwrap(); assert_eq!( actions, vec![ Filter::Root, Filter::Child("foo".to_string()), - Filter::Format(FormatOp::FormatString { - format: "Msg {}".to_string(), - arguments: vec!["msg".to_string()], - alias: "message".to_string() + Filter::Function(Func { + name: "formats".to_string(), + args: vec![ + FuncArg::Key("Msg {}".to_string()), + FuncArg::Key("msg".to_string()), + ], + alias: Some("message".to_string()), + should_deref: false, }), - Filter::Pick(vec![PickFilterInner::KeyedStr { - key: "message".to_string(), - alias: "message".to_string(), - }]) ] ); } @@ -735,10 +735,14 @@ mod test { vec![ Filter::Root, Filter::Child("bar".to_string()), - Filter::Format(FormatOp::FormatString { - format: "Msg {}".to_string(), - arguments: vec!["msg".to_string()], - alias: "message".to_string(), + Filter::Function(Func { + name: "formats".to_string(), + args: vec![ + FuncArg::Key("Msg {}".to_string()), + FuncArg::Key("msg".to_string()), + ], + alias: Some("message".to_string()), + should_deref: true, }), Filter::Pick(vec![PickFilterInner::KeyedStr { key: "message".to_string(), @@ -812,8 +816,35 @@ mod test { Filter::Child("path".to_string()), ]) ], + alias: None, + should_deref: false, }) ] ); } + + #[test] + fn parse_call_ending_with_deref() { + let actions = parse(">/#call('format {}', 'a', 'a') as* 'test'").unwrap(); + assert_eq!( + actions, + vec![ + Filter::Root, + Filter::Function(Func { + name: "call".to_string(), + args: vec![ + FuncArg::Key("format {}".to_string()), + FuncArg::Key("a".to_string()), + FuncArg::Key("a".to_string()), + ], + alias: Some("test".to_string()), + should_deref: true, + }), + Filter::Pick(vec![PickFilterInner::KeyedStr { + key: "test".to_string(), + alias: "test".to_string(), + }]), + ] + ); + } }