Skip to content

Commit

Permalink
array range with step
Browse files Browse the repository at this point in the history
  • Loading branch information
freestrings committed Jun 18, 2019
1 parent 51deec6 commit 74666d2
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 34 deletions.
43 changes: 39 additions & 4 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
]));

Expand Down Expand Up @@ -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
]));

Expand Down
89 changes: 62 additions & 27 deletions src/parser/parser.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
use std::str::FromStr;

use super::tokenizer::*;

const DUMMY: usize = 0;

type ParseResult<T> = Result<T, String>;

mod utils {
pub fn string_to_isize<F>(string: &String, msg_handler: F) -> Result<isize, String>
where F: Fn() -> String {
match string.as_str().parse::<isize>() {
Ok(n) => Ok(n),
_ => Err(msg_handler())
}
}
use std::str::FromStr;

pub fn string_to_f64<F>(string: &String, msg_handler: F) -> Result<f64, String>
where F: Fn() -> String {
match string.as_str().parse::<f64>() {
pub fn string_to_num<F, S: FromStr>(string: &String, msg_handler: F) -> Result<S, String>
where F: Fn() -> String
{
match string.as_str().parse() {
Ok(n) => Ok(n),
_ => Err(msg_handler())
}
Expand Down Expand Up @@ -43,7 +40,7 @@ pub enum ParseToken {
// ?( filter )
Filter(FilterToken),
// 1 : 2
Range(Option<isize>, Option<isize>),
Range(Option<isize>, Option<isize>, Option<usize>),
// 1, 2, 3
Union(Vec<isize>),

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
}
_ => {
Expand All @@ -359,46 +356,84 @@ impl Parser {
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");
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<Node> {
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())
}
}
}

fn range(num: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
fn range(from: isize, tokenizer: &mut TokenReader) -> ParseResult<Node> {
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())
Expand Down Expand Up @@ -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)))
}
}
Expand All @@ -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)))
}
_ => {
Expand Down Expand Up @@ -659,7 +694,7 @@ pub trait NodeVisitor {
| ParseToken::Relative
| ParseToken::All
| ParseToken::Key(_)
| ParseToken::Range(_, _)
| ParseToken::Range(_, _, _)
| ParseToken::Union(_)
| ParseToken::Number(_)
| ParseToken::Bool(_) => {
Expand Down
7 changes: 5 additions & 2 deletions src/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand All @@ -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);
}
Expand Down
9 changes: 8 additions & 1 deletion tests/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down

0 comments on commit 74666d2

Please sign in to comment.