bebop_lang/lisp/
parser.rs

1use crate::lisp::Lval;
2use nom::{
3    branch::alt,
4    character::complete::{char, multispace0, none_of, one_of},
5    combinator::{all_consuming, map},
6    error::{ErrorKind, ParseError},
7    multi::{many0, many1},
8    number::complete::double,
9    sequence::{delimited, preceded},
10    IResult,
11};
12
13#[derive(Debug, PartialEq)]
14pub enum SyntaxError<I> {
15    InvalidArguments,
16    InvalidSymbol,
17    Nom(I, ErrorKind),
18}
19
20impl<I> ParseError<I> for SyntaxError<I> {
21    fn from_error_kind(input: I, kind: ErrorKind) -> Self {
22        SyntaxError::Nom(input, kind)
23    }
24
25    fn append(_: I, _: ErrorKind, other: Self) -> Self {
26        other
27    }
28}
29
30fn parse_number(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
31    map(preceded(multispace0, double), |n| Lval::Num(n))(s)
32}
33
34fn parse_symbol(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
35    map(
36        preceded(
37            multispace0,
38            many1(map(
39                one_of(
40                    "_+\\:-*/=<>|!&%abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
41                ),
42                |c| format!("{}", c),
43            )),
44        ),
45        |o| Lval::Sym(o.join("")),
46    )(s)
47}
48
49fn parse_string(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
50    map(
51        delimited(
52            preceded(multispace0, char('"')),
53            many0(map(none_of("\""), |c| format!("{}", c))),
54            preceded(multispace0, char('"')),
55        ),
56        |o| Lval::Str(o.join("")),
57    )(s)
58}
59
60fn parse_sexpression(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
61    delimited(
62        preceded(multispace0, char('(')),
63        map(many0(parse_expression), |e| Lval::Sexpr(e)),
64        preceded(multispace0, char(')')),
65    )(s)
66}
67
68fn parse_qexpression(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
69    delimited(
70        preceded(multispace0, char('[')),
71        map(many0(parse_expression), |e| Lval::Qexpr(e)),
72        preceded(multispace0, char(']')),
73    )(s)
74}
75
76fn parse_expression(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
77    alt((
78        parse_number,
79        parse_symbol,
80        parse_string,
81        parse_sexpression,
82        parse_qexpression,
83    ))(s)
84}
85
86pub fn parse(s: &str) -> IResult<&str, Lval, SyntaxError<&str>> {
87    all_consuming(delimited(
88        multispace0,
89        map(many0(parse_expression), |e| Lval::Sexpr(e)),
90        multispace0,
91    ))(s)
92}
93
94#[cfg(test)]
95mod test {
96    use super::*;
97
98    #[test]
99    fn it_parses_numbers() {
100        assert_eq!(parse_number("1"), Ok(("", Lval::Num(1.0_f64))));
101        assert_eq!(
102            parse_number("1.000001-1"),
103            Ok(("-1", Lval::Num(1.000001_f64)))
104        );
105        assert_eq!(parse_number("123E-02"), Ok(("", Lval::Num(1.23_f64))));
106        assert_eq!(parse_number("-12302"), Ok(("", Lval::Num(-12302_f64))));
107        assert_eq!(parse_number("  \t1"), Ok(("", Lval::Num(1_f64))));
108    }
109
110    #[test]
111    fn it_parses_all_symbols() {
112        assert_eq!(parse_symbol("+"), Ok(("", Lval::Sym(String::from("+")))));
113        assert_eq!(parse_symbol("\t-"), Ok(("", Lval::Sym(String::from("-")))));
114        assert_eq!(parse_symbol("  *"), Ok(("", Lval::Sym(String::from("*")))));
115        assert_eq!(parse_symbol("\n/"), Ok(("", Lval::Sym(String::from("/")))));
116        assert_eq!(
117            parse_symbol("orange"),
118            Ok(("", Lval::Sym(String::from("orange"))))
119        );
120        assert_eq!(
121            parse_symbol("tail"),
122            Ok(("", Lval::Sym(String::from("tail"))))
123        );
124    }
125
126    #[test]
127    fn it_parses_sexpr() {
128        assert_eq!(
129            parse_sexpression(
130                "(* 1
131             2 3)"
132            ),
133            Ok((
134                "",
135                Lval::Sexpr(vec!(
136                    Lval::Sym(String::from("*")),
137                    Lval::Num(1_f64),
138                    Lval::Num(2_f64),
139                    Lval::Num(3_f64),
140                ))
141            ))
142        );
143    }
144
145    #[test]
146    fn it_parses_qexpr() {
147        assert_eq!(
148            parse_qexpression(
149                "[* 1
150             2 3]"
151            ),
152            Ok((
153                "",
154                Lval::Qexpr(vec!(
155                    Lval::Sym(String::from("*")),
156                    Lval::Num(1_f64),
157                    Lval::Num(2_f64),
158                    Lval::Num(3_f64),
159                ))
160            ))
161        );
162    }
163
164    #[test]
165    fn it_parses_an_expression() {
166        assert_eq!(
167            parse_expression(
168                "(* 1
169             2 3)"
170            ),
171            Ok((
172                "",
173                Lval::Sexpr(vec!(
174                    Lval::Sym(String::from("*")),
175                    Lval::Num(1_f64),
176                    Lval::Num(2_f64),
177                    Lval::Num(3_f64),
178                ))
179            ))
180        );
181
182        assert_eq!(
183            parse_expression(
184                "(* 1
185             2 (* 1
186          2 3))"
187            ),
188            Ok((
189                "",
190                Lval::Sexpr(vec!(
191                    Lval::Sym(String::from("*")),
192                    Lval::Num(1_f64),
193                    Lval::Num(2_f64),
194                    Lval::Sexpr(vec!(
195                        Lval::Sym(String::from("*")),
196                        Lval::Num(1_f64),
197                        Lval::Num(2_f64),
198                        Lval::Num(3_f64),
199                    )),
200                ))
201            ))
202        );
203
204        assert_eq!(
205            parse_expression(
206                "9 (* 1
207             2 (* 1
208          2 3))"
209            ),
210            Ok((
211                " (* 1\n             2 (* 1\n          2 3))",
212                Lval::Num(9_f64)
213            ))
214        );
215        assert_eq!(parse_expression("1"), Ok(("", Lval::Num(1_f64),)));
216        assert_eq!(
217            parse_expression("*"),
218            Ok(("", Lval::Sym(String::from("*"),)))
219        );
220    }
221
222    #[test]
223    fn it_parses_expressions() {
224        assert_eq!(
225            parse(
226                "* 9 (* 1
227             2 (* 1
228          2 3))"
229            ),
230            Ok((
231                "",
232                Lval::Sexpr(vec!(
233                    Lval::Sym(String::from("*")),
234                    Lval::Num(9_f64),
235                    Lval::Sexpr(vec!(
236                        Lval::Sym(String::from("*")),
237                        Lval::Num(1_f64),
238                        Lval::Num(2_f64),
239                        Lval::Sexpr(vec!(
240                            Lval::Sym(String::from("*")),
241                            Lval::Num(1_f64),
242                            Lval::Num(2_f64),
243                            Lval::Num(3_f64),
244                        )),
245                    )),
246                ))
247            ))
248        );
249        assert_eq!(parse(""), Ok(("", Lval::Sexpr(vec![]))));
250        assert_eq!(
251            parse("()"),
252            Ok(("", Lval::Sexpr(vec![Lval::Sexpr(vec![])])))
253        );
254        assert_eq!(
255            parse("*"),
256            Ok(("", Lval::Sexpr(vec![Lval::Sym(String::from("*"))]),))
257        );
258        assert_eq!(parse("9"), Ok(("", Lval::Sexpr(vec![Lval::Num(9_f64)]),)));
259        assert_eq!(
260            parse("* 1 2 3"),
261            Ok((
262                "",
263                Lval::Sexpr(vec!(
264                    Lval::Sym(String::from("*")),
265                    Lval::Num(1_f64),
266                    Lval::Num(2_f64),
267                    Lval::Num(3_f64),
268                )),
269            ))
270        );
271    }
272}