parent
a8223490c2
commit
c777001598
140
src/parser.rs
140
src/parser.rs
|
@ -631,9 +631,13 @@ impl<'a> Parser<'a> {
|
||||||
};
|
};
|
||||||
let end = self.next_pos();
|
let end = self.next_pos();
|
||||||
let input = &self.input[start..end];
|
let input = &self.input[start..end];
|
||||||
let ret = if !is_float && !input.starts_with("+") &&
|
let ret = if decimal.is_none() &&
|
||||||
!input.starts_with("-") && self.eat('-') {
|
exponent.is_none() &&
|
||||||
self.datetime(start, end + 1)
|
!input.starts_with("+") &&
|
||||||
|
!input.starts_with("-") &&
|
||||||
|
start + 4 == end &&
|
||||||
|
self.eat('-') {
|
||||||
|
self.datetime(start)
|
||||||
} else {
|
} else {
|
||||||
let input = match (decimal, exponent) {
|
let input = match (decimal, exponent) {
|
||||||
(None, None) => prefix,
|
(None, None) => prefix,
|
||||||
|
@ -658,7 +662,9 @@ impl<'a> Parser<'a> {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn integer(&mut self, start: usize, allow_leading_zeros: bool,
|
fn integer(&mut self,
|
||||||
|
start: usize,
|
||||||
|
allow_leading_zeros: bool,
|
||||||
allow_sign: bool) -> Option<String> {
|
allow_sign: bool) -> Option<String> {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
if allow_sign {
|
if allow_sign {
|
||||||
|
@ -745,52 +751,81 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn datetime(&mut self, start: usize, end_so_far: usize) -> Option<Value> {
|
fn datetime(&mut self, start: usize) -> Option<Value> {
|
||||||
let mut date = format!("{}", &self.input[start..end_so_far]);
|
// Up to `start` already contains the year, and we've eaten the next
|
||||||
for _ in 0..15 {
|
// `-`, so we just resume parsing from there.
|
||||||
match self.cur.next() {
|
|
||||||
Some((_, ch)) => date.push(ch),
|
let mut valid = true;
|
||||||
None => {
|
|
||||||
self.errors.push(ParserError {
|
// month
|
||||||
lo: start,
|
valid = valid && digit(self.cur.next());
|
||||||
hi: end_so_far,
|
valid = valid && digit(self.cur.next());
|
||||||
desc: format!("malformed date literal"),
|
|
||||||
});
|
// day
|
||||||
return None
|
valid = valid && self.cur.next().map(|c| c.1) == Some('-');
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
|
||||||
|
valid = valid && self.cur.next().map(|c| c.1) == Some('T');
|
||||||
|
|
||||||
|
// hour
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
|
||||||
|
// minute
|
||||||
|
valid = valid && self.cur.next().map(|c| c.1) == Some(':');
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
|
||||||
|
// second
|
||||||
|
valid = valid && self.cur.next().map(|c| c.1) == Some(':');
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
|
||||||
|
// fractional seconds
|
||||||
|
if self.eat('.') {
|
||||||
|
valid = valid && digit(self.cur.next());
|
||||||
|
loop {
|
||||||
|
match self.cur.clone().next() {
|
||||||
|
Some((_, c)) if is_digit(c) => {
|
||||||
|
self.cur.next();
|
||||||
|
}
|
||||||
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut it = date.chars();
|
|
||||||
let mut valid = true;
|
// time zone
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
if !self.eat('Z') {
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
valid = valid && (self.eat('+') || self.eat('-'));
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
// hour
|
||||||
valid = valid && it.next().map(|c| c == '-').unwrap_or(false);
|
valid = valid && digit(self.cur.next());
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
valid = valid && digit(self.cur.next());
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(|c| c == '-').unwrap_or(false);
|
// minute
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
valid = valid && self.cur.next().map(|c| c.1) == Some(':');
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
valid = valid && digit(self.cur.next());
|
||||||
valid = valid && it.next().map(|c| c == 'T').unwrap_or(false);
|
valid = valid && digit(self.cur.next());
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
}
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(|c| c == ':').unwrap_or(false);
|
return if valid {
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
Some(Value::Datetime(self.input[start..self.next_pos()].to_string()))
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(|c| c == ':').unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(is_digit).unwrap_or(false);
|
|
||||||
valid = valid && it.next().map(|c| c == 'Z').unwrap_or(false);
|
|
||||||
if valid {
|
|
||||||
Some(Value::Datetime(date.clone()))
|
|
||||||
} else {
|
} else {
|
||||||
|
let next = self.next_pos();
|
||||||
self.errors.push(ParserError {
|
self.errors.push(ParserError {
|
||||||
lo: start,
|
lo: start,
|
||||||
hi: start + date.len(),
|
hi: start + next,
|
||||||
desc: format!("malformed date literal"),
|
desc: format!("malformed date literal"),
|
||||||
});
|
});
|
||||||
None
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
fn digit(val: Option<(usize, char)>) -> bool {
|
||||||
|
match val {
|
||||||
|
Some((_, c)) => is_digit(c),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1521,4 +1556,27 @@ trimmed in raw strings.
|
||||||
[a]
|
[a]
|
||||||
", "redefinition of table `a`");
|
", "redefinition of table `a`");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn datetimes() {
|
||||||
|
macro_rules! t {
|
||||||
|
($actual:expr) => ({
|
||||||
|
let f = format!("foo = {}", $actual);
|
||||||
|
let mut p = Parser::new(&f);
|
||||||
|
let table = Table(p.parse().unwrap());
|
||||||
|
assert_eq!(table.lookup("foo").and_then(|k| k.as_datetime()),
|
||||||
|
Some($actual));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t!("2016-09-09T09:09:09Z");
|
||||||
|
t!("2016-09-09T09:09:09.0Z");
|
||||||
|
t!("2016-09-09T09:09:09.0+10:00");
|
||||||
|
t!("2016-09-09T09:09:09.01234567890-02:00");
|
||||||
|
bad!("foo = 2016-09-09T09:09:09.Z", "malformed date literal");
|
||||||
|
bad!("foo = 2016-9-09T09:09:09Z", "malformed date literal");
|
||||||
|
bad!("foo = 2016-09-09T09:09:09+2:00", "malformed date literal");
|
||||||
|
bad!("foo = 2016-09-09T09:09:09-2:00", "malformed date literal");
|
||||||
|
bad!("foo = 2016-09-09T09:09:09Z-2:00", "expected");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue