use std::collections::HashMap; use std::iter::Peekable; use std::str::{Chars,FromStr}; #[derive(Clone,Debug,PartialEq)] pub enum Adnot { Sum(String, Vec), Prod(HashMap), List(Vec), Str(String), Sym(String), Num(i64), Dbl(f64), } #[derive(Debug)] pub struct AdnotError { message: String, } type Stream<'a> = Peekable>; fn fail(s: &str) -> Result { Err(AdnotError { message: s.to_string()}) } fn parse_val(s: &mut Stream) -> Result { 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(try!(parse_sym(c, s)))), _ => fail(&format!("Invalid character: {:?}", c)), } } else { fail("Unexpected end of input") } } fn parse_list(s: &mut Stream) -> Result { let mut vec = vec![]; loop { skip_space(s); if let Some(&']') = s.peek() { s.next(); return Ok(Adnot::List(vec)); } else { vec.push(try!(parse_val(s))); } } } fn parse_sum(s: &mut Stream) -> Result { 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 = try!(parse_sym(first, s)); loop { skip_space(s); if let Some(&')') = s.peek() { s.next(); return Ok(Adnot::Sum(name, vec)); } else { vec.push(try!(parse_val(s))); } } } fn parse_prod(s: &mut Stream) -> Result { 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 = try!(parse_sym(first, s)); skip_space(s); let val = try!(parse_val(s)); map.insert(key, val); } } } fn parse_string(s: &mut Stream) -> Result { 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 { 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 { 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 { let mut stream = s.chars().peekable(); skip_space(&mut stream); parse_val(&mut stream) } }