aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parser.rs126
-rw-r--r--tests/valid/hard_example.toml6
-rw-r--r--tests/valid/key-special-chars.toml2
-rw-r--r--tests/valid/key-with-pound.toml2
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<String> {
+ 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<Value> {
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<String> {
+ 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<char> {
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