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

View File

@ -33,6 +33,7 @@ pub enum ParseToken {
All,
Key(String),
Keys(Vec<String>),
// []
Array,
// 메타토큰
@ -232,12 +233,39 @@ impl Parser {
}
}
fn array_quota_value(tokenizer: &mut TokenReader) -> ParseResult<Node> {
debug!("#array_quota_value");
fn array_keys(tokenizer: &mut TokenReader, first_key: String) -> ParseResult<Node> {
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() {
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(_)

View File

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

View File

@ -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<String>) {
fn _collect<'a>(v: &'a Value, tmp: &mut Vec<&'a Value>, keys: &Vec<String>, 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())));
}

View File

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