From 3517215eab1a2846b1059cf8182cb9d621a6623e Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Sun, 27 Mar 2016 20:46:19 +0200 Subject: Implement a more advanced algorithm for lookup The new algorithm allows the explicit usage of "" and '' to denote key names. This is useful for accessing tables or keys that are named in a non-conventional manner. --- src/lib.rs | 52 ++++++++++++++++++++++++++++++++-- src/parser.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 133 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 8afaa3d..9019327 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -181,12 +181,16 @@ impl Value { /// assert_eq!(no_bar.is_none(), true); /// ``` pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> { + let ref path = match Parser::new(path).lookup() { + Some(path) => path, + None => return None, + }; let mut cur_value = self; if path.len() == 0 { return Some(cur_value) } - for key in path.split('.') { + for key in path { match *cur_value { Value::Table(ref hm) => { match hm.get(key) { @@ -205,8 +209,8 @@ impl Value { }; Some(cur_value) - } + } /// Lookups for mutable value at specified path. /// /// Uses '.' as a path separator. @@ -278,6 +282,7 @@ impl FromStr for Value { #[cfg(test)] mod tests { use super::Value; + use parser::Parser; #[test] fn lookup_mut_change() { @@ -428,4 +433,47 @@ mod tests { let baz = foo.lookup("foo").unwrap(); assert_eq!(baz.as_str().unwrap(), "bar"); } + + #[test] + fn lookup_advanced() { + let value: Value = "[table]\n\"value\" = 0".parse().unwrap(); + let looked = value.lookup("table.\"value\"").unwrap(); + assert_eq!(*looked, Value::Integer(0)); + } + + #[test] + fn lookup_internal() { + let mut parser = Parser::new(r#"hello."world\t".a.0.'escaped'.value"#); + let result = vec![ + String::from("hello"), + String::from("world\t"), + String::from("a"), + String::from("0"), + String::from("escaped"), + String::from("value") + ]; + + assert_eq!(parser.lookup().unwrap(), result); + } + + #[test] + fn lookup_internal_void() { + let mut parser = Parser::new(""); + assert_eq!(parser.lookup().unwrap(), Vec::::new()); + } + + #[test] + fn lookup_internal_simple() { + let mut parser = Parser::new("value"); + assert_eq!(parser.lookup().unwrap(), vec![String::from("value")]); + } + + // This is due to key_name not parsing an empty "" correctly. Disabled for now. + #[test] + #[ignore] + fn lookup_internal_quoted_void() { + let mut parser = Parser::new("\"\""); + assert_eq!(parser.lookup().unwrap(), vec![String::from("")]); + } + } diff --git a/src/parser.rs b/src/parser.rs index 185f325..35b57fa 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -290,6 +290,46 @@ impl<'a> Parser<'a> { } } + // Parse an array index as a natural number + fn array_index(&mut self) -> Option { + let mut index = String::new(); + while let Some((_, ch)) = self.peek(0) { + match ch { + v @ '0' ... '9' => { + if !self.eat(v) { + return None + } + index.push(v); + } + _ => return Some(index), + } + } + if index.len() > 0 { + return Some(index); + } + None + } + + /// Parse a path into a vector of paths + pub fn lookup(&mut self) -> Option> { + if self.input.len() == 0 { + return Some(vec![]); + } + let mut keys = Vec::new(); + loop { + self.ws(); + if let Some(s) = self.key_name() { + keys.push(s); + } else if let Some(s) = self.array_index() { + keys.push(s); + } else { + return None + } + self.ws(); + if !self.expect('.') { return Some(keys) } + } + } + // Parse a single key name starting at `start` fn key_name(&mut self) -> Option { let start = self.next_pos(); @@ -969,6 +1009,42 @@ mod tests { }) } + #[test] + fn lookup_internal() { + let mut parser = Parser::new(r#"hello."world\t".a.0.'escaped'.value"#); + let result = vec![ + String::from("hello"), + String::from("world\t"), + String::from("a"), + String::from("0"), + String::from("escaped"), + String::from("value") + ]; + + assert_eq!(parser.lookup().unwrap(), result); + } + + #[test] + fn lookup_internal_void() { + let mut parser = Parser::new(""); + assert_eq!(parser.lookup().unwrap(), Vec::::new()); + } + + #[test] + fn lookup_internal_simple() { + let mut parser = Parser::new("value"); + assert_eq!(parser.lookup().unwrap(), vec![String::from("value")]); + } + + // This is due to key_name not parsing an empty "" correctly. Disabled for now. + #[test] + #[ignore] + fn lookup_internal_quoted_void() { + let mut parser = Parser::new("\"\""); + assert_eq!(parser.lookup().unwrap(), vec![String::from("")]); + } + + #[test] fn crlf() { let mut p = Parser::new("\ @@ -1246,10 +1322,10 @@ trimmed in raw strings. assert!(table.lookup("foo_3").is_some()); assert!(table.lookup("foo_-2--3--r23f--4-f2-4").is_some()); assert!(table.lookup("a").is_some()); - assert!(table.lookup("!").is_some()); - assert!(table.lookup("\"").is_some()); - assert!(table.lookup("character encoding").is_some()); - assert!(table.lookup("ʎǝʞ").is_some()); + assert!(table.lookup("\"!\"").is_some()); + assert!(table.lookup("\"\\\"\"").is_some()); + assert!(table.lookup("\"character encoding\"").is_some()); + assert!(table.lookup("'ʎǝʞ'").is_some()); } #[test] @@ -1293,9 +1369,9 @@ trimmed in raw strings. "); let table = Table(p.parse().unwrap()); assert!(table.lookup("a.b").is_some()); - assert!(table.lookup("f f").is_some()); - assert!(table.lookup("\"").is_some()); - assert!(table.lookup("\"\"").is_some()); + assert!(table.lookup("\"f f\"").is_some()); + assert!(table.lookup("\"\\\"\"").is_some()); + assert!(table.lookup("'\"\"'").is_some()); } #[test] -- cgit v1.2.3 From 0e3e0654ba7454c4a09043064a15037d5b90e122 Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Sun, 27 Mar 2016 20:48:23 +0200 Subject: Implement advanced key lookup for lookup_mut --- src/lib.rs | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 9019327..760cdde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -241,12 +241,17 @@ impl Value { /// assert_eq!(result.as_str().unwrap(), "foo"); /// ``` pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> { + let ref path = match Parser::new(path).lookup() { + Some(path) => path, + None => return None, + }; + let mut cur = self; if path.len() == 0 { return Some(cur) } - for key in path.split('.') { + for key in path { let tmp = cur; match *tmp { Value::Table(ref mut hm) => { @@ -282,7 +287,6 @@ impl FromStr for Value { #[cfg(test)] mod tests { use super::Value; - use parser::Parser; #[test] fn lookup_mut_change() { @@ -441,39 +445,4 @@ mod tests { assert_eq!(*looked, Value::Integer(0)); } - #[test] - fn lookup_internal() { - let mut parser = Parser::new(r#"hello."world\t".a.0.'escaped'.value"#); - let result = vec![ - String::from("hello"), - String::from("world\t"), - String::from("a"), - String::from("0"), - String::from("escaped"), - String::from("value") - ]; - - assert_eq!(parser.lookup().unwrap(), result); - } - - #[test] - fn lookup_internal_void() { - let mut parser = Parser::new(""); - assert_eq!(parser.lookup().unwrap(), Vec::::new()); - } - - #[test] - fn lookup_internal_simple() { - let mut parser = Parser::new("value"); - assert_eq!(parser.lookup().unwrap(), vec![String::from("value")]); - } - - // This is due to key_name not parsing an empty "" correctly. Disabled for now. - #[test] - #[ignore] - fn lookup_internal_quoted_void() { - let mut parser = Parser::new("\"\""); - assert_eq!(parser.lookup().unwrap(), vec![String::from("")]); - } - } -- cgit v1.2.3 From 770052a9a782efcc896b4dbf9452f73a1b5551ab Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Sun, 27 Mar 2016 20:50:25 +0200 Subject: Add a test for lookup_mut_advanced --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 760cdde..88d8072 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -445,4 +445,13 @@ mod tests { assert_eq!(*looked, Value::Integer(0)); } + #[test] + fn lookup_mut_advanced() { + let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + let looked = value.lookup_mut("table.\"value\".1").unwrap(); + assert_eq!(*looked, Value::Integer(1)); + } + + + } -- cgit v1.2.3 From 442f663c2d53e7ad469a3979c04907282a38ac12 Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Sun, 27 Mar 2016 20:55:49 +0200 Subject: Add advanced lookup for table names --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 88d8072..9af0c70 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -445,6 +445,13 @@ mod tests { assert_eq!(*looked, Value::Integer(0)); } + #[test] + fn lookup_advanced_table() { + let value: Value = r#"[table."name.other"] value = "my value""#.parse().unwrap(); + let looked = value.lookup(r#"table."name.other".value"#).unwrap(); + assert_eq!(*looked, Value::String(String::from("my value"))); + } + #[test] fn lookup_mut_advanced() { let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); -- cgit v1.2.3 From 568dd2ef4ba7db91d59988825f07e3e847e8c8d8 Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Sun, 27 Mar 2016 21:19:03 +0200 Subject: Fix the indentation to be consistent with the repository --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 9af0c70..83faec7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -445,12 +445,12 @@ mod tests { assert_eq!(*looked, Value::Integer(0)); } - #[test] - fn lookup_advanced_table() { + #[test] + fn lookup_advanced_table() { let value: Value = r#"[table."name.other"] value = "my value""#.parse().unwrap(); let looked = value.lookup(r#"table."name.other".value"#).unwrap(); assert_eq!(*looked, Value::String(String::from("my value"))); - } + } #[test] fn lookup_mut_advanced() { -- cgit v1.2.3 From 33c49d03578010e10d5cf881272292558dc043dd Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Mon, 28 Mar 2016 06:24:25 +0200 Subject: Leverage fn array_index to fn integer --- src/parser.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) (limited to 'src') diff --git a/src/parser.rs b/src/parser.rs index 35b57fa..9eb506a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -292,22 +292,7 @@ impl<'a> Parser<'a> { // Parse an array index as a natural number fn array_index(&mut self) -> Option { - let mut index = String::new(); - while let Some((_, ch)) = self.peek(0) { - match ch { - v @ '0' ... '9' => { - if !self.eat(v) { - return None - } - index.push(v); - } - _ => return Some(index), - } - } - if index.len() > 0 { - return Some(index); - } - None + self.integer(0, false, false) } /// Parse a path into a vector of paths -- cgit v1.2.3 From 644dc88c0410dffacb7def8cd7195edb9b382afc Mon Sep 17 00:00:00 2001 From: Bourgond Aries Date: Mon, 28 Mar 2016 06:35:39 +0200 Subject: Add a bunch of negative and positive tests --- src/lib.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 83faec7..ec8106a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -459,6 +459,34 @@ mod tests { assert_eq!(*looked, Value::Integer(1)); } + #[test] + fn single_dot() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup(".")); + } + #[test] + fn array_dot() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("0.")); + } + + #[test] + fn dot_inside() { + let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("table.\"value.0\"")); + } + + #[test] + fn table_with_quotes() { + let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(None, value.lookup("\"table.element\".\"value\".0")); + } + + #[test] + fn table_with_quotes_2() { + let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap(); + assert_eq!(Value::Integer(0), *value.lookup("table.\"element\".\"value\".0").unwrap()); + } } -- cgit v1.2.3