1
//! Parsing helpers for binding forms.
2
//!
3
//! - `parse_bindings` — the `((name init) (name init) ...)` list LET
4
//!   / LET* / DO take.
5
//! - `parse_param_list` — a simple required-param list for places
6
//!   that don't accept `&optional` / `&rest` / `&key` / `&aux`.
7
//! - `parse_lambda_params` — the full lambda-list parser plus its
8
//!   `ParseState` state machine.
9

            
10
use crate::ast::{Expr, LambdaParams};
11
use crate::error::{Error, Result};
12

            
13
31950
pub(in crate::compiler) fn parse_bindings(
14
31950
    context: &str,
15
31950
    expr: &Expr,
16
31950
) -> Result<Vec<(String, Option<Expr>)>> {
17
31950
    let list = match expr {
18
31950
        Expr::List(elems) => elems,
19
        _ => {
20
            return Err(Error::Compile(format!(
21
                "{context}: expected bindings list, got {expr:?}"
22
            )));
23
        }
24
    };
25
31950
    list.iter()
26
36849
        .map(|binding| match binding {
27
            Expr::Symbol(name) => Ok((name.clone(), None)),
28
36920
            Expr::List(elems) if elems.len() == 1 => {
29
71
                let name = elems[0].as_symbol().ok_or_else(|| {
30
                    Error::Compile(format!(
31
                        "{context}: binding name must be a symbol, got {:?}",
32
                        elems[0]
33
                    ))
34
                })?;
35
71
                Ok((name.to_string(), None))
36
            }
37
36849
            Expr::List(elems) if elems.len() == 2 => {
38
36849
                let name = elems[0].as_symbol().ok_or_else(|| {
39
71
                    Error::Compile(format!(
40
71
                        "{context}: binding name must be a symbol, got {:?}",
41
71
                        elems[0]
42
71
                    ))
43
71
                })?;
44
36778
                Ok((name.to_string(), Some(elems[1].clone())))
45
            }
46
            _ => Err(Error::Compile(format!(
47
                "{context}: malformed binding: {binding:?}"
48
            ))),
49
36920
        })
50
31950
        .collect()
51
31950
}
52

            
53
781
pub(in crate::compiler) fn parse_param_list(context: &str, expr: &Expr) -> Result<Vec<String>> {
54
781
    let params = parse_lambda_params(context, expr)?;
55
781
    if !params.optional.is_empty()
56
781
        || params.rest.is_some()
57
781
        || !params.key.is_empty()
58
781
        || !params.aux.is_empty()
59
    {
60
        return Err(Error::Compile(format!(
61
            "{context}: lambda list keywords not supported in this context"
62
        )));
63
781
    }
64
781
    Ok(params.required)
65
781
}
66

            
67
426143
pub(in crate::compiler) fn parse_lambda_params(context: &str, expr: &Expr) -> Result<LambdaParams> {
68
426143
    match expr {
69
426143
        Expr::List(ps) => {
70
426143
            let mut params = LambdaParams::simple(Vec::new());
71
426143
            let mut state = ParseState::Required;
72

            
73
434027
            for param in ps {
74
434027
                if let Some(sym) = param.as_symbol() {
75
433388
                    if sym.eq_ignore_ascii_case("&optional") {
76
355
                        if matches!(
77
355
                            state,
78
                            ParseState::Optional
79
                                | ParseState::Rest
80
                                | ParseState::Key
81
                                | ParseState::Aux
82
                        ) {
83
                            return Err(Error::Compile(format!(
84
                                "{context}: &optional after {}",
85
                                state.name()
86
                            )));
87
355
                        }
88
355
                        state = ParseState::Optional;
89
355
                        continue;
90
433033
                    }
91
433033
                    if sym.eq_ignore_ascii_case("&rest") {
92
497
                        if matches!(state, ParseState::Rest | ParseState::Key | ParseState::Aux) {
93
                            return Err(Error::Compile(format!(
94
                                "{context}: &rest after {}",
95
                                state.name()
96
                            )));
97
497
                        }
98
497
                        state = ParseState::Rest;
99
497
                        continue;
100
432536
                    }
101
432536
                    if sym.eq_ignore_ascii_case("&key") {
102
355
                        if matches!(state, ParseState::Key | ParseState::Aux) {
103
                            return Err(Error::Compile(format!(
104
                                "{context}: &key after {}",
105
                                state.name()
106
                            )));
107
355
                        }
108
355
                        state = ParseState::Key;
109
355
                        continue;
110
432181
                    }
111
432181
                    if sym.eq_ignore_ascii_case("&aux") {
112
213
                        if matches!(state, ParseState::Aux) {
113
                            return Err(Error::Compile(format!("{context}: &aux after aux")));
114
213
                        }
115
213
                        state = ParseState::Aux;
116
213
                        continue;
117
431968
                    }
118
639
                }
119

            
120
432607
                match state {
121
                    ParseState::Required => {
122
431187
                        let name = param
123
431187
                            .as_symbol()
124
431187
                            .ok_or_else(|| {
125
                                Error::Compile(format!(
126
                                    "{context}: required parameter must be a symbol, got {param:?}"
127
                                ))
128
                            })?
129
431187
                            .to_string();
130
431187
                        params.required.push(name);
131
                    }
132
                    ParseState::Optional => {
133
355
                        let (name, default) = parse_optional_param(context, param)?;
134
355
                        params.optional.push((name, default));
135
                    }
136
                    ParseState::Rest => {
137
497
                        let name = param
138
497
                            .as_symbol()
139
497
                            .ok_or_else(|| {
140
                                Error::Compile(format!(
141
                                    "{context}: &rest parameter must be a symbol, got {param:?}"
142
                                ))
143
                            })?
144
497
                            .to_string();
145
497
                        if params.rest.is_some() {
146
                            return Err(Error::Compile(format!(
147
                                "{context}: multiple &rest parameters"
148
                            )));
149
497
                        }
150
497
                        params.rest = Some(name);
151
497
                        state = ParseState::PostRest;
152
                    }
153
                    ParseState::PostRest => {
154
                        return Err(Error::Compile(format!(
155
                            "{context}: parameters after &rest must use &key or &aux"
156
                        )));
157
                    }
158
                    ParseState::Key => {
159
355
                        let (name, default) = parse_optional_param(context, param)?;
160
355
                        params.key.push((name, default));
161
                    }
162
                    ParseState::Aux => {
163
213
                        let (name, default) = parse_optional_param(context, param)?;
164
213
                        params.aux.push((name, default));
165
                    }
166
                }
167
            }
168

            
169
426143
            Ok(params)
170
        }
171
        other => Err(Error::Compile(format!(
172
            "{context}: expected parameter list, got {other:?}"
173
        ))),
174
    }
175
426143
}
176

            
177
#[derive(Debug, Clone)]
178
enum ParseState {
179
    Required,
180
    Optional,
181
    Rest,
182
    PostRest,
183
    Key,
184
    Aux,
185
}
186

            
187
impl ParseState {
188
    fn name(&self) -> &'static str {
189
        match self {
190
            ParseState::Required => "required",
191
            ParseState::Optional => "&optional",
192
            ParseState::Rest => "&rest",
193
            ParseState::PostRest => "post-&rest",
194
            ParseState::Key => "&key",
195
            ParseState::Aux => "&aux",
196
        }
197
    }
198
}
199

            
200
923
fn parse_optional_param(context: &str, param: &Expr) -> Result<(String, Option<Expr>)> {
201
639
    match param {
202
284
        Expr::Symbol(name) => Ok((name.clone(), None)),
203
639
        Expr::List(elems) if elems.len() == 2 => {
204
639
            let name = elems[0]
205
639
                .as_symbol()
206
639
                .ok_or_else(|| {
207
                    Error::Compile(format!(
208
                        "{context}: parameter name must be a symbol, got {:?}",
209
                        elems[0]
210
                    ))
211
                })?
212
639
                .to_string();
213
639
            Ok((name, Some(elems[1].clone())))
214
        }
215
        _ => Err(Error::Compile(format!(
216
            "{context}: optional parameter must be a symbol or (symbol default), got {param:?}"
217
        ))),
218
    }
219
923
}