]> Witch of Git - ess/blob - src/lib.rs
Add an extremely basic s-expression parser using nom
[ess] / src / lib.rs
1 //! A lightweight S-expression parser intended for language implementation.
2
3 #![warn(missing_docs)]
4 #![deny(unsafe_code)]
5
6 #[macro_use]
7 extern crate nom;
8
9 use nom::{digit, multispace, IResult};
10
11 #[derive(Debug, PartialEq, Clone, PartialOrd)]
12 pub enum Atom {
13 /// A value representing a symbol. A symbol is an atomic unit
14 Sym(String),
15 /// A value representing a string literal.
16 Str(String),
17 /// A value representing an integer. Any number containing no decimal point
18 /// will be parsed as an `Int`.
19 Int(i64),
20 /// A value representing a float. Any number containing a decimal point will
21 /// be parsed as a `Float`.
22 Float(f64),
23 }
24
25 #[derive(Debug, PartialEq, Clone, PartialOrd)]
26 pub enum Sexp {
27 /// A wrapper around the atom type
28 Atom {
29 atom: Atom,
30 },
31 /// A list of subexpressions
32 List {
33 list: Vec<Sexp>,
34 }
35 }
36
37 pub fn parse(input: &str) -> Result<Sexp, ()> {
38 match sexp(input) {
39 IResult::Done(_, res) => Ok(res),
40 _ => Err(()),
41 }
42 }
43
44 named!(sexp<&str, Sexp>,
45 alt!(
46 list => { |list| Sexp::List { list: list } }
47 | atom => { |atom| Sexp::Atom { atom: atom } }
48 )
49 );
50
51 named!(list<&str, Vec<Sexp> >,
52 do_parse!(
53 opt!(multispace) >>
54 tag_s!("(") >>
55 entries: many0!(sexp) >>
56 opt!(multispace) >>
57 tag_s!(")") >>
58 (entries)
59 )
60 );
61
62 named!(atom<&str, Atom>, alt!(string | symbol | number));
63
64 named!(string<&str, Atom>,
65 do_parse!(
66 opt!(multispace) >>
67 tag_s!("\"") >>
68 contents: take_until_s!("\"") >>
69 tag_s!("\"") >>
70 opt!(multispace) >>
71 (Atom::Str(contents.into()))
72 )
73 );
74
75 named!(symbol<&str, Atom>,
76 do_parse!(
77 peek!(not!(digit)) >>
78 name: take_while1_s!(valid_ident_char) >>
79 (Atom::Sym(name.into()))
80 )
81 );
82
83 fn valid_ident_char(c: char) -> bool {
84 !c.is_whitespace() && c != '"' && c != '(' && c != ')'
85 }
86
87 named!(number<&str, Atom>,
88 do_parse!(
89 opt!(multispace) >>
90 integral: digit >>
91 (Atom::Int(integral.chars().fold(0, |i, c| i * 10 + c as i64 - '0' as i64)))
92 )
93 );
94
95 #[cfg(test)]
96 mod tests {
97 #[test]
98 fn it_works() {
99 }
100 }