]>
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
, ()> {
40 match do_parse!(input
, exp
: sexp
>> opt!(multispace
) >> eof!() >> (exp
)) {
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!("\"") >>
72 (Atom
::Str(contents
.into()))
76 named!(symbol
<&str, Atom
>,
79 peek!(valid_ident_prefix
) >>
80 name
: take_while1_s!(valid_ident_char
) >>
81 (Atom
::Sym(name
.into()))
85 fn valid_ident_prefix(ident
: &str) -> IResult
<&str, ()> {
86 match ident
.chars().next() {
87 Some(c
) if c
!= '
#' && !c.is_digit(10) && valid_ident_char(c) =>
88 IResult
::Done(&ident
[1..], ()),
89 None
=> IResult
::Incomplete(nom
::Needed
::Unknown
),
90 _
=> IResult
::Error(nom
::ErrorKind
::Custom(0)),
94 fn valid_ident_char(c
: char) -> bool
{
95 !c
.is_whitespace() && c
!= '
"' && c != '(' && c != ')'
98 named!(number<&str, Atom>,
102 peek!(not!(valid_ident_prefix)) >>
103 (Atom::Int(integral.chars().fold(0, |i, c| i * 10 + c as i64 - '0' as i64)))
107 named!(character<&str, Atom>,
111 character: take_s!(1) >>
112 (Atom::Char(character.chars().next().unwrap()))
118 fn test_parse_number() {
119 assert_eq!(number("0"), IResult::Done("", Atom::Int(0)));
120 assert_eq!(number("123"), IResult::Done("", Atom::Int(123)));
121 assert_eq!(number("0123456789"), IResult::Done("", Atom::Int(123456789)));
122 assert_eq!(number(" 42"), IResult::Done("", Atom::Int(42)));
124 assert!(number(" 42a").is_err());
125 assert_eq!(number("13()"), IResult::Done("()", Atom::Int(13)));
127 assert!(number("abc").is_err());
128 assert!(number("()").is_err());
129 assert!(number("").is_incomplete());
134 fn test_parse_ident() {
135 assert_eq!(symbol("+"), IResult::Done("", Atom::Sym("+".into())));
136 assert_eq!(symbol(" nil?"), IResult::Done("", Atom::Sym("nil?".into())));
137 assert_eq!(symbol(" ->socket"), IResult::Done("", Atom::Sym("->socket".into())));
138 assert_eq!(symbol("fib("), IResult::Done("(", Atom::Sym("fib".into())));
140 // We reserve #foo for the implementation to do as it wishes
141 assert!(symbol("#hi").is_err());
143 assert!(symbol("0").is_err());
144 assert!(symbol("()").is_err());
145 assert!(symbol("").is_incomplete());
150 fn test_parse_string() {
151 assert_eq!(string(r#""hello""#), IResult::Done("", Atom::Str("hello".into())));
152 assert_eq!(string(r#" "this is a nice string
153 with 0123 things in it""#),
154 IResult::Done("", Atom::Str("this is a nice string\nwith 0123 things in it".into())));
156 assert!(string(r#""hi"#).is_err());
161 fn test_parse_char() {
162 assert_eq!(character("#\\\""), IResult
::Done("", Atom
::Char('
"')));
163 assert_eq!(character("#\\ "), IResult::Done("", Atom::Char(' ')));
164 assert_eq!(character(" #\\\\"), IResult::Done("", Atom::Char('\\')));
166 assert!(character("#").is_incomplete());
167 assert!(character("a").is_err());
172 fn test_parse_list() {
173 assert_eq!(list("()"), IResult::Done("", vec![]));
174 assert_eq!(list("(1)"), IResult::Done("", vec![Sexp::Atom { atom: Atom::Int(1) }]));
175 assert_eq!(list(" ( 1 2 3 a )"), IResult::Done("", vec![
176 Sexp::Atom { atom: Atom::Int(1) },
177 Sexp::Atom { atom: Atom::Int(2) },
178 Sexp::Atom { atom: Atom::Int(3) },
179 Sexp::Atom { atom: Atom::Sym("a".into()) },
185 fn test_cant_parse() {
186 assert!(parse("1 2").is_err());
191 fn test_parse_expression() {
194 (print (str "say " #\" "Hello, World" #\" " today!")))
198 Sexp::Atom { atom: Atom::Sym("def
".into()) },
201 Sexp::Atom { atom: Atom::Sym("main
".into()) }
206 Sexp::Atom { atom: Atom::Sym("print
".into()) },
210 atom: Atom::Sym("str".into())
213 atom: Atom::Str("say
".into())
215 Sexp::Atom { atom: Atom::Char('"'
) },
217 atom
: Atom
::Str("Hello, World".into())
219 Sexp
::Atom
{ atom
: Atom
::Char('
"') },
221 atom: Atom::Str(" today
!".into())
This page took 0.075709 seconds and 5 git commands to generate.