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
This commit is contained in:
parent
bc825a5fb5
commit
0285af245a
126
src/parser.rs
126
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
|
// Parses the values into the given TomlTable. Returns true in case of success
|
||||||
// and false in case of error.
|
// and false in case of error.
|
||||||
fn values(&mut self, into: &mut TomlTable) -> bool {
|
fn values(&mut self, into: &mut TomlTable) -> bool {
|
||||||
loop {
|
loop {
|
||||||
self.ws();
|
self.ws();
|
||||||
if self.newline() { continue }
|
if self.newline() { continue }
|
||||||
match self.cur.clone().next() {
|
if self.comment() { continue }
|
||||||
Some((_, '#')) => { self.comment(); }
|
match self.peek(0) {
|
||||||
Some((_, '[')) => break,
|
Some((_, '[')) => break,
|
||||||
Some((start, _)) => {
|
Some(..) => {}
|
||||||
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');
|
|
||||||
}
|
|
||||||
None => break,
|
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
|
return true
|
||||||
}
|
}
|
||||||
|
@ -290,7 +295,6 @@ impl<'a> Parser<'a> {
|
||||||
fn string(&mut self, start: usize) -> Option<Value> {
|
fn string(&mut self, start: usize) -> Option<Value> {
|
||||||
if !self.expect('"') { return None }
|
if !self.expect('"') { return None }
|
||||||
let mut multiline = false;
|
let mut multiline = false;
|
||||||
let mut ret = String::new();
|
|
||||||
|
|
||||||
// detect multiline literals, but be careful about empty ""
|
// detect multiline literals, but be careful about empty ""
|
||||||
// strings
|
// strings
|
||||||
|
@ -300,10 +304,18 @@ impl<'a> Parser<'a> {
|
||||||
self.newline();
|
self.newline();
|
||||||
} else {
|
} else {
|
||||||
// empty
|
// 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 {
|
loop {
|
||||||
while multiline && self.newline() { ret.push('\n') }
|
while multiline && self.newline() { ret.push('\n') }
|
||||||
match self.cur.next() {
|
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 }
|
||||||
if !self.eat('"') { ret.push_str("\"\""); continue }
|
if !self.eat('"') { ret.push_str("\"\""); continue }
|
||||||
}
|
}
|
||||||
break
|
return Some(ret)
|
||||||
}
|
}
|
||||||
Some((pos, '\\')) => {
|
Some((pos, '\\')) => {
|
||||||
match escape(self, pos, multiline) {
|
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> {
|
fn escape(me: &mut Parser, pos: usize, multiline: bool) -> Option<char> {
|
||||||
if multiline && me.newline() {
|
if multiline && me.newline() {
|
||||||
while me.ws() || me.newline() { /* ... */ }
|
while me.ws() || me.newline() { /* ... */ }
|
||||||
|
@ -1065,4 +1075,40 @@ trimmed in raw strings.
|
||||||
t!("2e+10", 2e10);
|
t!("2e+10", 2e10);
|
||||||
t!("2e-10", 2e-10);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ test_string = "You'll hate me after this - #" # " Annoying, isn't it?
|
||||||
# Things will get harder
|
# Things will get harder
|
||||||
|
|
||||||
[the.hard.bit#]
|
[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 = [
|
multi_line_array = [
|
||||||
"]",
|
"]",
|
||||||
# ] Oh yes I did
|
# ] Oh yes I did
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
~!@#$^&*()_+-`1234567890[]\|/?><.,;:' = 1
|
"~!@#$^&*()_+-`1234567890[]\\|/?><.,;:'" = 1
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
key#name = 5
|
"key#name" = 5
|
||||||
|
|
Loading…
Reference in a new issue