use std::collections::HashMap;
use std::iter::Peekable;
use std::str::{Chars,FromStr};
#[derive(Clone,Debug,PartialEq)]
pub enum Adnot {
Sum(String, Vec<Adnot>),
Prod(HashMap<String, Adnot>),
List(Vec<Adnot>),
Str(String),
Sym(String),
Num(i64),
Dbl(f64),
}
#[derive(Debug)]
pub struct AdnotError {
message: String,
}
type Stream<'a> = Peekable<Chars<'a>>;
fn fail<T>(s: &str) -> Result<T, AdnotError> {
Err(AdnotError { message: s.to_string()})
}
fn parse_val(s: &mut Stream) -> Result<Adnot, AdnotError> {
if let Some(c) = s.next() {
match c {
'[' => parse_list(s),
'(' => parse_sum(s),
'{' => parse_prod(s),
'"' => parse_string(s),
_ if c.is_digit(10) || c == '-' => parse_num(c, s),
_ if c.is_alphabetic() =>
Ok(Adnot::Sym(parse_sym(c, s)?)),
_ => Err(AdnotError { message: format!("Invalid character: {:?}", c) }),
}
} else {
fail("Unexpected end of input")
}
}
fn parse_list(s: &mut Stream) -> Result<Adnot, AdnotError> {
let mut vec = vec![];
loop {
skip_space(s);
if let Some(&']') = s.peek() {
s.next();
return Ok(Adnot::List(vec));
} else {
vec.push(parse_val(s)?);
}
}
}
fn parse_sum(s: &mut Stream) -> Result<Adnot, AdnotError> {
let mut vec = vec![];
skip_space(s);
let first = match s.next() {
Some(c) if c.is_alphabetic() => c,
Some(_) => { return fail("Expected a tagname character") }
None => { return fail("Unexpected end of input") }
};
let name = parse_sym(first, s)?;
loop {
skip_space(s);
if let Some(&')') = s.peek() {
s.next();
return Ok(Adnot::Sum(name, vec));
} else {
vec.push(parse_val(s)?);
}
}
}
fn parse_prod(s: &mut Stream) -> Result<Adnot, AdnotError> {
let mut map = HashMap::new();
loop {
skip_space(s);
if let Some(&'}') = s.peek() {
s.next();
return Ok(Adnot::Prod(map));
} else {
skip_space(s);
let first = match s.next() {
Some(c) if c.is_alphabetic() => c,
Some(_) => { return fail("Expected a tagname character") }
None => { return fail("Unexpected end of input") }
};
let key = parse_sym(first, s)?;
skip_space(s);
let val = parse_val(s)?;
map.insert(key, val);
}
}
}
fn parse_string(s: &mut Stream) -> Result<Adnot, AdnotError> {
let mut chars = Vec::new();
while let Some(c) = s.next() {
if c == '"' {
break;
} else if c == '\\' {
match s.next() {
Some('n') => chars.push('\n'),
Some('r') => chars.push('\r'),
Some('t') => chars.push('\t'),
Some('\'') => chars.push('\''),
Some('\"') => chars.push('\"'),
Some('\\') => chars.push('\\'),
_ => return fail("Invalid escape sequence"),
}
} else {
chars.push(c);
}
};
Ok(Adnot::Str(chars.iter().cloned().collect()))
}
fn parse_num(c: char, s: &mut Stream) -> Result<Adnot, AdnotError> {
let mut str = vec![c];
str.extend(s.take_while(|c| c.is_digit(10)));
let string: String = str.iter().cloned().collect();
Ok(Adnot::Num(i64::from_str(&string).unwrap()))
}
fn parse_sym(c: char, s: &mut Stream) -> Result<String, AdnotError> {
let mut chars = vec![c];
while let Some(&c) = s.peek() {
if c.is_alphanumeric() || c == '_' {
chars.push(s.next().unwrap());
} else {
break;
}
};
Ok(chars.iter().cloned().collect())
}
fn skip_space(s: &mut Stream) {
while let Some(&c) = s.peek() {
match c {
'#' => { skip_comment(s); }
_ if c.is_whitespace() => { s.next(); }
_ => break,
}
}
}
fn skip_comment(s: &mut Stream) {
s.next();
while let Some(&c) = s.peek() {
if c == '\n' || c == '\r' {
s.next();
return;
} else {
s.next();
}
}
}
impl Adnot {
pub fn parse(s: &str) -> Result<Adnot, AdnotError> {
let mut stream = s.chars().peekable();
skip_space(&mut stream);
parse_val(&mut stream)
}
}