|
@@ -0,0 +1,168 @@
|
|
|
+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(try!(parse_sym(c, s)))),
|
|
|
+ _ => fail(&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(try!(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 = 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<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 = 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<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)
|
|
|
+ }
|
|
|
+}
|