mirror of
https://github.com/fluencelabs/jsonpath
synced 2025-04-25 17:32:15 +00:00
array range with step
This commit is contained in:
parent
51deec66d0
commit
74666d264e
@ -162,21 +162,56 @@ mod parser_tests {
|
|||||||
assert_eq!(run("$.a[10:]"), Ok(vec![
|
assert_eq!(run("$.a[10:]"), Ok(vec![
|
||||||
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
||||||
ParseToken::Array,
|
ParseToken::Array,
|
||||||
ParseToken::Range(Some(10), None),
|
ParseToken::Range(Some(10), None, None),
|
||||||
ParseToken::ArrayEof
|
ParseToken::ArrayEof
|
||||||
]));
|
]));
|
||||||
|
|
||||||
assert_eq!(run("$.a[:11]"), Ok(vec![
|
assert_eq!(run("$.a[:11]"), Ok(vec![
|
||||||
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
||||||
ParseToken::Array,
|
ParseToken::Array,
|
||||||
ParseToken::Range(None, Some(11)),
|
ParseToken::Range(None, Some(11), None),
|
||||||
ParseToken::ArrayEof
|
ParseToken::ArrayEof
|
||||||
]));
|
]));
|
||||||
|
|
||||||
assert_eq!(run("$.a[-12:13]"), Ok(vec![
|
assert_eq!(run("$.a[-12:13]"), Ok(vec![
|
||||||
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
|
||||||
ParseToken::Array,
|
ParseToken::Array,
|
||||||
ParseToken::Range(Some(-12), Some(13)),
|
ParseToken::Range(Some(-12), Some(13), None),
|
||||||
|
ParseToken::ArrayEof
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(run(r#"$[0:3:2]"#), Ok(vec![
|
||||||
|
ParseToken::Absolute,
|
||||||
|
ParseToken::Array,
|
||||||
|
ParseToken::Range(Some(0), Some(3), Some(2)),
|
||||||
|
ParseToken::ArrayEof
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(run(r#"$[:3:2]"#), Ok(vec![
|
||||||
|
ParseToken::Absolute,
|
||||||
|
ParseToken::Array,
|
||||||
|
ParseToken::Range(None, Some(3), Some(2)),
|
||||||
|
ParseToken::ArrayEof
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(run(r#"$[:]"#), Ok(vec![
|
||||||
|
ParseToken::Absolute,
|
||||||
|
ParseToken::Array,
|
||||||
|
ParseToken::Range(None, None, None),
|
||||||
|
ParseToken::ArrayEof
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(run(r#"$[::]"#), Ok(vec![
|
||||||
|
ParseToken::Absolute,
|
||||||
|
ParseToken::Array,
|
||||||
|
ParseToken::Range(None, None, None),
|
||||||
|
ParseToken::ArrayEof
|
||||||
|
]));
|
||||||
|
|
||||||
|
assert_eq!(run(r#"$[::2]"#), Ok(vec![
|
||||||
|
ParseToken::Absolute,
|
||||||
|
ParseToken::Array,
|
||||||
|
ParseToken::Range(None, None, Some(2)),
|
||||||
ParseToken::ArrayEof
|
ParseToken::ArrayEof
|
||||||
]));
|
]));
|
||||||
|
|
||||||
@ -266,7 +301,7 @@ mod parser_tests {
|
|||||||
assert_eq!(run("$[:]"), Ok(vec![
|
assert_eq!(run("$[:]"), Ok(vec![
|
||||||
ParseToken::Absolute,
|
ParseToken::Absolute,
|
||||||
ParseToken::Array,
|
ParseToken::Array,
|
||||||
ParseToken::Range(None, None),
|
ParseToken::Range(None, None, None),
|
||||||
ParseToken::ArrayEof
|
ParseToken::ArrayEof
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use super::tokenizer::*;
|
use super::tokenizer::*;
|
||||||
|
|
||||||
const DUMMY: usize = 0;
|
const DUMMY: usize = 0;
|
||||||
@ -5,17 +7,12 @@ const DUMMY: usize = 0;
|
|||||||
type ParseResult<T> = Result<T, String>;
|
type ParseResult<T> = Result<T, String>;
|
||||||
|
|
||||||
mod utils {
|
mod utils {
|
||||||
pub fn string_to_isize<F>(string: &String, msg_handler: F) -> Result<isize, String>
|
use std::str::FromStr;
|
||||||
where F: Fn() -> String {
|
|
||||||
match string.as_str().parse::<isize>() {
|
|
||||||
Ok(n) => Ok(n),
|
|
||||||
_ => Err(msg_handler())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string_to_f64<F>(string: &String, msg_handler: F) -> Result<f64, String>
|
pub fn string_to_num<F, S: FromStr>(string: &String, msg_handler: F) -> Result<S, String>
|
||||||
where F: Fn() -> String {
|
where F: Fn() -> String
|
||||||
match string.as_str().parse::<f64>() {
|
{
|
||||||
|
match string.as_str().parse() {
|
||||||
Ok(n) => Ok(n),
|
Ok(n) => Ok(n),
|
||||||
_ => Err(msg_handler())
|
_ => Err(msg_handler())
|
||||||
}
|
}
|
||||||
@ -43,7 +40,7 @@ pub enum ParseToken {
|
|||||||
// ?( filter )
|
// ?( filter )
|
||||||
Filter(FilterToken),
|
Filter(FilterToken),
|
||||||
// 1 : 2
|
// 1 : 2
|
||||||
Range(Option<isize>, Option<isize>),
|
Range(Option<isize>, Option<isize>, Option<usize>),
|
||||||
// 1, 2, 3
|
// 1, 2, 3
|
||||||
Union(Vec<isize>),
|
Union(Vec<isize>),
|
||||||
|
|
||||||
@ -291,7 +288,7 @@ impl Parser {
|
|||||||
debug!("#array_value_key");
|
debug!("#array_value_key");
|
||||||
match tokenizer.next_token() {
|
match tokenizer.next_token() {
|
||||||
Ok(Token::Key(pos, ref val)) => {
|
Ok(Token::Key(pos, ref val)) => {
|
||||||
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?;
|
let digit = utils::string_to_num(val, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
Self::eat_whitespace(tokenizer);
|
Self::eat_whitespace(tokenizer);
|
||||||
|
|
||||||
match tokenizer.peek_token() {
|
match tokenizer.peek_token() {
|
||||||
@ -348,7 +345,7 @@ impl Parser {
|
|||||||
Self::eat_whitespace(tokenizer);
|
Self::eat_whitespace(tokenizer);
|
||||||
match tokenizer.next_token() {
|
match tokenizer.next_token() {
|
||||||
Ok(Token::Key(pos, ref val)) => {
|
Ok(Token::Key(pos, ref val)) => {
|
||||||
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?;
|
let digit = utils::string_to_num(val, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
values.push(digit);
|
values.push(digit);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -359,33 +356,70 @@ impl Parser {
|
|||||||
Ok(Self::node(ParseToken::Union(values)))
|
Ok(Self::node(ParseToken::Union(values)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn range_from(num: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
fn range_value<S: FromStr>(tokenizer: &mut TokenReader) -> Result<Option<S>, String> {
|
||||||
|
if tokenizer.peek_is(SPLIT) {
|
||||||
|
Self::eat_token(tokenizer);
|
||||||
|
Self::eat_whitespace(tokenizer);
|
||||||
|
|
||||||
|
if tokenizer.peek_is(KEY) {
|
||||||
|
match tokenizer.next_token() {
|
||||||
|
Ok(Token::Key(pos, str_step)) => {
|
||||||
|
match utils::string_to_num(&str_step, || tokenizer.err_msg_with_pos(pos)) {
|
||||||
|
Ok(step) => Ok(Some(step)),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range_from(from: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
||||||
debug!("#range_from");
|
debug!("#range_from");
|
||||||
Self::eat_token(tokenizer);
|
Self::eat_token(tokenizer);
|
||||||
Self::eat_whitespace(tokenizer);
|
Self::eat_whitespace(tokenizer);
|
||||||
|
|
||||||
match tokenizer.peek_token() {
|
match tokenizer.peek_token() {
|
||||||
Ok(Token::Key(_, _)) => {
|
Ok(Token::Key(_, _)) => {
|
||||||
Self::range(num, tokenizer)
|
Self::range(from, tokenizer)
|
||||||
|
}
|
||||||
|
Ok(Token::Split(_)) => {
|
||||||
|
match Self::range_value(tokenizer)? {
|
||||||
|
Some(step) => Ok(Self::node(ParseToken::Range(Some(from), None, Some(step)))),
|
||||||
|
_ => Ok(Self::node(ParseToken::Range(Some(from), None, None)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Ok(Self::node(ParseToken::Range(Some(num), None)))
|
Ok(Self::node(ParseToken::Range(Some(from), None, None)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn range_to(tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
fn range_to(tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
||||||
debug!("#range_to");
|
debug!("#range_to");
|
||||||
|
|
||||||
|
match Self::range_value(tokenizer)? {
|
||||||
|
Some(step) => return Ok(Self::node(ParseToken::Range(None, None, Some(step)))),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
match tokenizer.peek_token() {
|
match tokenizer.peek_token() {
|
||||||
Ok(Token::CloseArray(_)) => {
|
Ok(Token::CloseArray(_)) => {
|
||||||
return Ok(Self::node(ParseToken::Range(None, None)));
|
return Ok(Self::node(ParseToken::Range(None, None, None)));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match tokenizer.next_token() {
|
match tokenizer.next_token() {
|
||||||
Ok(Token::Key(pos, ref val)) => {
|
Ok(Token::Key(pos, ref to_str)) => {
|
||||||
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?;
|
let to = utils::string_to_num(to_str, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
Ok(Self::node(ParseToken::Range(None, Some(digit))))
|
let step = Self::range_value(tokenizer)?;
|
||||||
|
Ok(Self::node(ParseToken::Range(None, Some(to), step)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Err(tokenizer.err_msg())
|
Err(tokenizer.err_msg())
|
||||||
@ -393,12 +427,13 @@ impl Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn range(num: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
fn range(from: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
|
||||||
debug!("#range");
|
debug!("#range");
|
||||||
match tokenizer.next_token() {
|
match tokenizer.next_token() {
|
||||||
Ok(Token::Key(pos, ref val)) => {
|
Ok(Token::Key(pos, ref str_to)) => {
|
||||||
let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?;
|
let to = utils::string_to_num(str_to, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
Ok(Self::node(ParseToken::Range(Some(num), Some(digit))))
|
let step = Self::range_value(tokenizer)?;
|
||||||
|
Ok(Self::node(ParseToken::Range(Some(from), Some(to), step)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
Err(tokenizer.err_msg())
|
Err(tokenizer.err_msg())
|
||||||
@ -505,7 +540,7 @@ impl Parser {
|
|||||||
Self::term_num_float(val.as_str(), tokenizer)
|
Self::term_num_float(val.as_str(), tokenizer)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let number = utils::string_to_f64(&val, || tokenizer.err_msg_with_pos(pos))?;
|
let number = utils::string_to_num(&val, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
Ok(Self::node(ParseToken::Number(number)))
|
Ok(Self::node(ParseToken::Number(number)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -528,7 +563,7 @@ impl Parser {
|
|||||||
f.push_str(&mut num);
|
f.push_str(&mut num);
|
||||||
f.push('.');
|
f.push('.');
|
||||||
f.push_str(frac.as_str());
|
f.push_str(frac.as_str());
|
||||||
let number = utils::string_to_f64(&f, || tokenizer.err_msg_with_pos(pos))?;
|
let number = utils::string_to_num(&f, || tokenizer.err_msg_with_pos(pos))?;
|
||||||
Ok(Self::node(ParseToken::Number(number)))
|
Ok(Self::node(ParseToken::Number(number)))
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -659,7 +694,7 @@ pub trait NodeVisitor {
|
|||||||
| ParseToken::Relative
|
| ParseToken::Relative
|
||||||
| ParseToken::All
|
| ParseToken::All
|
||||||
| ParseToken::Key(_)
|
| ParseToken::Key(_)
|
||||||
| ParseToken::Range(_, _)
|
| ParseToken::Range(_, _, _)
|
||||||
| ParseToken::Union(_)
|
| ParseToken::Union(_)
|
||||||
| ParseToken::Number(_)
|
| ParseToken::Number(_)
|
||||||
| ParseToken::Bool(_) => {
|
| ParseToken::Bool(_) => {
|
||||||
|
@ -933,7 +933,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParseToken::Range(from, to) => {
|
ParseToken::Range(from, to, step) => {
|
||||||
if !self.terms.is_empty() {
|
if !self.terms.is_empty() {
|
||||||
unimplemented!("range syntax in filter");
|
unimplemented!("range syntax in filter");
|
||||||
}
|
}
|
||||||
@ -955,7 +955,10 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> {
|
|||||||
vec.len()
|
vec.len()
|
||||||
};
|
};
|
||||||
|
|
||||||
for i in from..to {
|
for i in (from..to).step_by(match step {
|
||||||
|
Some(step) => *step,
|
||||||
|
_ => 1
|
||||||
|
}) {
|
||||||
if let Some(v) = vec.get(i) {
|
if let Some(v) = vec.get(i) {
|
||||||
tmp.push(v);
|
tmp.push(v);
|
||||||
}
|
}
|
||||||
|
@ -352,10 +352,17 @@ fn filer_same_obj() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_range() {
|
fn range() {
|
||||||
setup();
|
setup();
|
||||||
|
|
||||||
select_and_then_compare("$[:]", json!(["first", "second"]), json!(["first", "second"]));
|
select_and_then_compare("$[:]", json!(["first", "second"]), json!(["first", "second"]));
|
||||||
|
select_and_then_compare("$[::]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "second", "third", "forth", "fifth"]));
|
||||||
|
select_and_then_compare("$[::2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "third", "fifth"]));
|
||||||
|
select_and_then_compare("$[1: :]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second", "third", "forth", "fifth"]));
|
||||||
|
select_and_then_compare("$[1:2:]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second"]));
|
||||||
|
select_and_then_compare("$[1::2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["second", "forth"]));
|
||||||
|
select_and_then_compare("$[0:3:1]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "second", "third"]));
|
||||||
|
select_and_then_compare("$[0:3:2]", json!(["first", "second", "third", "forth", "fifth"]), json!(["first", "third"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user