]>
Witch of Git - ess/blob - src/lib.rs
1 //! A lightweight S-expression parser intended for language implementation.
9 use nom
::{digit
, multispace
, IResult
};
11 #[derive(Debug, PartialEq, Clone, PartialOrd)]
13 /// A value representing a symbol. A symbol is an atomic unit
15 /// A value representing a string literal.
17 /// A value representing a single character.
19 /// A value representing an integer. Any number containing no decimal point
20 /// will be parsed as an `Int`.
22 /// A value representing a float. Any number containing a decimal point will
23 /// be parsed as a `Float`.
27 #[derive(Debug, PartialEq, Clone, PartialOrd)]
29 /// A wrapper around the atom type
33 /// A list of subexpressions
39 pub fn parse(input
: &str) -> Result
<Sexp
, ()> {
41 IResult
::Done(_
, res
) => Ok(res
),
46 named
!(sexp
<&str, Sexp
>,
48 list
=> { |list
| Sexp
::List
{ list
: list
} }
49 | atom
=> { |atom
| Sexp
::Atom
{ atom
: atom
} }
53 named
!(list
<&str, Vec
<Sexp
> >,
57 entries
: many0
!(sexp
) >>
64 named
!(atom
<&str, Atom
>, alt
!(string
| symbol
| number
| character
));
66 named
!(string
<&str, Atom
>,
70 contents: take_until_s!("\"") >>
73 (Atom::Str(contents.into()))
77 named!(symbol<&str, Atom>,
80 peek!(valid_ident_prefix) >>
81 name: take_while1_s!(valid_ident_char) >>
82 (Atom::Sym(name.into()))
86 fn valid_ident_prefix(ident: &str) -> IResult<&str, ()> {
87 match ident.chars().next() {
88 Some(c) if c != '#' && !c.is_digit(10) && valid_ident_char(c) =>
89 IResult::Done(&ident[1..], ()),
90 None => IResult::Incomplete(nom::Needed::Unknown),
91 _ => IResult::Error(nom::ErrorKind::Custom(0)),
95 fn valid_ident_char(c: char) -> bool {
96 !c.is_whitespace() && c != '"'
&& c
!= '
('
&& c
!= '
)'
99 named
!(number
<&str, Atom
>,
103 peek
!(not
!(valid_ident_prefix
)) >>
104 (Atom
::Int(integral
.chars().fold
(0, |i
, c
| i
* 10 + c
as i64 - '
0'
as i64)))
108 named
!(character
<&str, Atom
>,
112 character
: take_s
!(1) >>
113 (Atom
::Char(character
.chars().next().unwrap
()))
119 fn test_parse_number() {
120 assert_eq
!(number("0"), IResult
::Done("", Atom
::Int(0)));
121 assert_eq
!(number("123"), IResult
::Done("", Atom
::Int(123)));
122 assert_eq
!(number("0123456789"), IResult
::Done("", Atom
::Int(123456789)));
123 assert_eq
!(number(" 42"), IResult
::Done("", Atom
::Int(42)));
125 assert
!(number(" 42a").is
_err
());
126 assert_eq
!(number("13()"), IResult
::Done("()", Atom
::Int(13)));
128 assert
!(number("abc").is
_err
());
129 assert
!(number("()").is
_err
());
130 assert
!(number("").is
_incomplete
());
135 fn test_parse_ident() {
136 assert_eq
!(symbol("+"), IResult
::Done("", Atom
::Sym("+".into
())));
137 assert_eq
!(symbol(" nil?"), IResult
::Done("", Atom
::Sym("nil?".into
())));
138 assert_eq
!(symbol(" ->socket"), IResult
::Done("", Atom
::Sym("->socket".into
())));
139 assert_eq
!(symbol("fib("), IResult
::Done("(", Atom
::Sym("fib".into
())));
141 // We reserve #foo for the implementation to do as it wishes
142 assert
!(symbol("#hi").is
_err
());
144 assert
!(symbol("0").is
_err
());
145 assert
!(symbol("()").is
_err
());
146 assert
!(symbol("").is
_incomplete
());
151 fn test_parse_char() {
152 assert_eq
!(character("#\\\""), IResult::Done("", Atom::Char('"'
)));
153 assert_eq
!(character("#\\ "), IResult
::Done("", Atom
::Char(' '
)));
154 assert_eq
!(character(" #\\\\"), IResult
::Done("", Atom
::Char('
\\'
)));
156 assert
!(character("#").is
_incomplete
());
157 assert
!(character("a").is
_err
());