From de97e2f95a47cef45f407854142d2d941c3ea0f9 Mon Sep 17 00:00:00 2001 From: freestrings Date: Tue, 18 Jun 2019 11:46:24 +0900 Subject: [PATCH] array keys --- src/parser/mod.rs | 7 +++++++ src/parser/parser.rs | 45 +++++++++++++++++++++++++++++++++-------- src/parser/tokenizer.rs | 8 ++++---- src/select/mod.rs | 37 ++++++++++++++++++++++----------- tests/filter.rs | 4 ++++ 5 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d8453b7..f3b5c17 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -215,6 +215,13 @@ mod parser_tests { ParseToken::ArrayEof ])); + assert_eq!(run(r#"$["a", 'b']"#), Ok(vec![ + ParseToken::Absolute, + ParseToken::Array, + ParseToken::Keys(vec!["a".to_string(), "b".to_string()]), + ParseToken::ArrayEof + ])); + assert_eq!(run("$.a[?(1>2)]"), Ok(vec![ ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Array, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index b515acd..3258dec 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -33,6 +33,7 @@ pub enum ParseToken { All, Key(String), + Keys(Vec), // [] Array, // 메타토큰 @@ -232,12 +233,39 @@ impl Parser { } } - fn array_quota_value(tokenizer: &mut TokenReader) -> ParseResult { - debug!("#array_quota_value"); + fn array_keys(tokenizer: &mut TokenReader, first_key: String) -> ParseResult { + let mut keys = vec![first_key]; + while tokenizer.peek_is(COMMA) { + Self::eat_token(tokenizer); + Self::eat_whitespace(tokenizer); + + if !(tokenizer.peek_is(SINGLE_QUOTE) || tokenizer.peek_is(DOUBLE_QUOTE)) { + return Err(tokenizer.err_msg()); + } + + match tokenizer.next_token() { + Ok(Token::SingleQuoted(_, val)) + | Ok(Token::DoubleQuoted(_, val)) => { + keys.push(val); + } + _ => {} + } + + Self::eat_whitespace(tokenizer); + } + + Ok(Self::node(ParseToken::Keys(keys))) + } + + fn array_quote_value(tokenizer: &mut TokenReader) -> ParseResult { + debug!("#array_quote_value"); match tokenizer.next_token() { - Ok(Token::SingleQuoted(_, val)) - | Ok(Token::DoubleQuoted(_, val)) => { - Ok(Self::node(ParseToken::Key(val))) + Ok(Token::SingleQuoted(_, val)) | Ok(Token::DoubleQuoted(_, val)) => { + if !tokenizer.peek_is(COMMA) { + Ok(Self::node(ParseToken::Key(val))) + } else { + Self::array_keys(tokenizer, val) + } } Err(TokenError::Eof) => { Ok(Self::node(ParseToken::Eof)) @@ -322,7 +350,7 @@ impl Parser { } Ok(Token::DoubleQuoted(_, _)) | Ok(Token::SingleQuoted(_, _)) => { - Self::array_quota_value(tokenizer) + Self::array_quote_value(tokenizer) } Err(TokenError::Eof) => { Ok(Self::node(ParseToken::Eof)) @@ -602,8 +630,8 @@ impl Parser { return Self::json_path(tokenizer); } - if tokenizer.peek_is(DOUBLE_QUOTA) || tokenizer.peek_is(SINGLE_QUOTA) { - return Self::array_quota_value(tokenizer); + if tokenizer.peek_is(DOUBLE_QUOTE) || tokenizer.peek_is(SINGLE_QUOTE) { + return Self::array_quote_value(tokenizer); } if tokenizer.peek_is(KEY) { @@ -694,6 +722,7 @@ pub trait NodeVisitor { | ParseToken::Relative | ParseToken::All | ParseToken::Key(_) + | ParseToken::Keys(_) | ParseToken::Range(_, _, _) | ParseToken::Union(_) | ParseToken::Number(_) diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 3568e08..638f166 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -15,8 +15,8 @@ pub const SPLIT: &'static str = ":"; pub const OPEN_PARENTHESIS: &'static str = "("; pub const CLOSE_PARENTHESIS: &'static str = ")"; pub const KEY: &'static str = "Key"; -pub const DOUBLE_QUOTA: &'static str = "\""; -pub const SINGLE_QUOTA: &'static str = "'"; +pub const DOUBLE_QUOTE: &'static str = "\""; +pub const SINGLE_QUOTE: &'static str = "'"; pub const EQUAL: &'static str = "=="; pub const GREATER_OR_EQUAL: &'static str = ">="; pub const GREATER: &'static str = ">"; @@ -109,8 +109,8 @@ impl Token { Token::OpenParenthesis(_) => OPEN_PARENTHESIS, Token::CloseParenthesis(_) => CLOSE_PARENTHESIS, Token::Key(_, _) => KEY, - Token::DoubleQuoted(_, _) => DOUBLE_QUOTA, - Token::SingleQuoted(_, _) => SINGLE_QUOTA, + Token::DoubleQuoted(_, _) => DOUBLE_QUOTE, + Token::SingleQuoted(_, _) => SINGLE_QUOTE, Token::Equal(_) => EQUAL, Token::GreaterOrEqual(_) => GREATER_OR_EQUAL, Token::Greater(_) => GREATER, diff --git a/src/select/mod.rs b/src/select/mod.rs index fabc2f1..ffce0d7 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -693,20 +693,22 @@ impl<'a, 'b> Selector<'a, 'b> { debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current); } - fn next_from_current_with_str(&mut self, key: &str) { - fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, key: &str, visited: &mut HashSet<*const Value>) { + fn next_from_current_with_str(&mut self, keys: &Vec) { + fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, keys: &Vec, visited: &mut HashSet<*const Value>) { match v { Value::Object(map) => { - if let Some(v) = map.get(key) { - let ptr = v as *const Value; - if !visited.contains(&ptr) { - visited.insert(ptr); - tmp.push(v) + for key in keys { + if let Some(v) = map.get(key) { + let ptr = v as *const Value; + if !visited.contains(&ptr) { + visited.insert(ptr); + tmp.push(v) + } } } } Value::Array(vec) => for v in vec { - _collect(v, tmp, key, visited); + _collect(v, tmp, keys, visited); } _ => {} } @@ -716,12 +718,12 @@ impl<'a, 'b> Selector<'a, 'b> { let mut tmp = Vec::new(); let mut visited = HashSet::new(); for c in current { - _collect(c, &mut tmp, key, &mut visited); + _collect(c, &mut tmp, keys, &mut visited); } self.current = Some(tmp); } - debug!("next_from_current_with_str : {}, {:?}", key, self.current); + debug!("next_from_current_with_str : {:?}, {:?}", keys, self.current); } fn next_all_from_current(&mut self) { @@ -838,7 +840,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { self.next_from_current_with_num(to_f64(&n)); } ExprTerm::String(key) => { - self.next_from_current_with_str(&key); + self.next_from_current_with_str(&vec![key]); } ExprTerm::Json(_, v) => { if v.is_empty() { @@ -886,7 +888,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { self.all_from_current_with_str(key.as_str()) } ParseToken::In => { - self.next_from_current_with_str(key.as_str()) + self.next_from_current_with_str(&vec![key.clone()]) } _ => {} } @@ -905,6 +907,17 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { _ => {} } } + ParseToken::Keys(keys) => { + if !self.terms.is_empty() { + unimplemented!("keys in filter"); + } + + if let Some(ParseToken::Array) = self.tokens.pop() { + self.next_from_current_with_str(keys); + } else { + unreachable!(); + } + } ParseToken::Number(v) => { self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); } diff --git a/tests/filter.rs b/tests/filter.rs index cfdd6a6..9193ce4 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -40,6 +40,10 @@ fn array() { select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([ "Millicent Norman" ])); + + select_and_then_compare(r#"$.["eyeColor", "name"]"#, read_json("./benches/data_obj.json"), json!([ + "blue", "Leonor Herman" + ])); } #[test]