From 0285af245aaf649c0528575ff7bd03838b31c743 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 15 Jan 2015 17:27:53 -0800 Subject: [PATCH] Implement new rules for bare key names * Key names are restricted to [a-zA-Z0-9_-] * Equal signs must be on the same line (as must the next value) as the key * Quoted keys are allowed and are parsed as basic strings. Closes #48 --- src/parser.rs | 126 ++++++++++++++++++++--------- tests/valid/hard_example.toml | 6 +- tests/valid/key-special-chars.toml | 2 +- tests/valid/key-with-pound.toml | 2 +- 4 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index c0c60da..9e5bc15 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -212,50 +212,55 @@ impl<'a> Parser<'a> { } } + fn key_name(&mut self, start: usize) -> Option { + if self.eat('"') { + return self.finish_string(start, false); + } + let mut ret = String::new(); + loop { + match self.cur.clone().next() { + Some((_, ch)) => { + match ch { + 'a' ... 'z' | + 'A' ... 'Z' | + '0' ... '9' | + '_' | '-' => { self.cur.next(); ret.push(ch) } + _ => break, + } + } + None => {} + } + } + Some(ret) + } + // Parses the values into the given TomlTable. Returns true in case of success // and false in case of error. fn values(&mut self, into: &mut TomlTable) -> bool { loop { self.ws(); if self.newline() { continue } - match self.cur.clone().next() { - Some((_, '#')) => { self.comment(); } + if self.comment() { continue } + match self.peek(0) { Some((_, '[')) => break, - Some((start, _)) => { - let mut key = String::new(); - let mut found_eq = false; - for (pos, ch) in self.cur { - match ch { - ' ' | '\t' => break, - '=' => { found_eq = true; break } - '\n' => { - self.errors.push(ParserError { - lo: start, - hi: pos + 1, - desc: format!("keys cannot be defined \ - across lines"), - }) - } - c => key.push(c), - } - } - if !found_eq { - self.ws(); - if !self.expect('=') { return false } - } - - let value = match self.value() { - Some(value) => value, - None => return false, - }; - self.insert(into, key, value, start); - self.ws(); - self.comment(); - self.eat('\r'); - self.eat('\n'); - } + Some(..) => {} None => break, } + let key_lo = self.next_pos(); + let key = match self.key_name(key_lo) { + Some(s) => s, + None => return false + }; + self.ws(); + if !self.expect('=') { return false } + let value = match self.value() { + Some(value) => value, + None => return false, + }; + self.insert(into, key, value, key_lo); + self.ws(); + self.comment(); + self.newline(); } return true } @@ -290,7 +295,6 @@ impl<'a> Parser<'a> { fn string(&mut self, start: usize) -> Option { if !self.expect('"') { return None } let mut multiline = false; - let mut ret = String::new(); // detect multiline literals, but be careful about empty "" // strings @@ -300,10 +304,18 @@ impl<'a> Parser<'a> { self.newline(); } else { // empty - return Some(Value::String(ret)) + return Some(Value::String(String::new())) } } + self.finish_string(start, multiline).map(Value::String) + } + + // Finish parsing a basic string after the opening quote has been seen + fn finish_string(&mut self, + start: usize, + multiline: bool) -> Option { + let mut ret = String::new(); loop { while multiline && self.newline() { ret.push('\n') } match self.cur.next() { @@ -312,7 +324,7 @@ impl<'a> Parser<'a> { if !self.eat('"') { ret.push_str("\""); continue } if !self.eat('"') { ret.push_str("\"\""); continue } } - break + return Some(ret) } Some((pos, '\\')) => { match escape(self, pos, multiline) { @@ -340,8 +352,6 @@ impl<'a> Parser<'a> { } } - return Some(Value::String(ret)); - fn escape(me: &mut Parser, pos: usize, multiline: bool) -> Option { if multiline && me.newline() { while me.ws() || me.newline() { /* ... */ } @@ -1065,4 +1075,40 @@ trimmed in raw strings. t!("2e+10", 2e10); t!("2e-10", 2e-10); } + + #[test] + fn bare_key_names() { + let mut p = Parser::new(" + foo = 3 + foo_3 = 3 + foo_-2--3--r23f--4-f2-4 = 3 + _ = 3 + - = 3 + 8 = 8 + \"a\" = 3 + \"!\" = 3 + \"a^b\" = 3 + \"\\\"\" = 3 + "); + let table = Table(p.parse().unwrap()); + assert!(table.lookup("foo").is_some()); + assert!(table.lookup("-").is_some()); + assert!(table.lookup("_").is_some()); + assert!(table.lookup("8").is_some()); + 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()); + } + + #[test] + fn bad_keys() { + assert!(Parser::new("key\n=3").parse().is_none()); + assert!(Parser::new("key=\n3").parse().is_none()); + assert!(Parser::new("key|=3").parse().is_none()); + assert!(Parser::new("\"\"|=3").parse().is_none()); + assert!(Parser::new("\"\n\"|=3").parse().is_none()); + assert!(Parser::new("\"\r\"|=3").parse().is_none()); + } } diff --git a/tests/valid/hard_example.toml b/tests/valid/hard_example.toml index 1d5d8e9..6c47b2c 100644 --- a/tests/valid/hard_example.toml +++ b/tests/valid/hard_example.toml @@ -12,9 +12,9 @@ test_string = "You'll hate me after this - #" # " Annoying, isn't it? another_test_string = " Same thing, but with a string #" harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too" # Things will get harder - + [the.hard.bit#] - what? = "You don't think some user won't do that?" + "what?" = "You don't think some user won't do that?" multi_line_array = [ "]", # ] Oh yes I did @@ -30,4 +30,4 @@ test_string = "You'll hate me after this - #" # " Annoying, isn't it? # "or here, # and here" # ] End of array comment, forgot the # -#number = 3.14 pi <--again forgot the # +#number = 3.14 pi <--again forgot the # diff --git a/tests/valid/key-special-chars.toml b/tests/valid/key-special-chars.toml index 8b3fc51..dc43625 100644 --- a/tests/valid/key-special-chars.toml +++ b/tests/valid/key-special-chars.toml @@ -1 +1 @@ -~!@#$^&*()_+-`1234567890[]\|/?><.,;:' = 1 +"~!@#$^&*()_+-`1234567890[]\\|/?><.,;:'" = 1 diff --git a/tests/valid/key-with-pound.toml b/tests/valid/key-with-pound.toml index 1c54f53..65b766f 100644 --- a/tests/valid/key-with-pound.toml +++ b/tests/valid/key-with-pound.toml @@ -1 +1 @@ -key#name = 5 +"key#name" = 5