aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/parser.rs200
-rw-r--r--tests/invalid.rs2
-rw-r--r--tests/valid/hard_example.toml2
-rw-r--r--tests/valid/table-whitespace.toml2
-rw-r--r--tests/valid/table-with-pound.toml2
5 files changed, 114 insertions, 94 deletions
diff --git a/src/parser.rs b/src/parser.rs
index 8bb7bf1..df19ba4 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -158,80 +158,85 @@ impl<'a> Parser<'a> {
/// to determine the cause of the parse failure.
pub fn parse(&mut self) -> Option<TomlTable> {
let mut ret = BTreeMap::new();
- loop {
+ while self.peek(0).is_some() {
self.ws();
if self.newline() { continue }
- match self.peek(0) {
- Some((_, '#')) => { self.comment(); }
- Some((start, '[')) => {
- self.cur.next();
- let array = self.eat('[');
-
- // Parse the name of the section
- let mut section = String::new();
- for (pos, ch) in self.cur {
- if ch == ']' { break }
- if ch == '[' {
- self.errors.push(ParserError {
- lo: pos,
- hi: pos + 1,
- desc: format!("section names cannot contain \
- a `[` character"),
- });
- continue
- }
- section.push(ch);
- }
-
- if section.len() == 0 {
- self.errors.push(ParserError {
- lo: start,
- hi: start + if array {3} else {1},
- desc: format!("section name must not be empty"),
- });
- continue
- } else if array && !self.expect(']') {
- return None
+ if self.comment() { continue }
+ if self.eat('[') {
+ let array = self.eat('[');
+ let start = self.next_pos();
+
+ // Parse the name of the section
+ let mut keys = Vec::new();
+ loop {
+ self.ws();
+ match self.key_name() {
+ Some(s) => keys.push(s),
+ None => {}
}
-
- // Build the section table
- let mut table = BTreeMap::new();
- if !self.values(&mut table) { return None }
- if array {
- self.insert_array(&mut ret, section, Table(table), start)
- } else {
- self.insert_table(&mut ret, section, table, start)
+ self.ws();
+ if self.eat(']') {
+ if array && !self.expect(']') { return None }
+ break
}
+ if !self.expect('.') { return None }
}
- Some(_) => {
- if !self.values(&mut ret) { return None }
+ if keys.len() == 0 { return None }
+
+ // Build the section table
+ let mut table = BTreeMap::new();
+ if !self.values(&mut table) { return None }
+ if array {
+ self.insert_array(&mut ret, &keys[], Table(table), start)
+ } else {
+ self.insert_table(&mut ret, &keys[], table, start)
}
- None if self.errors.len() == 0 => return Some(ret),
- None => return None,
+ } else {
+ if !self.values(&mut ret) { return None }
}
}
+ if self.errors.len() > 0 {
+ None
+ } else {
+ Some(ret)
+ }
}
- 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,
+ // Parse a single key name starting at `start`
+ fn key_name(&mut self) -> Option<String> {
+ let start = self.next_pos();
+ let key = if self.eat('"') {
+ self.finish_string(start, false)
+ } else {
+ 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 => {}
}
- None => {}
}
+ Some(ret)
+ };
+ match key {
+ Some(ref name) if name.len() == 0 => {
+ self.errors.push(ParserError {
+ lo: start,
+ hi: start,
+ desc: format!("expected a key but found an empty string"),
+ });
+ None
+ }
+ Some(name) => Some(name),
+ None => None,
}
- Some(ret)
}
// Parses the values into the given TomlTable. Returns true in case of success
@@ -247,7 +252,7 @@ impl<'a> Parser<'a> {
None => break,
}
let key_lo = self.next_pos();
- let key = match self.key_name(key_lo) {
+ let key = match self.key_name() {
Some(s) => s,
None => return false
};
@@ -675,38 +680,25 @@ impl<'a> Parser<'a> {
}
}
- fn recurse<'b>(&mut self, mut cur: &'b mut TomlTable, orig_key: &'b str,
+ fn recurse<'b>(&mut self, mut cur: &'b mut TomlTable, keys: &'b [String],
key_lo: usize) -> Option<(&'b mut TomlTable, &'b str)> {
- if orig_key.starts_with(".") || orig_key.ends_with(".") ||
- orig_key.contains("..") {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + orig_key.len(),
- desc: format!("tables cannot have empty names"),
- });
- return None
- }
- let key = match orig_key.rfind('.') {
- Some(n) => orig_key.slice_to(n),
- None => return Some((cur, orig_key)),
- };
- for part in key.as_slice().split('.') {
- let part = part.to_string();
+ let key_hi = keys.iter().fold(0, |a, b| a + b.len());
+ for part in keys[..keys.len() - 1].iter() {
let tmp = cur;
- if tmp.contains_key(&part) {
- match *tmp.get_mut(&part).unwrap() {
+ if tmp.contains_key(part) {
+ match *tmp.get_mut(part).unwrap() {
Table(ref mut table) => {
cur = table;
continue
}
Array(ref mut array) => {
- match array.as_mut_slice().last_mut() {
+ match array.last_mut() {
Some(&mut Table(ref mut table)) => cur = table,
_ => {
self.errors.push(ParserError {
lo: key_lo,
- hi: key_lo + key.len(),
+ hi: key_hi,
desc: format!("array `{}` does not contain \
tables", part)
});
@@ -718,7 +710,7 @@ impl<'a> Parser<'a> {
_ => {
self.errors.push(ParserError {
lo: key_lo,
- hi: key_lo + key.len(),
+ hi: key_hi,
desc: format!("key `{}` was not previously a table",
part)
});
@@ -729,17 +721,17 @@ impl<'a> Parser<'a> {
// Initialize an empty table as part of this sub-key
tmp.insert(part.clone(), Table(BTreeMap::new()));
- match *tmp.get_mut(&part).unwrap() {
+ match *tmp.get_mut(part).unwrap() {
Table(ref mut inner) => cur = inner,
_ => unreachable!(),
}
}
- return Some((cur, orig_key.slice_from(key.len() + 1)))
+ Some((cur, &keys.last().unwrap()[]))
}
- fn insert_table(&mut self, into: &mut TomlTable, key: String, value: TomlTable,
- key_lo: usize) {
- let (into, key) = match self.recurse(into, key.as_slice(), key_lo) {
+ fn insert_table(&mut self, into: &mut TomlTable, keys: &[String],
+ value: TomlTable, key_lo: usize) {
+ let (into, key) = match self.recurse(into, keys, key_lo) {
Some(pair) => pair,
None => return,
};
@@ -780,9 +772,9 @@ impl<'a> Parser<'a> {
}
}
- fn insert_array(&mut self, into: &mut TomlTable, key: String, value: Value,
- key_lo: usize) {
- let (into, key) = match self.recurse(into, key.as_slice(), key_lo) {
+ fn insert_array(&mut self, into: &mut TomlTable,
+ keys: &[String], value: Value, key_lo: usize) {
+ let (into, key) = match self.recurse(into, keys, key_lo) {
Some(pair) => pair,
None => return,
};
@@ -1111,8 +1103,36 @@ trimmed in raw strings.
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("=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());
}
+
+ #[test]
+ fn bad_table_names() {
+ assert!(Parser::new("[]").parse().is_none());
+ assert!(Parser::new("[.]").parse().is_none());
+ assert!(Parser::new("[\"\".\"\"]").parse().is_none());
+ assert!(Parser::new("[a.]").parse().is_none());
+ assert!(Parser::new("[\"\"]").parse().is_none());
+ assert!(Parser::new("[!]").parse().is_none());
+ assert!(Parser::new("[\"\n\"]").parse().is_none());
+ assert!(Parser::new("[a.b]\n[a.\"b\"]").parse().is_none());
+ }
+
+ #[test]
+ fn table_names() {
+ let mut p = Parser::new("
+ [a.\"b\"]
+ [\"f f\"]
+ [\"f.f\"]
+ [\"\\\"\"]
+ ");
+ 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());
+ }
}
diff --git a/tests/invalid.rs b/tests/invalid.rs
index 582e25d..3df4d02 100644
--- a/tests/invalid.rs
+++ b/tests/invalid.rs
@@ -5,8 +5,8 @@ use toml::{Parser};
fn run(toml: &str) {
let mut p = Parser::new(toml);
let table = p.parse();
- assert!(p.errors.len() > 0);
assert!(table.is_none());
+ assert!(p.errors.len() > 0);
}
macro_rules! test( ($name:ident, $toml:expr) => (
diff --git a/tests/valid/hard_example.toml b/tests/valid/hard_example.toml
index 6c47b2c..38856c8 100644
--- a/tests/valid/hard_example.toml
+++ b/tests/valid/hard_example.toml
@@ -13,7 +13,7 @@ test_string = "You'll hate me after this - #" # " Annoying, isn't it?
harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
# Things will get harder
- [the.hard.bit#]
+ [the.hard."bit#"]
"what?" = "You don't think some user won't do that?"
multi_line_array = [
"]",
diff --git a/tests/valid/table-whitespace.toml b/tests/valid/table-whitespace.toml
index 798756c..daf881d 100644
--- a/tests/valid/table-whitespace.toml
+++ b/tests/valid/table-whitespace.toml
@@ -1 +1 @@
-[valid key]
+["valid key"]
diff --git a/tests/valid/table-with-pound.toml b/tests/valid/table-with-pound.toml
index e7b777e..33f2c4f 100644
--- a/tests/valid/table-with-pound.toml
+++ b/tests/valid/table-with-pound.toml
@@ -1,2 +1,2 @@
-[key#group]
+["key#group"]
answer = 42