array range with step

This commit is contained in:
freestrings 2019-06-17 18:05:02 +09:00
parent 51deec66d0
commit 74666d264e
4 changed files with 114 additions and 34 deletions

View File

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

View File

@ -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(_) => {

View File

@ -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);
} }

View File

@ -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]