array keys

This commit is contained in:
freestrings 2019-06-18 11:46:24 +09:00
parent 74666d264e
commit de97e2f95a
5 changed files with 77 additions and 24 deletions

View File

@ -215,6 +215,13 @@ mod parser_tests {
ParseToken::ArrayEof 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![ assert_eq!(run("$.a[?(1>2)]"), Ok(vec![
ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()), ParseToken::Absolute, ParseToken::In, ParseToken::Key("a".to_owned()),
ParseToken::Array, ParseToken::Array,

View File

@ -33,6 +33,7 @@ pub enum ParseToken {
All, All,
Key(String), Key(String),
Keys(Vec<String>),
// [] // []
Array, Array,
// 메타토큰 // 메타토큰
@ -232,12 +233,39 @@ impl Parser {
} }
} }
fn array_quota_value(tokenizer: &mut TokenReader) -> ParseResult<Node> { fn array_keys(tokenizer: &mut TokenReader, first_key: String) -> ParseResult<Node> {
debug!("#array_quota_value"); 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<Node> {
debug!("#array_quote_value");
match tokenizer.next_token() { match tokenizer.next_token() {
Ok(Token::SingleQuoted(_, val)) Ok(Token::SingleQuoted(_, val)) | Ok(Token::DoubleQuoted(_, val)) => {
| Ok(Token::DoubleQuoted(_, val)) => { if !tokenizer.peek_is(COMMA) {
Ok(Self::node(ParseToken::Key(val))) Ok(Self::node(ParseToken::Key(val)))
} else {
Self::array_keys(tokenizer, val)
}
} }
Err(TokenError::Eof) => { Err(TokenError::Eof) => {
Ok(Self::node(ParseToken::Eof)) Ok(Self::node(ParseToken::Eof))
@ -322,7 +350,7 @@ impl Parser {
} }
Ok(Token::DoubleQuoted(_, _)) Ok(Token::DoubleQuoted(_, _))
| Ok(Token::SingleQuoted(_, _)) => { | Ok(Token::SingleQuoted(_, _)) => {
Self::array_quota_value(tokenizer) Self::array_quote_value(tokenizer)
} }
Err(TokenError::Eof) => { Err(TokenError::Eof) => {
Ok(Self::node(ParseToken::Eof)) Ok(Self::node(ParseToken::Eof))
@ -602,8 +630,8 @@ impl Parser {
return Self::json_path(tokenizer); return Self::json_path(tokenizer);
} }
if tokenizer.peek_is(DOUBLE_QUOTA) || tokenizer.peek_is(SINGLE_QUOTA) { if tokenizer.peek_is(DOUBLE_QUOTE) || tokenizer.peek_is(SINGLE_QUOTE) {
return Self::array_quota_value(tokenizer); return Self::array_quote_value(tokenizer);
} }
if tokenizer.peek_is(KEY) { if tokenizer.peek_is(KEY) {
@ -694,6 +722,7 @@ pub trait NodeVisitor {
| ParseToken::Relative | ParseToken::Relative
| ParseToken::All | ParseToken::All
| ParseToken::Key(_) | ParseToken::Key(_)
| ParseToken::Keys(_)
| ParseToken::Range(_, _, _) | ParseToken::Range(_, _, _)
| ParseToken::Union(_) | ParseToken::Union(_)
| ParseToken::Number(_) | ParseToken::Number(_)

View File

@ -15,8 +15,8 @@ pub const SPLIT: &'static str = ":";
pub const OPEN_PARENTHESIS: &'static str = "("; pub const OPEN_PARENTHESIS: &'static str = "(";
pub const CLOSE_PARENTHESIS: &'static str = ")"; pub const CLOSE_PARENTHESIS: &'static str = ")";
pub const KEY: &'static str = "Key"; pub const KEY: &'static str = "Key";
pub const DOUBLE_QUOTA: &'static str = "\""; pub const DOUBLE_QUOTE: &'static str = "\"";
pub const SINGLE_QUOTA: &'static str = "'"; pub const SINGLE_QUOTE: &'static str = "'";
pub const EQUAL: &'static str = "=="; pub const EQUAL: &'static str = "==";
pub const GREATER_OR_EQUAL: &'static str = ">="; pub const GREATER_OR_EQUAL: &'static str = ">=";
pub const GREATER: &'static str = ">"; pub const GREATER: &'static str = ">";
@ -109,8 +109,8 @@ impl Token {
Token::OpenParenthesis(_) => OPEN_PARENTHESIS, Token::OpenParenthesis(_) => OPEN_PARENTHESIS,
Token::CloseParenthesis(_) => CLOSE_PARENTHESIS, Token::CloseParenthesis(_) => CLOSE_PARENTHESIS,
Token::Key(_, _) => KEY, Token::Key(_, _) => KEY,
Token::DoubleQuoted(_, _) => DOUBLE_QUOTA, Token::DoubleQuoted(_, _) => DOUBLE_QUOTE,
Token::SingleQuoted(_, _) => SINGLE_QUOTA, Token::SingleQuoted(_, _) => SINGLE_QUOTE,
Token::Equal(_) => EQUAL, Token::Equal(_) => EQUAL,
Token::GreaterOrEqual(_) => GREATER_OR_EQUAL, Token::GreaterOrEqual(_) => GREATER_OR_EQUAL,
Token::Greater(_) => GREATER, Token::Greater(_) => GREATER,

View File

@ -693,20 +693,22 @@ impl<'a, 'b> Selector<'a, 'b> {
debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current); debug!("next_from_current_with_num : {:?}, {:?}", &index, self.current);
} }
fn next_from_current_with_str(&mut self, key: &str) { fn next_from_current_with_str(&mut self, keys: &Vec<String>) {
fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, key: &str, visited: &mut HashSet<*const Value>) { fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, keys: &Vec<String>, visited: &mut HashSet<*const Value>) {
match v { match v {
Value::Object(map) => { Value::Object(map) => {
if let Some(v) = map.get(key) { for key in keys {
let ptr = v as *const Value; if let Some(v) = map.get(key) {
if !visited.contains(&ptr) { let ptr = v as *const Value;
visited.insert(ptr); if !visited.contains(&ptr) {
tmp.push(v) visited.insert(ptr);
tmp.push(v)
}
} }
} }
} }
Value::Array(vec) => for v in vec { 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 tmp = Vec::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
for c in current { for c in current {
_collect(c, &mut tmp, key, &mut visited); _collect(c, &mut tmp, keys, &mut visited);
} }
self.current = Some(tmp); 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) { 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)); self.next_from_current_with_num(to_f64(&n));
} }
ExprTerm::String(key) => { ExprTerm::String(key) => {
self.next_from_current_with_str(&key); self.next_from_current_with_str(&vec![key]);
} }
ExprTerm::Json(_, v) => { ExprTerm::Json(_, v) => {
if v.is_empty() { 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()) self.all_from_current_with_str(key.as_str())
} }
ParseToken::In => { 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) => { ParseToken::Number(v) => {
self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); self.terms.push(Some(ExprTerm::Number(Number::from_f64(*v).unwrap())));
} }

View File

@ -40,6 +40,10 @@ fn array() {
select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([ select_and_then_compare("$['school']['friends'][0].['name']", read_json("./benches/data_obj.json"), json!([
"Millicent Norman" "Millicent Norman"
])); ]));
select_and_then_compare(r#"$.["eyeColor", "name"]"#, read_json("./benches/data_obj.json"), json!([
"blue", "Leonor Herman"
]));
} }
#[test] #[test]