Skip to main content

rx/
parser.rs

1//! String parser.
2
3use crate::brush::BrushMode;
4use crate::platform;
5use crate::session::{Direction, Mode, VisualState};
6
7use std::fmt;
8use std::path::PathBuf;
9use std::result;
10use std::str::FromStr;
11
12use directories as dirs;
13use rgx::core::Rgba8;
14
15pub type Result<'a, T> = result::Result<(T, Parser<'a>), Error>;
16
17#[derive(Debug, Clone)]
18pub struct Error {
19    msg: String,
20}
21
22impl Error {
23    pub fn new<S: Into<String>>(msg: S) -> Self {
24        Self { msg: msg.into() }
25    }
26
27    #[allow(dead_code)]
28    fn from<S: Into<String>, E: std::error::Error>(msg: S, err: E) -> Self {
29        Self {
30            msg: format!("{}: {}", msg.into(), err),
31        }
32    }
33}
34
35impl std::error::Error for Error {}
36
37impl From<&str> for Error {
38    fn from(input: &str) -> Self {
39        Error::new(input)
40    }
41}
42
43impl fmt::Display for Error {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        self.msg.fmt(f)
46    }
47}
48
49pub trait Parse<'a>: Sized {
50    fn parse(input: Parser<'a>) -> Result<'a, Self>;
51}
52
53impl<'a> Parse<'a> for Rgba8 {
54    fn parse(p: Parser<'a>) -> Result<'a, Self> {
55        let (s, rest) = p.count(7)?; // Expect 7 characters including the '#'
56
57        match Rgba8::from_str(s) {
58            Ok(u) => Ok((u, rest)),
59            Err(_) => Err(Error::new(format!("malformed color value `{}`", s))),
60        }
61    }
62}
63
64impl<'a> Parse<'a> for u32 {
65    fn parse(p: Parser<'a>) -> Result<'a, Self> {
66        let (s, rest) = p.word()?;
67
68        match u32::from_str(s) {
69            Ok(u) => Ok((u, rest)),
70            Err(_) => Err(Error::new("error parsing u32")),
71        }
72    }
73}
74
75impl<'a> Parse<'a> for i32 {
76    fn parse(p: Parser<'a>) -> Result<'a, Self> {
77        let (s, rest) = p.word()?;
78
79        match i32::from_str(s) {
80            Ok(u) => Ok((u, rest)),
81            Err(_) => Err(Error::new("error parsing i32")),
82        }
83    }
84}
85
86impl<'a> Parse<'a> for f64 {
87    fn parse(p: Parser<'a>) -> Result<'a, Self> {
88        let (s, rest) = p.word()?;
89
90        match f64::from_str(s) {
91            Ok(u) => Ok((u, rest)),
92            Err(_) => Err(Error::new("error parsing f64")),
93        }
94    }
95}
96
97impl<'a> Parse<'a> for (u32, u32) {
98    fn parse(p: Parser<'a>) -> Result<'a, Self> {
99        let (w, p) = p.parse::<u32>()?;
100        let (_, p) = p.whitespace()?;
101        let (h, p) = p.parse::<u32>()?;
102
103        Ok(((w, h), p))
104    }
105}
106
107impl<'a> Parse<'a> for (i32, i32) {
108    fn parse(p: Parser<'a>) -> Result<'a, Self> {
109        let (w, p) = p.parse::<i32>()?;
110        let (_, p) = p.whitespace()?;
111        let (h, p) = p.parse::<i32>()?;
112
113        Ok(((w, h), p))
114    }
115}
116
117impl<'a> Parse<'a> for (f64, f64) {
118    fn parse(p: Parser<'a>) -> Result<'a, Self> {
119        let (x, p) = p.parse::<f64>()?;
120        let (_, p) = p.whitespace()?;
121        let (y, p) = p.parse::<f64>()?;
122
123        Ok(((x, y), p))
124    }
125}
126
127impl<'a> Parse<'a> for char {
128    fn parse(p: Parser<'a>) -> Result<'a, Self> {
129        if let Some(c) = p.input.chars().next() {
130            Ok((c, Parser::new(&p.input[1..])))
131        } else {
132            Err(Error::new("error parsing char"))
133        }
134    }
135}
136
137impl<'a> Parse<'a> for BrushMode {
138    fn parse(p: Parser<'a>) -> Result<'a, Self> {
139        let (id, p) = p.identifier()?;
140        match id {
141            "erase" => Ok((BrushMode::Erase, p)),
142            "multi" => Ok((BrushMode::Multi, p)),
143            "perfect" => Ok((BrushMode::Perfect, p)),
144            "xsym" => Ok((BrushMode::XSym, p)),
145            "ysym" => Ok((BrushMode::YSym, p)),
146            "xray" => Ok((BrushMode::XRay, p)),
147            mode => Err(Error::new(format!("unknown brush mode '{}'", mode))),
148        }
149    }
150}
151
152impl<'a> Parse<'a> for platform::Key {
153    fn parse(p: Parser<'a>) -> Result<'a, Self> {
154        let (c, p) = p.parse::<char>()?;
155        let key: platform::Key = c.into();
156
157        if key == platform::Key::Unknown {
158            return Err(Error::new(format!("unknown key {:?}", c)));
159        }
160        Ok((key, p))
161    }
162}
163
164impl<'a> Parse<'a> for platform::InputState {
165    fn parse(p: Parser<'a>) -> Result<'a, Self> {
166        let (w, p) = p.word()?;
167        match w {
168            "pressed" => Ok((platform::InputState::Pressed, p)),
169            "released" => Ok((platform::InputState::Released, p)),
170            "repeated" => Ok((platform::InputState::Repeated, p)),
171            other => Err(Error::new(format!("unkown input state {:?}", other))),
172        }
173    }
174}
175
176impl<'a> Parse<'a> for Mode {
177    fn parse(p: Parser<'a>) -> Result<'a, Self> {
178        let (id, p) = p.identifier()?;
179        match id {
180            "command" => Ok((Mode::Command, p)),
181            "normal" => Ok((Mode::Normal, p)),
182            "visual" => Ok((Mode::Visual(VisualState::default()), p)),
183            "present" => Ok((Mode::Present, p)),
184            mode => Err(Error::new(format!("unknown mode '{}'", mode))),
185        }
186    }
187}
188
189impl<'a> Parse<'a> for Direction {
190    fn parse(p: Parser<'a>) -> Result<'a, Self> {
191        let (c, p) = p.parse::<char>()?;
192        match c {
193            '+' => Ok((Direction::Forward, p)),
194            '-' => Ok((Direction::Backward, p)),
195            _ => Err(Error::new("direction must be either `+` or `-`")),
196        }
197    }
198}
199
200///////////////////////////////////////////////////////////////////////////////
201
202#[derive(Debug, Clone)]
203pub struct Parser<'a> {
204    input: &'a str,
205}
206
207impl<'a> Parser<'a> {
208    pub fn new(input: &'a str) -> Self {
209        Self { input }
210    }
211
212    pub fn empty() -> Self {
213        Self { input: "" }
214    }
215
216    pub fn finish(self) -> Result<'a, ()> {
217        let (_, p) = self.whitespace()?;
218
219        if p.is_empty() {
220            Ok(((), Parser::empty()))
221        } else {
222            Err(Error::new(format!("extraneaous input: `{}`", p.input)))
223        }
224    }
225
226    pub fn path(self) -> Result<'a, String> {
227        let (path, parser) = self.word()?;
228
229        if path == "" {
230            return Ok((String::from(""), parser));
231        }
232
233        let mut path = PathBuf::from(path);
234
235        // Linux and BSD and MacOS use ~ to infer the home directory of a given user
236        if cfg!(unix) {
237            if let Ok(suffix) = path.strip_prefix("~") {
238                if let Some(base_dirs) = dirs::BaseDirs::new() {
239                    path = base_dirs.home_dir().join(suffix);
240                }
241            }
242        }
243
244        match path.to_str() {
245            Some(p) => Ok((p.to_string(), parser)),
246            None => Err(Error::new(format!("invalid path: {:?}", path))),
247        }
248    }
249
250    pub fn peek(&self) -> Option<char> {
251        self.input.chars().nth(0)
252    }
253
254    pub fn is_empty(&self) -> bool {
255        self.input.is_empty()
256    }
257
258    pub fn sigil(self, c: char) -> Result<'a, char> {
259        if self.input.starts_with(c) {
260            Ok((c, Parser::new(&self.input[1..])))
261        } else {
262            Err(Error::new(format!("expected '{}'", c)))
263        }
264    }
265
266    pub fn string(self) -> Result<'a, &'a str> {
267        let p = self;
268
269        let (_, p) = p.sigil('"')?;
270        let (s, p) = p.until(|c| c == '"')?;
271        let (_, p) = p.sigil('"')?;
272
273        Ok((s, p))
274    }
275
276    pub fn character(self) -> Result<'a, char> {
277        let p = self;
278
279        let (_, p) = p.sigil('\'')?;
280        let (c, p) = p.parse::<char>()?;
281        let (_, p) = p.sigil('\'')?;
282
283        Ok((c, p))
284    }
285
286    pub fn alpha(self) -> Result<'a, &'a str> {
287        self.expect(|c| c.is_alphanumeric())
288    }
289
290    pub fn comment(self) -> Result<'a, &'a str> {
291        let p = self;
292
293        let (_, p) = p.whitespace()?;
294        let (_, p) = p.sigil('-')?;
295        let (_, p) = p.sigil('-')?;
296        let (_, p) = p.whitespace()?;
297        let (s, p) = p.leftover()?;
298
299        Ok((s, p))
300    }
301
302    pub fn leftover(self) -> Result<'a, &'a str> {
303        Ok((self.input, Parser::empty()))
304    }
305
306    pub fn whitespace(self) -> Result<'a, ()> {
307        self.consume(|c| c.is_whitespace())
308    }
309
310    pub fn parse<T: Parse<'a>>(self) -> Result<'a, T> {
311        T::parse(self)
312    }
313
314    pub fn word(self) -> Result<'a, &'a str> {
315        self.expect(|c| !c.is_whitespace() && c != '{' && c != '}')
316    }
317
318    pub fn count(self, n: usize) -> Result<'a, &'a str> {
319        if self.input.len() >= n {
320            Ok((&self.input[..n], Parser::new(&self.input[n..])))
321        } else {
322            Err(Error::new("reached end of input"))
323        }
324    }
325
326    pub fn identifier(self) -> Result<'a, &'a str> {
327        self.expect(|c| {
328            (c.is_ascii_lowercase()
329                || c.is_ascii_uppercase()
330                || c.is_ascii_digit()
331                || [':', '/', '_', '+', '-', '!', '?'].contains(&c))
332        })
333    }
334
335    pub fn consume<P>(self, predicate: P) -> Result<'a, ()>
336    where
337        P: Fn(char) -> bool,
338    {
339        match self.input.find(|c| !predicate(c)) {
340            Some(i) => {
341                let (_, r) = self.input.split_at(i);
342                Ok(((), Parser::new(r)))
343            }
344            None => Ok(((), Parser::empty())),
345        }
346    }
347
348    pub fn until<P>(self, predicate: P) -> Result<'a, &'a str>
349    where
350        P: Fn(char) -> bool,
351    {
352        if self.input.is_empty() {
353            return Err(Error::new("expected input"));
354        }
355        match self.input.find(predicate) {
356            Some(i) => {
357                let (l, r) = self.input.split_at(i);
358                Ok((l, Parser::new(r)))
359            }
360            None => Ok((self.input, Parser::empty())),
361        }
362    }
363
364    pub fn expect<P>(self, predicate: P) -> Result<'a, &'a str>
365    where
366        P: Fn(char) -> bool,
367    {
368        if self.is_empty() {
369            return Err(Error::new("expected input"));
370        }
371        if !self.input.is_ascii() {
372            return Err(Error::new("error parsing non-ASCII characters"));
373        }
374
375        let mut index = 0;
376        for (i, c) in self.input.chars().enumerate() {
377            if predicate(c) {
378                index = i;
379            } else {
380                break;
381            }
382        }
383        let (l, r) = self.input.split_at(index + 1);
384        Ok((l, Parser::new(r)))
385    }
386}