symbolic_expressions/
parser.rs

1// (c) 2016-2017 Productize SPRL <joost@productize.be>
2
3use error::SexpError;
4use Sexp;
5use parse_error;
6use std::io;
7use std::fs::File;
8use std::io::prelude::*;
9
10#[derive(Default)]
11struct Parser {
12    data: Vec<char>,
13    position: usize,
14    line: usize,
15    line_position: usize,
16}
17
18impl Parser {
19    fn peek(&self) -> Result<char, SexpError> {
20        self.fail_on_eof()?;
21        Ok(self.data[self.position])
22    }
23
24    fn get(&mut self) -> Result<char, SexpError> {
25        self.fail_on_eof()?;
26        let c = self.data[self.position];
27        self.position += 1;
28        self.line_position += 1;
29        if c == '\n' {
30            self.line += 1;
31            self.line_position = 0;
32        }
33        Ok(c)
34    }
35
36    fn inc(&mut self) {
37        let c = self.data[self.position];
38        self.position += 1;
39        self.line_position += 1;
40        if c == '\n' {
41            self.line += 1;
42            self.line_position = 0;
43        }
44    }
45
46    fn eat_space(&mut self) {
47        while !self.eof() {
48            let c = self.data[self.position];
49            if c == ' ' || c == '\t' {
50                self.inc();
51                continue;
52            }
53            break;
54        }
55    }
56
57    fn eat_char(&mut self, c: char) -> Result<(), SexpError> {
58        let c2 = self.get()?;
59        if c != c2 {
60            self.parse_error(&format!("expected {} got {}", c, c2))
61        } else {
62            Ok(())
63        }
64    }
65
66    fn eof(&self) -> bool {
67        self.position >= self.data.len()
68    }
69
70    fn fail_on_eof(&self) -> Result<(), SexpError> {
71        if self.eof() {
72            return self.parse_error("End of file reached");
73        }
74        Ok(())
75    }
76
77    fn parse_error<T>(&self, msg: &str) -> Result<T, SexpError> {
78        parse_error(self.line + 1, self.line_position + 1, msg.to_string())
79    }
80}
81
82/// parse a &str to a symbolic-expression
83pub fn parse_str(sexp: &str) -> Result<Sexp, SexpError> {
84    if sexp.is_empty() {
85        return Ok(Sexp::default());
86    }
87    let mut parser = Parser::default();
88    parser.data = sexp.chars().collect();
89    parse(&mut parser)
90}
91
92fn parse(parser: &mut Parser) -> Result<Sexp, SexpError> {
93    parser.eat_space();
94    let c = parser.peek()?;
95    if c == '(' {
96        parse_list(parser)
97    } else if c == '"' {
98        parse_quoted_string(parser)
99    } else if c == ')' {
100        parser.parse_error("Unexpected )")
101    } else {
102        parse_bare_string(parser)
103    }
104}
105
106fn parse_list(parser: &mut Parser) -> Result<Sexp, SexpError> {
107    parser.eat_char('(')?;
108    let mut v = vec![];
109    while !parser.eof() {
110        let c = parser.peek()?;
111        if c == ')' {
112            break;
113        } else if c == ' ' || c == '\t' || c == '\r' || c == '\n' {
114            parser.inc()
115        } else {
116            let s = parse(parser)?;
117            v.push(s)
118        }
119    }
120    parser.eat_char(')')?;
121    parser.eat_space();
122    Ok(Sexp::List(v))
123}
124
125fn parse_quoted_string(parser: &mut Parser) -> Result<Sexp, SexpError> {
126    let mut s = String::new();
127    parser.eat_char('"')?;
128    // note that escaped quotes are actually not allowed
129    let mut escape = false;
130    while !parser.eof() {
131        let c = parser.peek()?;
132        if c == '\\' {
133            escape = true;
134        } else if c == '"' {
135            if !escape {
136                break;
137            } else {
138                escape = false;
139            }
140        } else {
141            escape = false;
142        }
143        s.push(c);
144        parser.inc()
145    }
146    parser.eat_char('"')?;
147    Ok(Sexp::String(s))
148}
149
150fn parse_bare_string(parser: &mut Parser) -> Result<Sexp, SexpError> {
151    let mut s = String::new();
152    while !parser.eof() {
153        let c = parser.peek()?;
154        if c == ' ' || c == '(' || c == ')' || c == '\r' || c == '\n' {
155            break;
156        }
157        s.push(c);
158        parser.inc()
159    }
160    Ok(Sexp::String(s))
161}
162
163fn read_file(name: &str) -> Result<String, io::Error> {
164    let mut f = File::open(name)?;
165    let mut s = String::new();
166    f.read_to_string(&mut s)?;
167    Ok(s)
168}
169
170/// parse a file as a symbolic-expression
171pub fn parse_file(name: &str) -> Result<Sexp, SexpError> {
172    let s = read_file(name)?;
173    parse_str(&s[..])
174}