Browse Source

Added Adnot parser, too

Getty Ritter 7 years ago
parent
commit
16bea25fe6
2 changed files with 169 additions and 0 deletions
  1. 168 0
      therm_util/src/adnot.rs
  2. 1 0
      therm_util/src/lib.rs

+ 168 - 0
therm_util/src/adnot.rs

@@ -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)
+    }
+}

+ 1 - 0
therm_util/src/lib.rs

@@ -1,3 +1,4 @@
+pub mod adnot;
 pub mod reader;
 
 #[cfg(test)]