gdritter repos thermidor / 16bea25
Added Adnot parser, too Getty Ritter 7 years ago
2 changed file(s) with 169 addition(s) and 0 deletion(s). Collapse all Expand all
1 use std::collections::HashMap;
2 use std::iter::Peekable;
3 use std::str::{Chars,FromStr};
4
5 #[derive(Clone,Debug,PartialEq)]
6 pub enum Adnot {
7 Sum(String, Vec<Adnot>),
8 Prod(HashMap<String, Adnot>),
9 List(Vec<Adnot>),
10 Str(String),
11 Sym(String),
12 Num(i64),
13 Dbl(f64),
14 }
15
16 #[derive(Debug)]
17 pub struct AdnotError {
18 message: String,
19 }
20
21 type Stream<'a> = Peekable<Chars<'a>>;
22
23 fn fail<T>(s: &str) -> Result<T, AdnotError> {
24 Err(AdnotError { message: s.to_string()})
25 }
26
27 fn parse_val(s: &mut Stream) -> Result<Adnot, AdnotError> {
28 if let Some(c) = s.next() {
29 match c {
30 '[' => parse_list(s),
31 '(' => parse_sum(s),
32 '{' => parse_prod(s),
33 '"' => parse_string(s),
34 _ if c.is_digit(10) || c == '-' => parse_num(c, s),
35 _ if c.is_alphabetic() =>
36 Ok(Adnot::Sym(try!(parse_sym(c, s)))),
37 _ => fail(&format!("Invalid character: {:?}", c)),
38 }
39 } else {
40 fail("Unexpected end of input")
41 }
42 }
43
44 fn parse_list(s: &mut Stream) -> Result<Adnot, AdnotError> {
45 let mut vec = vec![];
46 loop {
47 skip_space(s);
48 if let Some(&']') = s.peek() {
49 s.next();
50 return Ok(Adnot::List(vec));
51 } else {
52 vec.push(try!(parse_val(s)));
53 }
54 }
55 }
56
57 fn parse_sum(s: &mut Stream) -> Result<Adnot, AdnotError> {
58 let mut vec = vec![];
59 skip_space(s);
60 let first = match s.next() {
61 Some(c) if c.is_alphabetic() => c,
62 Some(_) => { return fail("Expected a tagname character") }
63 None => { return fail("Unexpected end of input") }
64 };
65 let name = try!(parse_sym(first, s));
66 loop {
67 skip_space(s);
68 if let Some(&')') = s.peek() {
69 s.next();
70 return Ok(Adnot::Sum(name, vec));
71 } else {
72 vec.push(try!(parse_val(s)));
73 }
74 }
75 }
76
77 fn parse_prod(s: &mut Stream) -> Result<Adnot, AdnotError> {
78 let mut map = HashMap::new();
79 loop {
80 skip_space(s);
81 if let Some(&'}') = s.peek() {
82 s.next();
83 return Ok(Adnot::Prod(map));
84 } else {
85 skip_space(s);
86 let first = match s.next() {
87 Some(c) if c.is_alphabetic() => c,
88 Some(_) => { return fail("Expected a tagname character") }
89 None => { return fail("Unexpected end of input") }
90 };
91 let key = try!(parse_sym(first, s));
92 skip_space(s);
93 let val = try!(parse_val(s));
94 map.insert(key, val);
95 }
96 }
97 }
98
99 fn parse_string(s: &mut Stream) -> Result<Adnot, AdnotError> {
100 let mut chars = Vec::new();
101 while let Some(c) = s.next() {
102 if c == '"' {
103 break;
104 } else if c == '\\' {
105 match s.next() {
106 Some('n') => chars.push('\n'),
107 Some('r') => chars.push('\r'),
108 Some('t') => chars.push('\t'),
109 Some('\'') => chars.push('\''),
110 Some('\"') => chars.push('\"'),
111 Some('\\') => chars.push('\\'),
112 _ => return fail("Invalid escape sequence"),
113 }
114 } else {
115 chars.push(c);
116 }
117 };
118 Ok(Adnot::Str(chars.iter().cloned().collect()))
119 }
120
121 fn parse_num(c: char, s: &mut Stream) -> Result<Adnot, AdnotError> {
122 let mut str = vec![c];
123 str.extend(s.take_while(|c| c.is_digit(10)));
124 let string: String = str.iter().cloned().collect();
125 Ok(Adnot::Num(i64::from_str(&string).unwrap()))
126 }
127
128 fn parse_sym(c: char, s: &mut Stream) -> Result<String, AdnotError> {
129 let mut chars = vec![c];
130 while let Some(&c) = s.peek() {
131 if c.is_alphanumeric() || c == '_' {
132 chars.push(s.next().unwrap());
133 } else {
134 break;
135 }
136 };
137 Ok(chars.iter().cloned().collect())
138 }
139
140 fn skip_space(s: &mut Stream) {
141 while let Some(&c) = s.peek() {
142 match c {
143 '#' => { skip_comment(s); }
144 _ if c.is_whitespace() => { s.next(); }
145 _ => break,
146 }
147 }
148 }
149
150 fn skip_comment(s: &mut Stream) {
151 s.next();
152 while let Some(&c) = s.peek() {
153 if c == '\n' || c == '\r' {
154 s.next();
155 return;
156 } else {
157 s.next();
158 }
159 }
160 }
161
162 impl Adnot {
163 pub fn parse(s: &str) -> Result<Adnot, AdnotError> {
164 let mut stream = s.chars().peekable();
165 skip_space(&mut stream);
166 parse_val(&mut stream)
167 }
168 }
1 pub mod adnot;
12 pub mod reader;
23
34 #[cfg(test)]