1
//! Reader macros for quote / quasiquote / unquote / unquote-splicing
2
//! plus the parenthesised list parser (including the dotted-pair tail
3
//! `(a . b)` form).
4

            
5
use winnow::combinator::cut_err;
6
use winnow::error::{AddContext, ContextError, ErrMode, ModalResult, StrContext, StrContextValue};
7
use winnow::prelude::*;
8

            
9
use crate::ast::Expr;
10

            
11
use super::parse_expr;
12
use super::whitespace::skip_ws_and_comments_no_annotations;
13

            
14
6275804
pub(super) fn parse_quote(input: &mut &str) -> ModalResult<Expr> {
15
6275804
    let _ = '\''.parse_next(input)?;
16
45731
    let expr = parse_expr.parse_next(input)?;
17
45731
    Ok(Expr::Quote(Box::new(expr)))
18
6275804
}
19

            
20
6230073
pub(super) fn parse_quasiquote(input: &mut &str) -> ModalResult<Expr> {
21
6230073
    let _ = '`'.parse_next(input)?;
22
1423
    let expr = parse_expr.parse_next(input)?;
23
1423
    Ok(Expr::Quasiquote(Box::new(expr)))
24
6230073
}
25

            
26
6228650
pub(super) fn parse_unquote(input: &mut &str) -> ModalResult<Expr> {
27
6228650
    let _ = ','.parse_next(input)?;
28
1566
    if input.starts_with('@') {
29
214
        let _ = '@'.parse_next(input)?;
30
214
        let expr = parse_expr.parse_next(input)?;
31
214
        return Ok(Expr::UnquoteSplicing(Box::new(expr)));
32
1352
    }
33
1352
    let expr = parse_expr.parse_next(input)?;
34
1352
    Ok(Expr::Unquote(Box::new(expr)))
35
6228650
}
36

            
37
6227084
pub(super) fn parse_list(input: &mut &str) -> ModalResult<Expr> {
38
6227084
    let _ = '('.parse_next(input)?;
39
2394594
    skip_ws_and_comments_no_annotations(input)?;
40

            
41
2394594
    let mut items = Vec::new();
42
2394594
    let mut dotted_cdr: Option<Expr> = None;
43

            
44
    loop {
45
8390536
        if input.starts_with(')') {
46
2392813
            break;
47
5997723
        }
48
5997723
        if input.is_empty() {
49
923
            return Err(ErrMode::Cut(
50
923
                ContextError::new()
51
923
                    .add_context(input, &input.checkpoint(), StrContext::Label("list"))
52
923
                    .add_context(
53
923
                        input,
54
923
                        &input.checkpoint(),
55
923
                        StrContext::Expected(StrContextValue::Description("closing paren")),
56
923
                    ),
57
923
            ));
58
5996800
        }
59
5996800
        if input.starts_with('.') && input.chars().nth(1).is_some_and(char::is_whitespace) {
60
6
            let _ = '.'.parse_next(input)?;
61
6
            skip_ws_and_comments_no_annotations(input)?;
62
6
            dotted_cdr = Some(
63
6
                cut_err(parse_expr)
64
6
                    .context(StrContext::Label("cdr expression"))
65
6
                    .parse_next(input)?,
66
            );
67
6
            skip_ws_and_comments_no_annotations(input)?;
68
6
            cut_err(')')
69
6
                .context(StrContext::Label("closing paren"))
70
6
                .context(StrContext::Expected(StrContextValue::Description(
71
6
                    "only one expression after dot in dotted pair",
72
6
                )))
73
6
                .parse_next(input)?;
74
5
            break;
75
5996794
        }
76
5996794
        let expr = parse_expr(input)?;
77
5995942
        items.push(expr);
78
5995942
        skip_ws_and_comments_no_annotations(input)?;
79
    }
80

            
81
2392818
    if dotted_cdr.is_none() {
82
2392813
        cut_err(')')
83
2392813
            .context(StrContext::Label("list"))
84
2392813
            .context(StrContext::Expected(StrContextValue::Description(
85
2392813
                "closing paren",
86
2392813
            )))
87
2392813
            .parse_next(input)?;
88
5
    }
89

            
90
2392818
    if let Some(cdr) = dotted_cdr {
91
5
        let mut result = cdr;
92
6
        for item in items.into_iter().rev() {
93
6
            result = Expr::cons(item, result);
94
6
        }
95
5
        Ok(result)
96
    } else {
97
2392813
        Ok(Expr::List(items))
98
    }
99
6227084
}