]>
Witch of Git - ess/blob - src/lib.rs
1 //! A lightweight S-expression parser intended for language implementation.
3 // #![warn(missing_docs)]
9 use nom
::{digit
, multispace
, IResult
};
11 /// Indicates how parsing failed.
12 #[derive(Debug, PartialEq, Eq, Clone, Copy)]
14 /// We can't explain how the parsing failed.
18 #[derive(Debug, PartialEq, Clone, PartialOrd)]
20 /// A value representing a symbol. A symbol is an atomic unit
22 /// A value representing a string literal.
24 /// A value representing a single character.
26 /// A value representing an integer. Any number containing no decimal point
27 /// will be parsed as an `Int`.
29 /// A value representing a float. Any number containing a decimal point will
30 /// be parsed as a `Float`.
32 /// A list of subexpressions
36 pub fn parse_one(input
: &str) -> Result
<Sexp
, ParseError
> {
37 match do_parse
!(input
,
39 opt
!(complete
!(multispace
)) >>
42 IResult
::Done(_
, res
) => Ok(res
),
43 _
=> Err(ParseError
::Unspecified
),
47 pub fn parse(input
: &str) -> Result
<Vec
<Sexp
>, ParseError
> {
48 let parse_res
: IResult
<&str, Vec
<Sexp
>> =
50 exps
: many1
!(complete
!(sexp
)) >>
51 opt
!(complete
!(multispace
)) >>
55 IResult
::Done(_
, res
) => Ok(res
),
58 Err(ParseError
::Unspecified
)
63 named
!(sexp
<&str, Sexp
>,
65 list
=> { |list
| Sexp
::List(list
) }
70 named
!(list
<&str, Vec
<Sexp
> >,
74 entries
: many0
!(sexp
) >>
81 named
!(atom
<&str, Sexp
>, alt_complete
!(string
| symbol
| number
| character
));
83 named
!(string
<&str, Sexp
>,
87 contents: take_until_s!(r#"""#) >>
89 (Sexp::Str(contents.into()))
93 named!(symbol<&str, Sexp>,
96 peek!(valid_ident_prefix) >>
97 name: take_while1_s!(valid_ident_char) >>
98 (Sexp::Sym(name.into()))
102 fn valid_ident_prefix(ident: &str) -> IResult<&str, ()> {
103 match ident.chars().next() {
104 Some(c) if c != '#' && !c.is_digit(10) && valid_ident_char(c) =>
105 IResult::Done(&ident[1..], ()),
106 None => IResult::Incomplete(nom::Needed::Unknown),
107 _ => IResult::Error(nom::ErrorKind::Custom(0)),
111 fn valid_ident_char(c: char) -> bool {
112 !c.is_whitespace() && c != '"' && c != '(' && c != ')'
115 named
!(number
<&str, Sexp
>,
119 peek
!(not
!(valid_ident_prefix
)) >>
120 (Sexp
::Int(integral
.chars().fold
(0, |i
, c
| i
* 10 + c
as i64 - '
0'
as i64)))
124 named
!(character
<&str, Sexp
>,
128 character
: take_s
!(1) >>
129 (Sexp
::Char(character
.chars().next().unwrap
()))
135 fn test_parse_number() {
136 assert_eq
!(number("0"), IResult
::Done("", Sexp
::Int(0)));
137 assert_eq
!(number("123"), IResult
::Done("", Sexp
::Int(123)));
138 assert_eq
!(number("0123456789"), IResult
::Done("", Sexp
::Int(123456789)));
139 assert_eq
!(number(" 42"), IResult
::Done("", Sexp
::Int(42)));
141 assert
!(number(" 42a").is
_err
());
142 assert_eq
!(number("13()"), IResult
::Done("()", Sexp
::Int(13)));
144 assert
!(number("abc").is
_err
());
145 assert
!(number("()").is
_err
());
146 assert
!(number("").is
_incomplete
());
151 fn test_parse_ident() {
152 assert_eq
!(symbol("+"), IResult
::Done("", Sexp
::Sym("+".into
())));
153 assert_eq
!(symbol(" nil?"), IResult
::Done("", Sexp
::Sym("nil?".into
())));
154 assert_eq
!(symbol(" ->socket"), IResult
::Done("", Sexp
::Sym("->socket".into
())));
155 assert_eq
!(symbol("fib("), IResult
::Done("(", Sexp
::Sym("fib".into
())));
157 // We reserve #foo for the implementation to do as it wishes
158 assert
!(symbol("#hi").is
_err
());
160 assert
!(symbol("0").is
_err
());
161 assert
!(symbol("()").is
_err
());
162 assert
!(symbol("").is
_incomplete
());
167 fn test_parse_string() {
168 assert_eq
!(string(r
#""hello""#), IResult::Done("", Sexp::Str("hello".into())));
169 assert_eq
!(string(r
#" "this is a nice string
170 with
0123 things
in it
""#),
171 IResult
::Done("", Sexp
::Str("this is a nice string\nwith 0123 things in it".into
())));
173 assert
!(string(r
#""hi"#).is_err());
178 fn test_parse_char() {
179 assert_eq!(character(r#"#\""#), IResult::Done("", Sexp::Char('"')));
180 assert_eq!(character(r#"#\ "#), IResult::Done("", Sexp::Char(' ')));
181 assert_eq!(character(r#" #\\"#), IResult::Done("", Sexp::Char('\\')));
183 assert!(character("#").is_incomplete());
184 assert!(character("a").is_err());
189 fn test_parse_list() {
190 assert_eq!(list("()"), IResult::Done("", vec![]));
191 assert_eq!(list("(1)"), IResult::Done("", vec![Sexp::Int(1)]));
192 assert_eq!(list(" ( 1 2 3 a )"), IResult::Done("", vec![
196 Sexp::Sym("a".into()),
202 fn test_parse_only_one() {
203 assert!(parse_one("1 2").is_err());
208 fn test_parse_expression() {
209 assert_eq!(parse_one(r#"
211 (print (str "say " #\" "Hello, World" #\" " today!")))
214 Sexp::Sym("def
".into()),
216 vec![Sexp::Sym("main
".into())]
219 Sexp::Sym("print
".into()),
221 Sexp::Sym("str".into()),
222 Sexp::Str("say
".into()),
224 Sexp
::Str("Hello, World".into
()),
226 Sexp::Str(" today
!".into()),
234 fn test_parse_multi() {
235 assert_eq!(parse(" 1 2 3 "),
236 Ok(vec![Sexp::Int(1), Sexp::Int(2), Sexp::Int(3)]));