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

Commit 1fbd1e0

Browse files
author
Marko Mikulicic
committed
Play with explicit AST
1 parent 4ad5133 commit 1fbd1e0

File tree

3 files changed

+243
-0
lines changed

3 files changed

+243
-0
lines changed

src/ast.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use serde_json::Value;
8+
9+
#[derive(Debug)]
10+
pub enum Path {
11+
Root(),
12+
Sel(Box<Path>, Selector),
13+
}
14+
15+
#[derive(Debug)]
16+
pub enum Selector {
17+
Dot(Index),
18+
Union(Vec<Index>),
19+
}
20+
21+
#[derive(Debug)]
22+
pub enum Index {
23+
Field(String),
24+
Number(i64),
25+
}
26+
27+
type Iter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
28+
29+
impl Path {
30+
pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> {
31+
match self {
32+
Path::Root() => Box::new(std::iter::once(input)),
33+
Path::Sel(left, sel) => Box::new(left.find(input).flat_map(move |v| sel.find(v))),
34+
}
35+
}
36+
}
37+
38+
impl Selector {
39+
pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> {
40+
match self {
41+
Selector::Dot(index) => Box::new(index.get(input)),
42+
Selector::Union(indices) => Box::new(indices.iter().flat_map(move |i| i.get(input))),
43+
}
44+
}
45+
}
46+
47+
impl Index {
48+
pub fn get<'a>(&self, v: &'a Value) -> Iter<'a> {
49+
match self {
50+
Index::Field(name) => Box::new(v.get(name).into_iter()),
51+
Index::Number(num) => Box::new(v.get(abs_index(*num, v)).into_iter()),
52+
}
53+
}
54+
}
55+
56+
fn abs_index(index: i64, node: &Value) -> usize {
57+
if index >= 0 {
58+
index as usize
59+
} else {
60+
let len = if let Value::Array(a) = node {
61+
a.len() as i64
62+
} else {
63+
0
64+
};
65+
(len + index) as usize
66+
}
67+
}
68+
69+
#[cfg(test)]
70+
mod test {
71+
use super::*;
72+
use crate::parser_ast::parse;
73+
use serde_json::json;
74+
75+
#[test]
76+
fn demo() {
77+
let a1 = Path::Sel(
78+
Box::new(Path::Root()),
79+
Selector::Dot(Index::Field("foo".to_owned())),
80+
);
81+
let a2 = Path::Sel(Box::new(a1), Selector::Dot(Index::Field("bar".to_owned())));
82+
let a3 = Path::Sel(
83+
Box::new(a2),
84+
Selector::Union(vec![Index::Field("baz".to_owned())]),
85+
);
86+
let a4 = Path::Sel(Box::new(a3), Selector::Union(vec![Index::Number(4)]));
87+
88+
let j = json!({"foo":{"bar":{"baz":[10,20,30,40,50,60]}}});
89+
println!("j: {}", j);
90+
91+
let v = a4.find(&j).collect::<Vec<_>>();
92+
assert_eq!(v[0], 50);
93+
}
94+
95+
#[test]
96+
fn parse_demo() -> Result<(), String> {
97+
let p = parse("$.foo.bar['baz'][4,-1]")?;
98+
println!("AST: {:?}", &p);
99+
let j = json!({"foo":{"bar":{"baz":[10,20,30,40,50,60]}}});
100+
101+
let v = p.find(&j).collect::<Vec<_>>();
102+
println!("RES: {:?}", v);
103+
104+
assert_eq!(v[0], 50);
105+
assert_eq!(v[1], 60);
106+
Ok(())
107+
}
108+
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ pub mod jsonpath;
1212
mod matchers;
1313
mod parser;
1414
pub mod path;
15+
16+
pub mod ast;
17+
pub mod parser_ast;

src/parser_ast.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use crate::ast::*;
8+
use crate::pest::Parser;
9+
10+
#[derive(Parser)]
11+
#[grammar = "grammar.pest"]
12+
struct PathParser;
13+
14+
pub fn parse(selector: &str) -> Result<Path, String> {
15+
let selector_rule = PathParser::parse(Rule::selector, selector)
16+
.map_err(|e| format!("{}", e))?
17+
.next()
18+
.unwrap();
19+
20+
let mut res = Path::Root();
21+
for r in selector_rule.into_inner() {
22+
res = match r.as_rule() {
23+
Rule::rootSelector => res,
24+
Rule::matcher => Path::Sel(Box::new(res), parse_selector(r)),
25+
_ => panic!("invalid parse tree {:?}", r),
26+
}
27+
}
28+
Ok(res)
29+
}
30+
31+
fn parse_selector(matcher_rule: pest::iterators::Pair<Rule>) -> Selector {
32+
for r in matcher_rule.into_inner() {
33+
match r.as_rule() {
34+
Rule::wildcardedDotChild => {} // TODO: fix grammar so that this is a silent rule since we don't need it
35+
Rule::namedDotChild => return Selector::Dot(parse_child_name(r)),
36+
Rule::union => return Selector::Union(parse_union_indices(r)),
37+
_ => panic!("invalid parse tree {:?}", r),
38+
}
39+
}
40+
panic!("invalid parse tree")
41+
}
42+
43+
fn parse_child_name(matcher_rule: pest::iterators::Pair<Rule>) -> Index {
44+
let r = matcher_rule.into_inner().next().unwrap();
45+
match r.as_rule() {
46+
Rule::childName => Index::Field(r.as_str().to_owned()),
47+
_ => panic!("invalid parse tree {:?}", r),
48+
}
49+
}
50+
51+
fn parse_union_indices(matcher_rule: pest::iterators::Pair<Rule>) -> Vec<Index> {
52+
let mut res = Vec::new();
53+
54+
for r in matcher_rule.into_inner() {
55+
match r.as_rule() {
56+
Rule::unionChild => res.append(&mut parse_union_child(r)),
57+
Rule::unionArrayIndex => res.push(parse_union_array_index(r)),
58+
_ => panic!("invalid parse tree {:?}", r),
59+
}
60+
}
61+
res
62+
}
63+
64+
fn parse_union_child(matcher_rule: pest::iterators::Pair<Rule>) -> Vec<Index> {
65+
let mut res = Vec::new();
66+
for r in matcher_rule.into_inner() {
67+
match r.as_rule() {
68+
Rule::doubleInner => res.push(Index::Field(unescape(r.as_str()))),
69+
Rule::singleInner => res.push(Index::Field(unescape_single(r.as_str()))),
70+
_ => panic!("invalid parse tree {:?}", r),
71+
}
72+
}
73+
res
74+
}
75+
76+
fn parse_union_array_index(matcher_rule: pest::iterators::Pair<Rule>) -> Index {
77+
let i = matcher_rule.as_str().parse().unwrap();
78+
Index::Number(i)
79+
}
80+
81+
fn unescape(contents: &str) -> String {
82+
let s = format!(r#""{}""#, contents);
83+
serde_json::from_str(&s).unwrap()
84+
}
85+
86+
fn unescape_single(contents: &str) -> String {
87+
let d = to_double_quoted(contents);
88+
unescape(&d)
89+
}
90+
91+
// converts a single quoted string body into a string that can be unescaped
92+
// by a function that knows how to unescape double quoted string,
93+
// It works by unescaping single quotes and escaping double quotes while leaving
94+
// everything else untouched.
95+
fn to_double_quoted(contents: &str) -> String {
96+
let mut output = String::new();
97+
let mut escaping = false;
98+
for ch in contents.chars() {
99+
if !escaping {
100+
if ch == '\\' {
101+
escaping = true;
102+
} else {
103+
if ch == '"' {
104+
output.push('\\');
105+
}
106+
output.push(ch);
107+
}
108+
} else {
109+
escaping = false;
110+
if ch != '\'' {
111+
output.push('\\');
112+
};
113+
output.push(ch);
114+
}
115+
}
116+
output
117+
}
118+
119+
#[cfg(test)]
120+
mod test {
121+
use super::*;
122+
123+
#[test]
124+
fn test_to_double() {
125+
assert_eq!(to_double_quoted(r#"ab"#), r#"ab"#);
126+
assert_eq!(to_double_quoted(r#"a"b"#), r#"a\"b"#);
127+
assert_eq!(to_double_quoted(r#"a\'b"#), r#"a'b"#);
128+
assert_eq!(to_double_quoted(r#"a\nb"#), r#"a\nb"#);
129+
assert_eq!(to_double_quoted(r#"a\bb"#), r#"a\bb"#);
130+
assert_eq!(to_double_quoted(r#"a\\b"#), r#"a\\b"#);
131+
}
132+
}

0 commit comments

Comments
 (0)