use std::str::FromStr;
use errstr;
use Error;
use MAX_RECURSION_DEPTH;
#[derive(Debug)]
pub struct Tree<'a> {
pub name: &'a str,
pub args: Vec<Tree<'a>>,
}
pub trait FromTree: Sized {
fn from_tree(top: &Tree) -> Result<Self, Error>;
}
impl<'a> Tree<'a> {
fn from_slice(sl: &'a str) -> Result<(Tree<'a>, &'a str), Error> {
Self::from_slice_helper(sl, 0u32)
}
fn from_slice_helper(mut sl: &'a str, depth: u32) -> Result<(Tree<'a>, &'a str), Error> {
if depth >= MAX_RECURSION_DEPTH {
return Err(Error::MaxRecursiveDepthExceeded);
}
enum Found {
Nothing,
Lparen(usize),
Comma(usize),
Rparen(usize),
}
let mut found = Found::Nothing;
for (n, ch) in sl.char_indices() {
match ch {
'(' => {
found = Found::Lparen(n);
break;
}
',' => {
found = Found::Comma(n);
break;
}
')' => {
found = Found::Rparen(n);
break;
}
_ => {}
}
}
match found {
Found::Nothing => Ok((
Tree {
name: &sl[..],
args: vec![],
},
"",
)),
Found::Comma(n) | Found::Rparen(n) => Ok((
Tree {
name: &sl[..n],
args: vec![],
},
&sl[n..],
)),
Found::Lparen(n) => {
let mut ret = Tree {
name: &sl[..n],
args: vec![],
};
sl = &sl[n + 1..];
loop {
let (arg, new_sl) = Tree::from_slice_helper(sl, depth + 1)?;
ret.args.push(arg);
if new_sl.is_empty() {
return Err(Error::ExpectedChar(')'));
}
sl = &new_sl[1..];
match new_sl.as_bytes()[0] {
b',' => {}
b')' => break,
_ => return Err(Error::ExpectedChar(',')),
}
}
Ok((ret, sl))
}
}
}
pub fn from_str(s: &'a str) -> Result<Tree<'a>, Error> {
for ch in s.bytes() {
if ch > 0x7f {
return Err(Error::Unprintable(ch));
}
}
let (top, rem) = Tree::from_slice(s)?;
if rem.is_empty() {
Ok(top)
} else {
Err(errstr(rem))
}
}
}
pub fn parse_num(s: &str) -> Result<u32, Error> {
if s.len() > 1 {
let ch = s.chars().next().unwrap();
if ch < '1' || ch > '9' {
return Err(Error::Unexpected(
"Number must start with a digit 1-9".to_string(),
));
}
}
u32::from_str(s).map_err(|_| errstr(s))
}
pub fn terminal<T, F, Err>(term: &Tree, convert: F) -> Result<T, Error>
where
F: FnOnce(&str) -> Result<T, Err>,
Err: ToString,
{
if term.args.is_empty() {
convert(term.name).map_err(|e| Error::Unexpected(e.to_string()))
} else {
Err(errstr(term.name))
}
}
pub fn unary<L, T, F>(term: &Tree, convert: F) -> Result<T, Error>
where
L: FromTree,
F: FnOnce(L) -> T,
{
if term.args.len() == 1 {
let left = FromTree::from_tree(&term.args[0])?;
Ok(convert(left))
} else {
Err(errstr(term.name))
}
}
pub fn binary<L, R, T, F>(term: &Tree, convert: F) -> Result<T, Error>
where
L: FromTree,
R: FromTree,
F: FnOnce(L, R) -> T,
{
if term.args.len() == 2 {
let left = FromTree::from_tree(&term.args[0])?;
let right = FromTree::from_tree(&term.args[1])?;
Ok(convert(left, right))
} else {
Err(errstr(term.name))
}
}
#[cfg(test)]
mod tests {
use super::parse_num;
#[test]
fn test_parse_num() {
assert!(parse_num("0").is_ok());
assert!(parse_num("00").is_err());
assert!(parse_num("0000").is_err());
assert!(parse_num("06").is_err());
assert!(parse_num("+6").is_err());
assert!(parse_num("-6").is_err());
}
}