Skip to content
This repository was archived by the owner on Feb 22, 2024. It is now read-only.

Commit 13368a9

Browse files
authored
Merge pull request #4 from jsonpath-standard/union-child
union child
2 parents b7a42c6 + de3416e commit 13368a9

File tree

5 files changed

+574
-19
lines changed

5 files changed

+574
-19
lines changed

src/grammar.pest

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,35 @@ selector = _{ SOI ~ jsonPath ~ EOI }
33
jsonPath = ${ rootSelector ~ matcher* }
44
rootSelector = { "$" }
55

6-
matcher = { dotChild }
6+
matcher = { dotChild | union }
77

88
dotChild = _{ wildcardedDotChild | namedDotChild }
99
wildcardedDotChild = { ".*" }
1010
namedDotChild = ${ "." ~ childName }
1111
childName = @{ char+ }
1212
char = {
13-
!("\"" | "'" | "\\") ~ ANY
13+
!("\"" | "'" | "\\") ~ ANY // char is still TBD in the draft
14+
}
15+
16+
union = { "[" ~ unionElement ~ ("," ~ unionElement)* ~ "]" }
17+
unionElement = _{ unionChild }
18+
unionChild = { doubleQuotedString | singleQuotedString }
19+
20+
doubleQuotedString = _{ "\"" ~ doubleInner ~ "\"" }
21+
doubleInner = @{ doubleChar* }
22+
doubleChar = {
23+
!("\"" | "\\" | '\u{00}'..'\u{1F}') ~ ANY
24+
| "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
25+
| "\\" ~ ("u" ~ upperHexDigit{4})
26+
}
27+
upperHexDigit = _{ ASCII_DIGIT | "A" | "B" | "C" | "D" | "E" | "F" }
28+
29+
singleQuotedString = _{ "'" ~ singleInner ~ "'" }
30+
singleInner = @{ singleChar* }
31+
singleChar = {
32+
!("'" | "\\" | '\u{00}'..'\u{1F}') ~ ANY
33+
| "\\" ~ ("'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
34+
| "\\" ~ ("u" ~ upperHexDigit{4})
1435
}
1536

1637
WHITESPACE = _{ " " }

src/matchers.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,15 @@ impl Matcher for WildcardedChild {
3333
}
3434
}
3535

