diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 16cfab5a..d8453b7c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -162,21 +162,56 @@ mod parser_tests { assert_eq!(run("$.a[10:]"), Ok(vec![ ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Array, - ParseToken::Range(Some(10), None), + ParseToken::Range(Some(10), None, None), ParseToken::ArrayEof ])); assert_eq!(run("$.a[:11]"), Ok(vec![ ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Array, - ParseToken::Range(None, Some(11)), + ParseToken::Range(None, Some(11), None), ParseToken::ArrayEof ])); assert_eq!(run("$.a[-12:13]"), Ok(vec![ ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), 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 ])); @@ -266,7 +301,7 @@ mod parser_tests { assert_eq!(run("$[:]"), Ok(vec![ ParseToken::Absolute, ParseToken::Array, - ParseToken::Range(None, None), + ParseToken::Range(None, None, None), ParseToken::ArrayEof ])); diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 15fd49ac..b515acd3 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use super::tokenizer::*; const DUMMY: usize = 0; @@ -5,17 +7,12 @@ const DUMMY: usize = 0; type ParseResult = Result; mod utils { - pub fn string_to_isize(string: &String, msg_handler: F) -> Result - where F: Fn() -> String { - match string.as_str().parse::() { - Ok(n) => Ok(n), - _ => Err(msg_handler()) - } - } + use std::str::FromStr; - pub fn string_to_f64(string: &String, msg_handler: F) -> Result - where F: Fn() -> String { - match string.as_str().parse::() { + pub fn string_to_num(string: &String, msg_handler: F) -> Result + where F: Fn() -> String + { + match string.as_str().parse() { Ok(n) => Ok(n), _ => Err(msg_handler()) } @@ -43,7 +40,7 @@ pub enum ParseToken { // ?( filter ) Filter(FilterToken), // 1 : 2 - Range(Option, Option), + Range(Option, Option, Option), // 1, 2, 3 Union(Vec), @@ -291,7 +288,7 @@ impl Parser { debug!("#array_value_key"); match tokenizer.next_token() { 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); match tokenizer.peek_token() { @@ -348,7 +345,7 @@ impl Parser { Self::eat_whitespace(tokenizer); match tokenizer.next_token() { 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); } _ => { @@ -359,33 +356,70 @@ impl Parser { Ok(Self::node(ParseToken::Union(values))) } - fn range_from(num: isize, tokenizer: &mut TokenReader) -> ParseResult { + fn range_value(tokenizer: &mut TokenReader) -> Result, 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 { debug!("#range_from"); Self::eat_token(tokenizer); Self::eat_whitespace(tokenizer); + match tokenizer.peek_token() { 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 { debug!("#range_to"); + + match Self::range_value(tokenizer)? { + Some(step) => return Ok(Self::node(ParseToken::Range(None, None, Some(step)))), + _ => {} + } + match tokenizer.peek_token() { Ok(Token::CloseArray(_)) => { - return Ok(Self::node(ParseToken::Range(None, None))); + return Ok(Self::node(ParseToken::Range(None, None, None))); } _ => {} } match tokenizer.next_token() { - Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; - Ok(Self::node(ParseToken::Range(None, Some(digit)))) + Ok(Token::Key(pos, ref to_str)) => { + let to = utils::string_to_num(to_str, || tokenizer.err_msg_with_pos(pos))?; + let step = Self::range_value(tokenizer)?; + Ok(Self::node(ParseToken::Range(None, Some(to), step))) } _ => { Err(tokenizer.err_msg()) @@ -393,12 +427,13 @@ impl Parser { } } - fn range(num: isize, tokenizer: &mut TokenReader) -> ParseResult { + fn range(from: isize, tokenizer: &mut TokenReader) -> ParseResult { debug!("#range"); match tokenizer.next_token() { - Ok(Token::Key(pos, ref val)) => { - let digit = utils::string_to_isize(val, || tokenizer.err_msg_with_pos(pos))?; - Ok(Self::node(ParseToken::Range(Some(num), Some(digit)))) + Ok(Token::Key(pos, ref str_to)) => { + let to = utils::string_to_num(str_to, || tokenizer.err_msg_with_pos(pos))?; + let step = Self::range_value(tokenizer)?; + Ok(Self::node(ParseToken::Range(Some(from), Some(to), step))) } _ => { Err(tokenizer.err_msg()) @@ -505,7 +540,7 @@ impl Parser { 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))) } } @@ -528,7 +563,7 @@ impl Parser { f.push_str(&mut num); f.push('.'); 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))) } _ => { @@ -659,7 +694,7 @@ pub trait NodeVisitor { | ParseToken::Relative | ParseToken::All | ParseToken::Key(_) - | ParseToken::Range(_, _) + | ParseToken::Range(_, _, _) | ParseToken::Union(_) | ParseToken::Number(_) | ParseToken::Bool(_) => { diff --git a/src/select/mod.rs b/src/select/mod.rs index f2b19eae..fabc2f19 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -933,7 +933,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { unreachable!() } } - ParseToken::Range(from, to) => { + ParseToken::Range(from, to, step) => { if !self.terms.is_empty() { unimplemented!("range syntax in filter"); } @@ -955,7 +955,10 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { 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) { tmp.push(v); } diff --git a/tests/filter.rs b/tests/filter.rs index aa56e136..cfdd6a64 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -352,10 +352,17 @@ fn filer_same_obj() { } #[test] -fn empty_range() { +fn range() { setup(); 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]