mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Parse actual dates and make chrono datetimes
This commit is contained in:
parent
98dfcc7b2d
commit
05f87a3a74
5 changed files with 204 additions and 16 deletions
|
@ -15,6 +15,7 @@ ircbot = ["irc", "glob"]
|
|||
|
||||
[dependencies]
|
||||
rust-gmp = "0.3.2"
|
||||
chrono = "0.2.25"
|
||||
rustyline = { version = "0.2.3", optional = true }
|
||||
irc = { version = "0.11.3", optional = true }
|
||||
glob = { version = "0.2.11", optional = true }
|
||||
|
|
188
src/eval.rs
188
src/eval.rs
|
@ -20,6 +20,7 @@ pub struct Context {
|
|||
units: HashMap<String, Value>,
|
||||
aliases: HashMap<Unit, String>,
|
||||
prefixes: Vec<(String, Value)>,
|
||||
datepatterns: Vec<Vec<::unit_defs::DatePattern>>,
|
||||
pub short_output: bool,
|
||||
}
|
||||
|
||||
|
@ -118,6 +119,163 @@ fn to_string(rational: &Mpq) -> (bool, String) {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_date<I>(out: &mut ::chrono::format::parsed::Parsed,
|
||||
date: &mut ::std::iter::Peekable<I>,
|
||||
pat: &[::unit_defs::DatePattern]) -> Result<(), String>
|
||||
where I: Iterator<Item=::unit_defs::Token>+Clone {
|
||||
use unit_defs::{DatePattern, Token};
|
||||
use chrono::Weekday;
|
||||
|
||||
let tok = date.peek().cloned();
|
||||
|
||||
macro_rules! numeric_match {
|
||||
($name:expr, $digits:expr, $field:ident) => {
|
||||
match tok {
|
||||
Some(Token::Number(ref s, None, None)) if $digits == 0 || s.len() == $digits => {
|
||||
let value = i32::from_str_radix(&**s, 10).unwrap();
|
||||
out.$field = Some(value as _);
|
||||
Ok(())
|
||||
},
|
||||
x => Err(format!("Expected {}-digit {}, got {:?}", $digits, $name, x))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let res = match pat.first() {
|
||||
None if tok.is_some() => Err(format!("Expected EOF, got {:?}", tok.unwrap())),
|
||||
None => return Ok(()),
|
||||
Some(&DatePattern::Literal(ref l)) => match tok {
|
||||
Some(Token::Ident(ref s)) if s == l => Ok(()),
|
||||
x => Err(format!("Expected `{}`, got {:?}", l, x)),
|
||||
},
|
||||
Some(&DatePattern::Match(ref what)) => match &**what {
|
||||
"fullyear" => numeric_match!("fullyear", 4, year),
|
||||
"shortyear" => numeric_match!("shortyear", 2, year_mod_100),
|
||||
"century" => numeric_match!("century", 2, year_div_100),
|
||||
"monthnum" => numeric_match!("monthnum", 2, month),
|
||||
"day" => numeric_match!("day", 2, day),
|
||||
"hour12" => numeric_match!("hour12", 2, hour_mod_12),
|
||||
"min" => numeric_match!("min", 2, minute),
|
||||
"ordinal" => numeric_match!("ordinal", 3, ordinal),
|
||||
"isoyear" => numeric_match!("isoyear", 4, isoyear),
|
||||
"isoweek" => numeric_match!("isoweek", 2, isoweek),
|
||||
"unix" => numeric_match!("unix", 0, timestamp),
|
||||
"hour24" => match tok {
|
||||
Some(Token::Number(ref s, None, None)) if s.len() == 2 => {
|
||||
let value = u32::from_str_radix(&**s, 10).unwrap();
|
||||
out.hour_div_12 = Some(value / 12);
|
||||
out.hour_mod_12 = Some(value % 12);
|
||||
Ok(())
|
||||
},
|
||||
x => Err(format!("Expected 2-digit hour24, got {:?}", x))
|
||||
},
|
||||
"sec" => match tok {
|
||||
Some(Token::Number(ref s, None, None)) if s.len() == 2 => {
|
||||
let value = u32::from_str_radix(&**s, 10).unwrap();
|
||||
out.second = Some(value);
|
||||
Ok(())
|
||||
},
|
||||
Some(Token::Number(ref s, Some(ref f), None)) if s.len() == 2 => {
|
||||
let secs = u32::from_str_radix(&**s, 10).unwrap();
|
||||
let nsecs = u32::from_str_radix(&**f, 10).unwrap() * 10u32.pow(9 - f.len() as u32);
|
||||
out.second = Some(secs);
|
||||
out.nanosecond = Some(nsecs);
|
||||
Ok(())
|
||||
},
|
||||
x => Err(format!("Expected 2-digit sec, got {:?}", x))
|
||||
},
|
||||
"offset" => {
|
||||
macro_rules! take {
|
||||
($($pat: pat)|+) => {
|
||||
match date.peek().cloned() {
|
||||
$(Some($pat))|+ => date.next().unwrap(),
|
||||
x => return Err(format!("Expected {}, got {:?}", stringify!($($pat)|+), x))
|
||||
}
|
||||
};
|
||||
($pat:pat, $var:ident) => {
|
||||
match date.peek().cloned() {
|
||||
Some($pat) => {date.next(); $var},
|
||||
x => return Err(format!("Expected {}, got {:?}", stringify!($pat), x))
|
||||
}
|
||||
}
|
||||
}
|
||||
let s = match take!(Token::Plus | Token::Minus) {
|
||||
Token::Plus => 1, Token::Minus => -1, _ => panic!()
|
||||
};
|
||||
let h = take!(Token::Number(s, None, None), s);
|
||||
let h = i32::from_str_radix(&*h, 10).unwrap();
|
||||
take!(Token::Colon);
|
||||
let m = take!(Token::Number(s, None, None), s);
|
||||
let m = i32::from_str_radix(&*m, 10).unwrap();
|
||||
out.offset = Some(s * (h*3600 + m*60));
|
||||
Ok(())
|
||||
},
|
||||
"monthname" => match tok {
|
||||
Some(Token::Ident(ref s)) => {
|
||||
let res = match &*s.to_lowercase() {
|
||||
"jan" | "january" => 1,
|
||||
"feb" | "february" => 2,
|
||||
"mar" | "march" => 3,
|
||||
"apr" | "april" => 4,
|
||||
"may" => 5,
|
||||
"jun" | "june" => 6,
|
||||
"jul" | "july" => 7,
|
||||
"aug" | "august" => 8,
|
||||
"sep" | "september" => 9,
|
||||
"oct" | "october" => 10,
|
||||
"nov" | "november" => 11,
|
||||
"dec" | "december" => 12,
|
||||
x => return Err(format!("Unknown month name: {}", x))
|
||||
};
|
||||
out.month = Some(res);
|
||||
Ok(())
|
||||
},
|
||||
x => Err(format!("Expected month name, got {:?}", x))
|
||||
},
|
||||
"weekday" => match tok {
|
||||
Some(Token::Ident(ref s)) => {
|
||||
let res = match &*s.to_lowercase() {
|
||||
"mon" | "monday" => Weekday::Mon,
|
||||
"tue" | "tuesday" => Weekday::Tue,
|
||||
"wed" | "wednesday" => Weekday::Wed,
|
||||
"thu" | "thursday" => Weekday::Thu,
|
||||
"fri" | "friday" => Weekday::Fri,
|
||||
"sat" | "saturday" => Weekday::Sat,
|
||||
"sun" | "sunday" => Weekday::Sun,
|
||||
x => return Err(format!("Unknown weekday: {}", x))
|
||||
};
|
||||
out.weekday = Some(res);
|
||||
Ok(())
|
||||
},
|
||||
x => Err(format!("Expected weekday, got {:?}", x))
|
||||
},
|
||||
x => Err(format!("Unknown match pattern `{}`", x))
|
||||
},
|
||||
Some(&DatePattern::Optional(ref pats)) => {
|
||||
let mut iter = date.clone();
|
||||
match parse_date(out, &mut iter, &pats[..]) {
|
||||
Ok(()) => *date = iter,
|
||||
Err(_) => ()
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
Some(&DatePattern::Dash) => match tok {
|
||||
Some(Token::Minus) => Ok(()),
|
||||
x => Err(format!("Expected `-`, got {:?}", x))
|
||||
},
|
||||
Some(&DatePattern::Colon) => match tok {
|
||||
Some(Token::Colon) => Ok(()),
|
||||
x => Err(format!("Expected `:`, got {:?}", x))
|
||||
},
|
||||
Some(&DatePattern::Error(ref err)) => Err(err.clone())
|
||||
};
|
||||
date.next();
|
||||
match res {
|
||||
Ok(()) => parse_date(out, date, &pat[1..]),
|
||||
Err(e) => Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
fn btree_merge<K: ::std::cmp::Ord+Clone, V:Clone, F:Fn(&V, &V) -> Option<V>>(
|
||||
left: &BTreeMap<K, V>, right: &BTreeMap<K, V>, merge_func: F
|
||||
) -> BTreeMap<K, V> {
|
||||
|
@ -408,7 +566,32 @@ impl Context {
|
|||
let num = &num * &exp;
|
||||
Ok(Value::new(num))
|
||||
},
|
||||
Expr::Date(_) => unimplemented!(),
|
||||
Expr::Date(ref date) => {
|
||||
use chrono::format::Parsed;
|
||||
use chrono::{DateTime, UTC, FixedOffset};
|
||||
|
||||
for pat in &self.datepatterns {
|
||||
let attempt = || {
|
||||
let mut parsed = Parsed::new();
|
||||
try!(parse_date(&mut parsed, &mut date.iter().cloned().peekable(), &pat[..]));
|
||||
let offset = parsed.to_fixed_offset().unwrap_or(FixedOffset::east(0));
|
||||
let time = parsed.to_naive_time();
|
||||
let date = parsed.to_naive_date();
|
||||
match (time, date) {
|
||||
(Ok(time), Ok(date)) =>
|
||||
Ok(DateTime::<FixedOffset>::from_utc(date.and_time(time), offset)),
|
||||
(Ok(time), Err(_)) =>
|
||||
Ok(UTC::now().with_timezone(&offset).date().and_time(time).unwrap()),
|
||||
_ => Err(format!("Failed to construct a useful datetime"))
|
||||
}
|
||||
};
|
||||
match attempt() {
|
||||
Ok(_datetime) => unimplemented!(),
|
||||
Err(_) => ()
|
||||
}
|
||||
}
|
||||
unimplemented!()
|
||||
},
|
||||
Expr::Neg(ref expr) => self.eval(&**expr).and_then(|mut v| {
|
||||
v.0 = -v.0;
|
||||
Ok(v)
|
||||
|
@ -648,6 +831,7 @@ impl Context {
|
|||
units: HashMap::new(),
|
||||
aliases: HashMap::new(),
|
||||
prefixes: Vec::new(),
|
||||
datepatterns: Vec::new(),
|
||||
short_output: false,
|
||||
};
|
||||
|
||||
|
@ -681,7 +865,7 @@ impl Context {
|
|||
},
|
||||
Err(e) => println!("Prefix {} is malformed: {}", name, e)
|
||||
},
|
||||
Def::DatePattern(_) => unimplemented!(),
|
||||
Def::DatePattern(ref pat) => ctx.datepatterns.push(pat.clone()),
|
||||
Def::Error(ref err) => println!("Def {}: {}", name, err),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ println!("{}", one_line(&mut ctx, "kWh / year -> W").unwrap());
|
|||
*/
|
||||
|
||||
extern crate gmp;
|
||||
extern crate chrono;
|
||||
|
||||
pub mod unit_defs;
|
||||
pub mod eval;
|
||||
|
|
|
@ -8,7 +8,6 @@ pub enum Token {
|
|||
Comment(usize),
|
||||
Ident(String),
|
||||
Number(String, Option<String>, Option<String>),
|
||||
Date(String),
|
||||
Quote(String),
|
||||
Slash,
|
||||
Colon,
|
||||
|
@ -29,6 +28,7 @@ pub enum Token {
|
|||
Minus,
|
||||
Asterisk,
|
||||
DashArrow,
|
||||
Hash,
|
||||
ImaginaryUnit,
|
||||
Error(String),
|
||||
}
|
||||
|
@ -233,17 +233,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||
}
|
||||
Token::Quote(buf)
|
||||
},
|
||||
'#' => {
|
||||
let mut buf = String::new();
|
||||
loop {
|
||||
match self.0.next() {
|
||||
None | Some('\n') => return Some(Token::Error(format!("Unexpected newline or EOF"))),
|
||||
Some('#') => break,
|
||||
Some(c) => buf.push(c),
|
||||
}
|
||||
}
|
||||
Token::Date(buf)
|
||||
},
|
||||
'#' => Token::Hash,
|
||||
x => {
|
||||
let mut buf = String::new();
|
||||
buf.push(x);
|
||||
|
@ -267,7 +257,7 @@ pub type Iter<'a> = Peekable<TokenIterator<'a>>;
|
|||
pub enum Expr {
|
||||
Unit(String),
|
||||
Const(String, Option<String>, Option<String>),
|
||||
Date(String),
|
||||
Date(Vec<Token>),
|
||||
Frac(Box<Expr>, Box<Expr>),
|
||||
Mul(Vec<Expr>),
|
||||
Pow(Box<Expr>, Box<Expr>),
|
||||
|
@ -320,7 +310,16 @@ fn parse_term(mut iter: &mut Iter) -> Expr {
|
|||
x => Expr::Error(format!("Expected ), got {:?}", x))
|
||||
}
|
||||
},
|
||||
Token::Date(date) => Expr::Date(date),
|
||||
Token::Hash => {
|
||||
let mut out = vec![];
|
||||
loop {
|
||||
match iter.next().unwrap() {
|
||||
Token::Hash => break,
|
||||
x => out.push(x),
|
||||
}
|
||||
}
|
||||
Expr::Date(out)
|
||||
},
|
||||
x => Expr::Error(format!("Expected term, got {:?}", x))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4703,3 +4703,6 @@ HMS[time] :=
|
|||
}
|
||||
|
||||
|
||||
datepattern fullyear-monthnum-day'T'hour24:min:sec
|
||||
datepattern fullyear-monthnum-day
|
||||
datepattern hour24:min:sec offset
|
||||
|
|
Loading…
Reference in a new issue