36-
pub struct DotChild {
36+
pub struct Child {
3737
name: String,
3838
}
3939

40-
pub fn new_dot_child_matcher(name: String) -> DotChild {
41-
DotChild { name }
40+
pub fn new_child_matcher(name: String) -> Child {
41+
Child { name }
4242
}
4343

44-
impl Matcher for DotChild {
44+
impl Matcher for Child {
4545
fn select<'a>(&self, node: &'a Value) -> Box<dyn Iterator<Item = &'a Value> + 'a> {
4646
if node.is_object() {
4747
let mapping = node.as_object().unwrap();
@@ -55,3 +55,25 @@ impl Matcher for DotChild {
5555
}
5656
}
5757
}
58+
59+
pub struct Union {
60+
elements: Vec<Box<dyn Matcher>>,
61+
}
62+
63+
pub fn new_union(elements: Vec<Box<dyn Matcher>>) -> Union {
64+
Union { elements }
65+
}
66+
67+
impl Matcher for Union {
68+
fn select<'a, 'b>(&'a self, node: &'b Value) -> Box<dyn Iterator<Item = &'b Value> + 'b> {
69+
// union of matches of the matchers in the union
70+
let mut u = vec![];
71+
for m in &self.elements {
72+
let m_selection = m.select(node);
73+
for s in m_selection {
74+
u.push(s);
75+
}
76+
}
77+
Box::new(u.into_iter())
78+
}
79+
}

src/parser.rs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ pub fn parse<'a>(selector: &'a str) -> Result<Box<dyn path::Path + 'a>, String>
2222
for r in selector_rule.into_inner() {
2323
match r.as_rule() {
2424
Rule::rootSelector => ms.push(Box::new(matchers::RootSelector {})),
25+
2526
Rule::matcher => {
2627
for m in parse_matcher(r) {
2728
ms.push(m)
2829
}
2930
}
31+
3032
_ => println!("r={:?}", r),
3133
}
3234
}
@@ -39,11 +41,19 @@ fn parse_matcher(matcher_rule: pest::iterators::Pair<Rule>) -> Vec<Box<dyn match
3941
for r in matcher_rule.into_inner() {
4042
match r.as_rule() {
4143
Rule::wildcardedDotChild => ms.push(Box::new(matchers::WildcardedChild {})),
44+
4245
Rule::namedDotChild => {
4346
for m in parse_dot_child_matcher(r) {
4447
ms.push(m)
4548
}
4649
}
50+
51+
Rule::union => {
52+
for m in parse_union(r) {
53+
ms.push(m)
54+
}
55+
}
56+
4757
_ => (),
4858
}
4959
}
@@ -56,10 +66,74 @@ fn parse_dot_child_matcher(
5666
let mut ms: Vec<Box<dyn matchers::Matcher>> = Vec::new();
5767
for r in matcher_rule.into_inner() {
5868
if let Rule::childName = r.as_rule() {
59-
ms.push(Box::new(matchers::new_dot_child_matcher(
60-
r.as_str().to_owned(),
61-
)));
69+
ms.push(Box::new(matchers::new_child_matcher(r.as_str().to_owned())));
6270
}
6371
}
6472
ms
6573
}
74+
75+
fn parse_union(matcher_rule: pest::iterators::Pair<Rule>) -> Vec<Box<dyn matchers::Matcher>> {
76+
let mut ms: Vec<Box<dyn matchers::Matcher>> = Vec::new();
77+
for r in matcher_rule.into_inner() {
78+
if let Rule::unionChild = r.as_rule() {
79+
for m in parse_union_child(r) {
80+
ms.push(m)
81+
}
82+
}
83+
}
84+
vec![Box::new(matchers::new_union(ms))]
85+
}
86+
87+
fn parse_union_child(matcher_rule: pest::iterators::Pair<Rule>) -> Vec<Box<dyn matchers::Matcher>> {
88+
let mut ms: Vec<Box<dyn matchers::Matcher>> = Vec::new();
89+
for r in matcher_rule.into_inner() {
90+
match r.as_rule() {
91+
Rule::doubleInner => {
92+
ms.push(Box::new(matchers::new_child_matcher(unescape(r.as_str()))));
93+
}
94+
95+
Rule::singleInner => {
96+
ms.push(Box::new(matchers::new_child_matcher(unescape(r.as_str()))));
97+
}
98+
99+
_ => (),
100+
}
101+
}
102+
ms
103+
}
104+
105+
const ESCAPED: &str = "\"'\\/bfnrt";
106+
const UNESCAPED: &str = "\"'\\/\u{0008}\u{000C}\u{000A}\u{000D}\u{0009}";
107+
108+
fn unescape(contents: &str) -> String {
109+
let mut output = String::new();
110+
let xs: Vec<char> = contents.chars().collect();
111+
let mut i = 0;
112+
while i < xs.len() {
113+
if xs[i] == '\\' {
114+
i += 1;
115+
if xs[i] == 'u' {
116+
i += 1;
117+
118+
// convert xs[i..i+4] to Unicode character and add it to the output
119+
let x = xs[i..i + 4].iter().collect::<String>();
120+
let n = u32::from_str_radix(&x, 16);
121+
let u = std::char::from_u32(n.unwrap());
122+
output.push(u.unwrap());
123+
124+
i += 4;
125+
} else {
126+
for (j, c) in ESCAPED.chars().enumerate() {
127+
if xs[i] == c {
128+
output.push(UNESCAPED.chars().nth(j).unwrap())
129+
}
130+
}
131+
i += 1;
132+
}
133+
} else {
134+
output.push(xs[i]);
135+
i += 1;
136+
}
137+
}
138+
output
139+
}

0 commit comments

Comments
 (0)