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
5722298
pub(super) fn parse_quote(input: &mut &str) -> ModalResult<Expr> {
15
5722298
    let _ = '\''.parse_next(input)?;
16
43799
    let expr = parse_expr.parse_next(input)?;
17
43799
    Ok(Expr::Quote(Box::new(expr)))
18
5722298
}
19

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

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

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

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

            
44
    loop {
45
7594014
        if input.starts_with(')') {
46
2192302
            break;
47
5401712
        }
48
5401712
        if input.is_empty() {
49
816
            return Err(ErrMode::Cut(
50
816
                ContextError::new()
51
816
                    .add_context(input, &input.checkpoint(), StrContext::Label("list"))
52
816
                    .add_context(
53
816
                        input,
54
816
                        &input.checkpoint(),
55
816
                        StrContext::Expected(StrContextValue::Description("closing paren")),
56
816
                    ),
57
816
            ));
58
5400896
        }
59
5400896
        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
5400890
        }
76
5400890
        let expr = parse_expr(input)?;
77
5400482
        items.push(expr);
78
5400482
        skip_ws_and_comments_no_annotations(input)?;
79
    }
80

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

            
90
2192307
    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
2192302
        Ok(Expr::List(items))
98
    }
99
5675636
}