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

Commit 5f537a2

Browse files
authored
Merge pull request #20 from mkmik/ast
Play with explicit AST
2 parents 4ad5133 + 2ec6147 commit 5f537a2

File tree

8 files changed

+206
-303
lines changed

8 files changed

+206
-303
lines changed

src/ast.rs

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
* Copyright 2020 VMware, Inc.
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
use serde_json::Value;
8+
9+
/// A path is a tree of selector nodes.
10+
///
11+
/// For example, the JSONPath `$.foo.bar` yields this AST:
12+
///
13+
/// ```text
14+
/// ^
15+
/// / \
16+
/// ^ \___ DotName("bar")
17+
/// / \
18+
/// Root ___ / \___ DotName("foo")
19+
/// ```
20+
///
21+
/// A more complicated example: `$.foo[1,2]["bar"]`:
22+
///
23+
/// ```text
24+
/// ^
25+
/// / \
26+
/// ^ \___ Union
27+
/// / \ \
28+
/// / \___ Union \___ [Name("bar")]
29+
/// / \
30+
/// ^ \___ [Index(1), Index(2)]
31+
/// / \
32+
/// Root ___/ \___ DotName("foo")
33+
/// ```
34+
///
35+
/// Selectors are left associative, thus `$.foo[1,2]["bar"]` behaves
36+
/// like (pseudocode) `(($.foo)[1,2])["bar"]`; thus the root of the resulting
37+
/// tree is actually the right-most selector (the last one to be applied).
38+
///
39+
/// The Path::Root AST node is called "root" because that's the
40+
/// name of the node in the JSONPath grammar. It represents the source of
41+
/// the json value stream which gets operated upon by Selector nodes.
42+
/// This is why despite being called "root", this node doesn't lie at the root
43+
/// of the AST tree.
44+
#[derive(Debug)]
45+
pub enum Path {
46+
Root,
47+
Sel(Box<Path>, Selector),
48+
}
49+
50+
#[derive(Debug)]
51+
pub enum Selector {
52+
Union(Vec<UnionElement>),
53+
DotName(String),
54+
DotWildcard,
55+
}
56+
57+
#[derive(Debug)]
58+
pub enum UnionElement {
59+
Name(String),
60+
Index(i64),
61+
}
62+
63+
type Iter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
64+
65+
impl Path {
66+
pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> {
67+
match self {
68+
Path::Root => Box::new(std::iter::once(input)),
69+
Path::Sel(left, sel) => Box::new(left.find(input).flat_map(move |v| sel.find(v))),
70+
}
71+
}
72+
}
73+
74+
impl Selector {
75+
pub fn find<'a>(&'a self, input: &'a Value) -> Iter<'a> {
76+
match self {
77+
Selector::Union(indices) => Box::new(indices.iter().flat_map(move |i| i.get(input))),
78+
Selector::DotName(name) => Box::new(input.get(name).into_iter()),
79+
Selector::DotWildcard => match input {
80+
Value::Object(m) => Box::new(m.values()),
81+
Value::Array(a) => Box::new(a.iter()),
82+
_ => Box::new(std::iter::empty()),
83+
},
84+
}
85+
}
86+
}
87+
88+
impl UnionElement {
89+
pub fn get<'a>(&self, v: &'a Value) -> Iter<'a> {
90+
match self {
91+
UnionElement::Name(name) => Box::new(v.get(name).into_iter()),
92+
UnionElement::Index(num) => Box::new(v.get(abs_index(*num, v)).into_iter()),
93+
}
94+
}
95+
}
96+
97+
fn abs_index(index: i64, node: &Value) -> usize {
98+
if index >= 0 {
99+
index as usize
100+
} else {
101+
let len = if let Value::Array(a) = node {
102+
a.len() as i64
103+
} else {
104+
0
105+
};
106+
(len + index) as usize
107+
}
108+
}
109+
110+
#[cfg(test)]
111+
mod test {
112+
use super::*;
113+
use crate::parser::parse;
114+
use serde_json::json;
115+
116+
#[test]
117+
fn demo() {
118+
let a1 = Path::Sel(Box::new(Path::Root), Selector::DotName("foo".to_owned()));
119+
let a2 = Path::Sel(Box::new(a1), Selector::DotName("bar".to_owned()));
120+
let a3 = Path::Sel(
121+
Box::new(a2),
122+
Selector::Union(vec![UnionElement::Name("baz".to_owned())]),
123+
);
124+
let a4 = Path::Sel(Box::new(a3), Selector::Union(vec![UnionElement::Index(4)]));
125+
126+
let j = json!({"foo":{"bar":{"baz":[10,20,30,40,50,60]}}});
127+
println!("j: {}", j);
128+
129+
let v = a4.find(&j).collect::<Vec<_>>();
130+
assert_eq!(v[0], 50);
131+
}
132+
133+
#[test]
134+
fn parse_demo() -> Result<(), String> {
135+
let p = parse("$.foo['bar'].*[4,-1]")?;
136+
println!("AST: {:?}", &p);
137+
let j = json!({"foo":{"bar":{"baz":[10,20,30,40,50,60]}}});
138+
139+
let v = p.find(&j).collect::<Vec<_>>();
140+
println!("RES: {:?}", v);
141+
142+
assert_eq!(v[0], 50);
143+
assert_eq!(v[1], 60);
144+
Ok(())
145+
}
146+
}

src/grammar.pest

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
selector = _{ SOI ~ jsonPath ~ EOI }
1+
selector = _{ SOI ~ rootSelector ~ matchers ~ EOI }
22

3-
jsonPath = ${ rootSelector ~ matcher* }
3+
matchers = ${ matcher* }
44
rootSelector = { "$" }
55

66
matcher = { dotChild | union }

src/jsonpath.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* SPDX-License-Identifier: BSD-2-Clause
55
*/
66

7+
use crate::ast;
78
use crate::parser;
8-
use crate::path::Path;
9+
use serde_json::Value;
910

1011
#[derive(Debug)]
1112
pub struct SyntaxError {
@@ -18,6 +19,19 @@ impl std::fmt::Display for SyntaxError {
1819
}
1920
}
2021

21-
pub fn parse(selector: &str) -> Result<impl Path, SyntaxError> {
22-
parser::parse(selector).map_err(|m| SyntaxError { message: m })
22+
pub enum FindError {
23+
// no errors yet
24+
}
25+
26+
pub fn parse(selector: &str) -> Result<Path, SyntaxError> {
27+
let p = parser::parse(selector).map_err(|m| SyntaxError { message: m })?;
28+
Ok(Path(p))
29+
}
30+
31+
pub struct Path(ast::Path);
32+
33+
impl Path {
34+
pub fn find<'a>(&'a self, document: &'a Value) -> Result<Vec<&'a Value>, FindError> {
35+
Ok(self.0.find(document).collect())
36+
}
2337
}

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ extern crate pest;
88
#[macro_use]
99
extern crate pest_derive;
1010

11+
pub mod ast;
1112
pub mod jsonpath;
12-
mod matchers;
1313
mod parser;
14-
pub mod path;

src/matchers.rs

Lines changed: 0 additions & 176 deletions
This file was deleted.

0 commit comments

Comments
 (0)