From e210fad45cab39602e290263ac081f18049cefe3 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Thu, 7 Jul 2022 11:59:47 +0100 Subject: [PATCH 1/9] Bump CTS to obtain descendant selector tests --- jsonpath-compliance-test-suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonpath-compliance-test-suite b/jsonpath-compliance-test-suite index 9235770..f4ac3fe 160000 --- a/jsonpath-compliance-test-suite +++ b/jsonpath-compliance-test-suite @@ -1 +1 @@ -Subproject commit 923577060d75a8111fb3a91bdc0ab8eb2d748f1b +Subproject commit f4ac3fe9e93c38f31f4b788dd1ecd09332a4c5b9 From a1fe7f8b380293de72b9303529264159b9262f52 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Thu, 7 Jul 2022 13:22:24 +0100 Subject: [PATCH 2/9] Descendant selectors --- src/ast.rs | 17 +++++++++++++++++ src/grammar.pest | 6 +++++- src/parser.rs | 14 ++++++++++++++ tests/cts.rs | 7 +++++-- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 14679e7..9c6bda0 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -54,6 +54,9 @@ pub enum Selector { Union(Vec), DotName(String), DotWildcard, + DescendantDotName(String), + DescendantDotWildcard, + DescendantUnionElement(UnionElement), } #[derive(Debug)] @@ -85,6 +88,20 @@ impl Selector { Value::Array(a) => Box::new(a.iter()), _ => Box::new(std::iter::empty()), }, + Selector::DescendantDotName(name) => Self::traverse(input, move |n: &'a Value| Box::new(n.get(name).into_iter())), + Selector::DescendantDotWildcard => Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))), + Selector::DescendantUnionElement(element) => Self::traverse(input, move |n: &'a Value| element.find(n)), + } + } + + // traverse applies the given closure to all the descendants of the input value and + // returns a nodelist. + fn traverse<'a, F>(input: &'a Value, f: F) -> NodeList<'a> + where F: Fn(&'a Value) -> NodeList<'a> + Copy + 'a { + match input { + Value::Object(m) => Box::new(m.into_iter().flat_map(move |(_k, v)| (&f)(v).chain(Self::traverse::<'a>(v, f)))), + Value::Array(a) => Box::new(a.iter().flat_map(move |v| (&f)(v).chain(Self::traverse::<'a>(v, f)))), + _ => Box::new(std::iter::empty()), } } } diff --git a/src/grammar.pest b/src/grammar.pest index 40caaf2..2936962 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -3,7 +3,7 @@ selector = ${ SOI ~ rootSelector ~ matchers ~ EOI } matchers = ${ matcher* } rootSelector = @{ "$" } -matcher = !{ dotChild | union } +matcher = !{ dotChild | union | descendant } dotChild = _{ wildcardedDotChild | namedDotChild } wildcardedDotChild = { ".*" } @@ -27,6 +27,10 @@ sliceStart = @{ integer } sliceEnd = @{ integer } sliceStep = @{ integer } +descendant = ${ ".." ~ descendantVariant } +descendantVariant = _{ childName | wildcard | "[" ~ unionElement ~ "]" } +wildcard = { "*" } + doubleQuotedString = _{ "\"" ~ doubleInner ~ "\"" } doubleInner = @{ doubleChar* } doubleChar = { diff --git a/src/parser.rs b/src/parser.rs index 8032499..2c282d0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -40,6 +40,7 @@ fn parse_selector(matcher_rule: pest::iterators::Pair) -> Result Selector::DotWildcard, Rule::namedDotChild => Selector::DotName(parse_child_name(r)), Rule::union => Selector::Union(parse_union_indices(r)?), + Rule::descendant => parse_descendant(r)?, _ => panic!("invalid parse tree {:?}", r), }) } @@ -115,6 +116,19 @@ fn parse_union_array_slice( })) } +fn parse_descendant(matcher_rule: pest::iterators::Pair) -> Result { + let r = matcher_rule.into_inner().next().unwrap(); + + Ok(match r.as_rule() { + Rule::childName => Selector::DescendantDotName(r.as_str().to_owned()), + Rule::wildcard => Selector::DescendantDotWildcard, + Rule::unionChild => Selector::DescendantUnionElement(parse_union_child(r)), + Rule::unionArraySlice => Selector::DescendantUnionElement(parse_union_array_slice(r)?), + Rule::unionArrayIndex => Selector::DescendantUnionElement(parse_union_array_index(r)?), + _ => panic!("invalid descendant {:?}", r), + }) +} + fn unescape(contents: &str) -> String { let s = format!(r#""{}""#, contents); serde_json::from_str(&s).unwrap() diff --git a/tests/cts.rs b/tests/cts.rs index 865aa1c..20ad5f3 100644 --- a/tests/cts.rs +++ b/tests/cts.rs @@ -90,10 +90,13 @@ mod tests { assert!(false, "find failed") // should not happen } } else { - if !t.invalid_selector { + if t.invalid_selector { + // print failure message + println!("{}: parsing `{}` failed with: {}", t.name, t.selector, path.err().expect("should be an error")); + } else { assert!( path.is_ok(), - "{}: parsing {} should have succeeded but failed: {}", + "{}: parsing `{}` should have succeeded but failed: {}", t.name, t.selector, path.err().expect("should be an error") From ac3346c69a9fd00f75bf2931b87f690bcc585109 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Wed, 3 Aug 2022 17:58:42 +0100 Subject: [PATCH 3/9] cargo fmt --- src/ast.rs | 26 ++++++++++++++++++++------ src/parser.rs | 2 +- tests/cts.rs | 7 ++++++- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 9c6bda0..440c169 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -88,19 +88,33 @@ impl Selector { Value::Array(a) => Box::new(a.iter()), _ => Box::new(std::iter::empty()), }, - Selector::DescendantDotName(name) => Self::traverse(input, move |n: &'a Value| Box::new(n.get(name).into_iter())), - Selector::DescendantDotWildcard => Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))), - Selector::DescendantUnionElement(element) => Self::traverse(input, move |n: &'a Value| element.find(n)), + Selector::DescendantDotName(name) => { + Self::traverse(input, move |n: &'a Value| Box::new(n.get(name).into_iter())) + } + Selector::DescendantDotWildcard => { + Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))) + } + Selector::DescendantUnionElement(element) => { + Self::traverse(input, move |n: &'a Value| element.find(n)) + } } } // traverse applies the given closure to all the descendants of the input value and // returns a nodelist. fn traverse<'a, F>(input: &'a Value, f: F) -> NodeList<'a> - where F: Fn(&'a Value) -> NodeList<'a> + Copy + 'a { + where + F: Fn(&'a Value) -> NodeList<'a> + Copy + 'a, + { match input { - Value::Object(m) => Box::new(m.into_iter().flat_map(move |(_k, v)| (&f)(v).chain(Self::traverse::<'a>(v, f)))), - Value::Array(a) => Box::new(a.iter().flat_map(move |v| (&f)(v).chain(Self::traverse::<'a>(v, f)))), + Value::Object(m) => Box::new( + m.into_iter() + .flat_map(move |(_k, v)| (&f)(v).chain(Self::traverse::<'a>(v, f))), + ), + Value::Array(a) => Box::new( + a.iter() + .flat_map(move |v| (&f)(v).chain(Self::traverse::<'a>(v, f))), + ), _ => Box::new(std::iter::empty()), } } diff --git a/src/parser.rs b/src/parser.rs index 2c282d0..649e449 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -40,7 +40,7 @@ fn parse_selector(matcher_rule: pest::iterators::Pair) -> Result Selector::DotWildcard, Rule::namedDotChild => Selector::DotName(parse_child_name(r)), Rule::union => Selector::Union(parse_union_indices(r)?), - Rule::descendant => parse_descendant(r)?, + Rule::descendant => parse_descendant(r)?, _ => panic!("invalid parse tree {:?}", r), }) } diff --git a/tests/cts.rs b/tests/cts.rs index 20ad5f3..5e235ee 100644 --- a/tests/cts.rs +++ b/tests/cts.rs @@ -92,7 +92,12 @@ mod tests { } else { if t.invalid_selector { // print failure message - println!("{}: parsing `{}` failed with: {}", t.name, t.selector, path.err().expect("should be an error")); + println!( + "{}: parsing `{}` failed with: {}", + t.name, + t.selector, + path.err().expect("should be an error") + ); } else { assert!( path.is_ok(), From 296e5ad0c48de038b9971c4d4c65b8f73150f682 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Wed, 3 Aug 2022 17:59:42 +0100 Subject: [PATCH 4/9] Fix lints --- src/ast.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 440c169..c897009 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -109,11 +109,11 @@ impl Selector { match input { Value::Object(m) => Box::new( m.into_iter() - .flat_map(move |(_k, v)| (&f)(v).chain(Self::traverse::<'a>(v, f))), + .flat_map(move |(_k, v)| f(v).chain(Self::traverse::<'a>(v, f))), ), Value::Array(a) => Box::new( a.iter() - .flat_map(move |v| (&f)(v).chain(Self::traverse::<'a>(v, f))), + .flat_map(move |v| f(v).chain(Self::traverse::<'a>(v, f))), ), _ => Box::new(std::iter::empty()), } From 156e2e6751bddcfc1df13f9b871edf05ac32ae97 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 5 Aug 2022 17:25:44 +0100 Subject: [PATCH 5/9] Add more tests --- jsonpath-compliance-test-suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonpath-compliance-test-suite b/jsonpath-compliance-test-suite index f4ac3fe..d28bc55 160000 --- a/jsonpath-compliance-test-suite +++ b/jsonpath-compliance-test-suite @@ -1 +1 @@ -Subproject commit f4ac3fe9e93c38f31f4b788dd1ecd09332a4c5b9 +Subproject commit d28bc55954590c8742a0999ad3abaf264113da5e From 81e15310f2dc1ed444102ee022cd1e340b87e84f Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 5 Aug 2022 17:28:10 +0100 Subject: [PATCH 6/9] Support descendant with wildcarded index --- src/grammar.pest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/grammar.pest b/src/grammar.pest index 2936962..0c4d483 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -28,7 +28,7 @@ sliceEnd = @{ integer } sliceStep = @{ integer } descendant = ${ ".." ~ descendantVariant } -descendantVariant = _{ childName | wildcard | "[" ~ unionElement ~ "]" } +descendantVariant = _{ childName | wildcard | "[" ~ wildcard ~ "]" | "[" ~ unionElement ~ "]" } wildcard = { "*" } doubleQuotedString = _{ "\"" ~ doubleInner ~ "\"" } From f8d566325cf5b902d496d9687527e6c2213884b5 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 5 Aug 2022 18:00:19 +0100 Subject: [PATCH 7/9] More descendant selectors * Index wildcard * List --- src/ast.rs | 6 +++--- src/grammar.pest | 8 +++++--- src/parser.rs | 6 ++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index c897009..779fcd2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -56,7 +56,7 @@ pub enum Selector { DotWildcard, DescendantDotName(String), DescendantDotWildcard, - DescendantUnionElement(UnionElement), + DescendantUnion(Vec), } #[derive(Debug)] @@ -94,8 +94,8 @@ impl Selector { Selector::DescendantDotWildcard => { Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))) } - Selector::DescendantUnionElement(element) => { - Self::traverse(input, move |n: &'a Value| element.find(n)) + Selector::DescendantUnion(indices) => { + Self::traverse(input, move |n: &'a Value| Box::new(indices.iter().flat_map(move |i| i.find(n)))) } } } diff --git a/src/grammar.pest b/src/grammar.pest index 0c4d483..64fc5f9 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -3,7 +3,7 @@ selector = ${ SOI ~ rootSelector ~ matchers ~ EOI } matchers = ${ matcher* } rootSelector = @{ "$" } -matcher = !{ dotChild | union | descendant } +matcher = !{ dotChild | union | wildcardedIndex | descendant } dotChild = _{ wildcardedDotChild | namedDotChild } wildcardedDotChild = { ".*" } @@ -17,7 +17,7 @@ char = { | '\u{80}'..'\u{10FFFF}' } -union = { "[" ~ unionElement ~ ("," ~ unionElement)* ~ "]" } +union = !{ "[" ~ unionElement ~ ("," ~ unionElement)* ~ "]" } unionElement = _{ unionChild | unionArraySlice | unionArrayIndex } unionChild = ${ doubleQuotedString | singleQuotedString } unionArrayIndex = @{ integer } @@ -27,8 +27,10 @@ sliceStart = @{ integer } sliceEnd = @{ integer } sliceStep = @{ integer } +wildcardedIndex = { "[" ~ "*" ~ "]" } + descendant = ${ ".." ~ descendantVariant } -descendantVariant = _{ childName | wildcard | "[" ~ wildcard ~ "]" | "[" ~ unionElement ~ "]" } +descendantVariant = _{ childName | wildcard | "[" ~ wildcard ~ "]" | union } wildcard = { "*" } doubleQuotedString = _{ "\"" ~ doubleInner ~ "\"" } diff --git a/src/parser.rs b/src/parser.rs index 649e449..3fb6855 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -40,6 +40,7 @@ fn parse_selector(matcher_rule: pest::iterators::Pair) -> Result Selector::DotWildcard, Rule::namedDotChild => Selector::DotName(parse_child_name(r)), Rule::union => Selector::Union(parse_union_indices(r)?), + Rule::wildcardedIndex => Selector::DotWildcard, Rule::descendant => parse_descendant(r)?, _ => panic!("invalid parse tree {:?}", r), }) @@ -122,10 +123,7 @@ fn parse_descendant(matcher_rule: pest::iterators::Pair) -> Result Selector::DescendantDotName(r.as_str().to_owned()), Rule::wildcard => Selector::DescendantDotWildcard, - Rule::unionChild => Selector::DescendantUnionElement(parse_union_child(r)), - Rule::unionArraySlice => Selector::DescendantUnionElement(parse_union_array_slice(r)?), - Rule::unionArrayIndex => Selector::DescendantUnionElement(parse_union_array_index(r)?), - _ => panic!("invalid descendant {:?}", r), + _ => Selector::DescendantUnion(parse_union_indices(r)?), }) } From 40d8d826d1bddf67173f3b15925fcbc477b516c7 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 5 Aug 2022 18:03:23 +0100 Subject: [PATCH 8/9] cargo fmt --- src/ast.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 779fcd2..785c6c2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -94,9 +94,9 @@ impl Selector { Selector::DescendantDotWildcard => { Self::traverse(input, move |n: &'a Value| Box::new(iter::once(n))) } - Selector::DescendantUnion(indices) => { - Self::traverse(input, move |n: &'a Value| Box::new(indices.iter().flat_map(move |i| i.find(n)))) - } + Selector::DescendantUnion(indices) => Self::traverse(input, move |n: &'a Value| { + Box::new(indices.iter().flat_map(move |i| i.find(n))) + }), } } From 22d0c0387a9f98d91d556cccff5a5e560486af53 Mon Sep 17 00:00:00 2001 From: Glyn Normington Date: Fri, 12 Aug 2022 16:23:34 +0100 Subject: [PATCH 9/9] Bump CTS --- jsonpath-compliance-test-suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonpath-compliance-test-suite b/jsonpath-compliance-test-suite index d28bc55..b139469 160000 --- a/jsonpath-compliance-test-suite +++ b/jsonpath-compliance-test-suite @@ -1 +1 @@ -Subproject commit d28bc55954590c8742a0999ad3abaf264113da5e +Subproject commit b139469a6bdd83f19ad9e544c4c14db09162f580