Implement gnu units parser

This commit is contained in:
Tiffany Bennett 2016-08-20 17:00:26 -04:00
parent 8db988c589
commit 24f510f1f4
2 changed files with 73 additions and 207 deletions

View file

@ -156,14 +156,15 @@ impl<'a> Iterator for TokenIterator<'a> {
pub type Iter<'a> = Peekable<TokenIterator<'a>>;
/*fn parse_term(mut iter: &mut Iter) -> Expr {
fn parse_term(mut iter: &mut Iter) -> Expr {
match iter.next().unwrap() {
Token::Ident(name) => Expr::Unit(name),
Token::Number(num, frac, exp) => Expr::Const(num, frac, exp),
Token::Plus => Expr::Plus(Box::new(parse_term(iter))),
Token::Minus => Expr::Neg(Box::new(parse_term(iter))),
// NYI: Imaginary numbers
Token::ImaginaryUnit => Expr::Const("0".to_owned(), None, None),
Token::Dash => Expr::Neg(Box::new(parse_term(iter))),
Token::Slash => Expr::Frac(
Box::new(Expr::Const("1".to_owned(), None, None)),
Box::new(parse_term(iter))),
Token::LPar => {
let res = parse_expr(iter);
match iter.next().unwrap() {
@ -171,19 +172,6 @@ pub type Iter<'a> = Peekable<TokenIterator<'a>>;
x => Expr::Error(format!("Expected ), got {:?}", x))
}
},
Token::Hash => {
let mut out = vec![];
loop {
match iter.next().unwrap() {
Token::Hash => break,
Token::Eof | Token::Comment(_) | Token::Newline =>
return Expr::Error(format!("Unterminated date literal")),
x => out.push(x),
}
}
unimplemented!()
//Expr::Date(out)
},
x => Expr::Error(format!("Expected term, got {:?}", x))
}
}
@ -191,11 +179,16 @@ pub type Iter<'a> = Peekable<TokenIterator<'a>>;
fn parse_pow(mut iter: &mut Iter) -> Expr {
let left = parse_term(iter);
match *iter.peek().unwrap() {
Token::Carot => {
Token::Caret => {
iter.next();
let right = parse_pow(iter);
Expr::Pow(Box::new(left), Box::new(right))
},
Token::Pipe => {
iter.next();
let right = parse_pow(iter);
Expr::Frac(Box::new(left), Box::new(right))
},
_ => left
}
}
@ -203,9 +196,7 @@ fn parse_pow(mut iter: &mut Iter) -> Expr {
fn parse_mul(mut iter: &mut Iter) -> Expr {
let mut terms = vec![parse_pow(iter)];
loop { match iter.peek().cloned().unwrap() {
Token::DegC | Token::DegF | Token::DegRe | Token::DegRo | Token::DegDe | Token::DegN |
Token::Comma | Token::Equals | Token::Plus | Token::Minus | Token::DashArrow |
Token::TriplePipe | Token::RPar | Token::Newline | Token::Comment(_) | Token::Eof => break,
Token::Plus | Token::Dash | Token::RPar | Token::Newline | Token::Eof => break,
Token::Slash => {
iter.next();
let right = parse_pow(iter);
@ -228,146 +219,32 @@ fn parse_mul(mut iter: &mut Iter) -> Expr {
}
}
fn parse_suffix(mut iter: &mut Iter) -> Expr {
let left = parse_mul(iter);
match iter.peek().cloned().unwrap() {
Token::DegC => {
iter.next();
Expr::Suffix(SuffixOp::Celsius, Box::new(left))
},
Token::DegF => {
iter.next();
Expr::Suffix(SuffixOp::Fahrenheit, Box::new(left))
},
Token::DegRe => {
iter.next();
Expr::Suffix(SuffixOp::Reaumur, Box::new(left))
},
Token::DegRo => {
iter.next();
Expr::Suffix(SuffixOp::Romer, Box::new(left))
},
Token::DegDe => {
iter.next();
Expr::Suffix(SuffixOp::Delisle, Box::new(left))
},
Token::DegN => {
iter.next();
Expr::Suffix(SuffixOp::Newton, Box::new(left))
},
_ => left
}
}
fn parse_add(mut iter: &mut Iter) -> Expr {
let left = parse_suffix(iter);
let left = parse_mul(iter);
match *iter.peek().unwrap() {
Token::Plus => {
iter.next();
let right = parse_suffix(iter);
let right = parse_add(iter);
Expr::Add(Box::new(left), Box::new(right))
},
Token::Minus => {
Token::Dash => {
iter.next();
let right = parse_suffix(iter);
let right = parse_add(iter);
Expr::Sub(Box::new(left), Box::new(right))
},
_ => left
}
}
fn parse_eq(mut iter: &mut Iter) -> Expr {
let left = parse_add(iter);
match iter.peek().cloned().unwrap() {
Token::Equals => {
iter.next();
let right = parse_add(iter);
Expr::Equals(Box::new(left), Box::new(right))
},
_ => left
}
}
pub fn parse_expr(mut iter: &mut Iter) -> Expr {
match iter.peek().cloned() {
Some(Token::Ident(ref s)) if s == "factorize" => {
iter.next();
return Expr::Factorize(Box::new(parse_eq(iter)))
},
_ => ()
}
let left = parse_eq(iter);
match iter.peek().cloned().unwrap() {
Token::DashArrow => {
iter.next();
let right = match iter.peek().cloned().unwrap() {
Token::DegC => Expr::DegC,
Token::DegF => Expr::DegF,
Token::DegRe => Expr::DegRe,
Token::DegRo => Expr::DegRo,
Token::DegDe => Expr::DegDe,
Token::DegN => Expr::DegN,
_ => parse_eq(iter)
};
Expr::Convert(Box::new(left), Box::new(right))
},
_ => left
}
parse_add(iter)
}
fn parse_unknown(mut iter: &mut Iter) {
loop {
match iter.peek().cloned().unwrap() {
Token::Newline | Token::Comment(_) | Token::Eof => break,
_ => iter.next()
};
}
}
fn parse_alias(mut iter: &mut Iter) -> Option<(Expr, String)> {
match *iter.peek().unwrap() {
Token::Newline | Token::Comment(_) | Token::Eof => return None,
_ => ()
}
let expr = parse_expr(iter);
match iter.next().unwrap() {
Token::TriplePipe => (),
_ => return None
};
let name = match iter.next().unwrap() {
Token::Ident(name) => name,
_ => return None
};
match iter.peek().cloned().unwrap() {
Token::Newline | Token::Comment(_) | Token::Eof => (),
_ => return None
};
Some((expr, name))
}
fn parse_datepattern(mut iter: &mut Iter) -> Vec<DatePattern> {
let mut out = vec![];
loop {
match iter.peek().cloned().unwrap() {
Token::Newline | Token::Comment(_) | Token::Eof | Token::RBrack => break,
Token::Ident(name) => out.push(DatePattern::Match(name)),
Token::Quote(name) => out.push(DatePattern::Literal(name)),
Token::Minus => out.push(DatePattern::Dash),
Token::Colon => out.push(DatePattern::Colon),
Token::LBrack => {
iter.next();
let res = DatePattern::Optional(parse_datepattern(iter));
let res = match iter.next().unwrap() {
Token::RBrack => res,
x => DatePattern::Error(format!("Expected ], got {:?}", x))
};
out.push(res)
},
x => out.push(DatePattern::Error(format!("Unexpected token {:?} in date pattern", x)))
}
iter.next();
}
out
fn is_uppercase(name: &str) -> bool {
name.find(|c| match c {
'A'...'Z' | '_' => false,
_ => true
}).is_none()
}
pub fn parse(mut iter: &mut Iter) -> Defs {
@ -375,64 +252,51 @@ pub fn parse(mut iter: &mut Iter) -> Defs {
let mut aliases = vec![];
let mut line = 1;
loop {
let mut copy = iter.clone();
if let Some(a) = parse_alias(&mut copy) {
aliases.push(a);
*iter = copy;
continue
}
match iter.next().unwrap() {
Token::Newline => line += 1,
Token::Comment(lines) => line += lines,
Token::Eof => break,
Token::Ident(ref name) if name == "datepattern" =>
map.push((name.clone(), Rc::new(Def::DatePattern(parse_datepattern(iter))))),
Token::Ident(name) => {
let def = match iter.next().unwrap() {
Token::ColonDash => {
let expr = parse_expr(iter);
Def::Prefix(expr)
},
Token::DColonDash => {
let expr = parse_expr(iter);
Def::SPrefix(expr)
},
Token::EqBangEq => match iter.next().unwrap() {
Token::Ident(val) => Def::Dimension(val),
_ => Def::Error(format!("Line {}: Malformed dimensionless unit", line))
},
Token::ColonEq => Def::Unit(parse_expr(iter)),
Token::LBrack => {
// NYI
let mut n = 0;
let mut first = true;
loop {
match iter.peek().cloned().unwrap() {
Token::LBrace => n += 1,
Token::RBrace if n == 1 => {
iter.next();
break
},
Token::RBrace => n -= 1,
Token::Newline | Token::Comment(_) if !first && n == 0 => break,
Token::Newline if first => first = false,
Token::Eof => break,
Token::Comment(lines) => line += lines,
_ => ()
}
iter.next();
}
continue
//Def::Error(format!("NYI: functions"))
}
_ => {
parse_unknown(iter);
Def::Error(format!("Line {}: Unknown definition", line))
}
};
map.push((name.clone(), Rc::new(def)));
Token::Bang => loop {
match iter.next().unwrap() {
Token::Eof | Token::Newline => break,
_ => ()
}
},
_ => parse_unknown(iter)
Token::Ident(name) => {
if is_uppercase(&*name) {
// alias
let mut copy = iter.clone();
let expr = parse_expr(&mut copy);
aliases.push((expr, name.clone()));
}
if name.ends_with("-") {
// prefix
let expr = parse_expr(iter);
let mut name = name;
name.pop();
map.push((name, Rc::new(Def::SPrefix(expr))));
} else {
// unit
if let Some(&Token::Bang) = iter.peek() {
// dimension
iter.next();
if let Some(Token::Ident(ref _n)) = iter.peek().cloned() {
iter.next();
// dimensionless primitive unit
// not sure what to do with these
map.push((name.clone(), Rc::new(Def::Unit(Expr::Const(
"1".to_owned(), None, None)))));
} else {
map.push((name.clone(), Rc::new(Def::Dimension(name))));
}
} else {
// derived
let expr = parse_expr(iter);
map.push((name, Rc::new(Def::Unit(expr))));
}
}
},
x => println!("Expected definition on line {}, got {:?}", line, x),
};
}
Defs {
@ -440,7 +304,7 @@ pub fn parse(mut iter: &mut Iter) -> Defs {
aliases: aliases,
}
}
*/
pub fn tokens(mut iter: &mut Iter) -> Vec<Token> {
let mut out = vec![];
loop {

View file

@ -90,19 +90,19 @@ pub fn load() -> Result<Context, String> {
use std::io::Read;
use std::fs::File;
let f = File::open("units.txt");
let f = File::open("definitions.units");
let mut f = match f {
Ok(f) => f,
Err(_) => {
let mut path = try!(config_dir());
path.push("rink/units.txt");
path.push("rink/definitions.units");
let f = File::open(&path);
match f {
Ok(f) => f,
Err(e) => return Err(format!(
concat!("Failed to open units.txt: {}\nIf you installed using `cargo install`, ",
"then you need to obtain units.txt separately. Here is the URL, ",
"download it and put it in {:?}.\n\n{}\n\n"),
"Failed to open definitions.units: {}\nIf you installed using \
`cargo install`, then you need to obtain units.txt separately. Here is the \
URL, download it and put it in {:?}.\n\n{}\n\n",
e, &path, UNITS_TXT_URL))
}
}
@ -111,8 +111,10 @@ pub fn load() -> Result<Context, String> {
let mut buf = vec![];
f.read_to_end(&mut buf).unwrap();
let string = String::from_utf8_lossy(&*buf);
let mut iter = unit_defs::TokenIterator::new(&*string).peekable();
let res = unit_defs::parse(&mut iter);
//let mut iter = unit_defs::TokenIterator::new(&*string).peekable();
//let res = unit_defs::parse(&mut iter);
let mut iter = gnu_units::TokenIterator::new(&*string).peekable();
let res = gnu_units::parse(&mut iter);
Ok(eval::Context::new(res))
}