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

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

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

            
73
403835
            for param in ps {
74
403835
                if let Some(sym) = param.as_symbol() {
75
403223
                    if sym.eq_ignore_ascii_case("&optional") {
76
340
                        if matches!(
77
340
                            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
340
                        }
88
340
                        state = ParseState::Optional;
89
340
                        continue;
90
402883
                    }
91
402883
                    if sym.eq_ignore_ascii_case("&rest") {
92
476
                        if matches!(state, ParseState::Rest | ParseState::Key | ParseState::Aux) {
93
                            return Err(Error::Compile(format!(
94
                                "{context}: &rest after {}",
95
                                state.name()
96
                            )));
97
476
                        }
98
476
                        state = ParseState::Rest;
99
476
                        continue;
100
402407
                    }
101
402407
                    if sym.eq_ignore_ascii_case("&key") {
102
340
                        if matches!(state, ParseState::Key | ParseState::Aux) {
103
                            return Err(Error::Compile(format!(
104
                                "{context}: &key after {}",
105
                                state.name()
106
                            )));
107
340
                        }
108
340
                        state = ParseState::Key;
109
340
                        continue;
110
402067
                    }
111
402067
                    if sym.eq_ignore_ascii_case("&aux") {
112
204
                        if matches!(state, ParseState::Aux) {
113
                            return Err(Error::Compile(format!("{context}: &aux after aux")));
114
204
                        }
115
204
                        state = ParseState::Aux;
116
204
                        continue;
117
401863
                    }
118
612
                }
119

            
120
402475
                match state {
121
                    ParseState::Required => {
122
401115
                        let name = param
123
401115
                            .as_symbol()
124
401115
                            .ok_or_else(|| {
125
                                Error::Compile(format!(
126
                                    "{context}: required parameter must be a symbol, got {param:?}"
127
                                ))
128
                            })?
129
401115
                            .to_string();
130
401115
                        params.required.push(name);
131
                    }
132
                    ParseState::Optional => {
133
340
                        let (name, default) = parse_optional_param(context, param)?;
134
340
                        params.optional.push((name, default));
135
                    }
136
                    ParseState::Rest => {
137
476
                        let name = param
138
476
                            .as_symbol()
139
476
                            .ok_or_else(|| {
140
                                Error::Compile(format!(
141
                                    "{context}: &rest parameter must be a symbol, got {param:?}"
142
                                ))
143
                            })?
144
476
                            .to_string();
145
476
                        if params.rest.is_some() {
146
                            return Err(Error::Compile(format!(
147
                                "{context}: multiple &rest parameters"
148
                            )));
149
476
                        }
150
476
                        params.rest = Some(name);
151
476
                        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
340
                        let (name, default) = parse_optional_param(context, param)?;
160
340
                        params.key.push((name, default));
161
                    }
162
                    ParseState::Aux => {
163
204
                        let (name, default) = parse_optional_param(context, param)?;
164
204
                        params.aux.push((name, default));
165
                    }
166
                }
167
            }
168

            
169
396280
            Ok(params)
170
        }
171
        other => Err(Error::Compile(format!(
172
            "{context}: expected parameter list, got {other:?}"
173
        ))),
174
    }
175
396280
}
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
884
fn parse_optional_param(context: &str, param: &Expr) -> Result<(String, Option<Expr>)> {
201
612
    match param {
202
272
        Expr::Symbol(name) => Ok((name.clone(), None)),
203
612
        Expr::List(elems) if elems.len() == 2 => {
204
612
            let name = elems[0]
205
612
                .as_symbol()
206
612
                .ok_or_else(|| {
207
                    Error::Compile(format!(
208
                        "{context}: parameter name must be a symbol, got {:?}",
209
                        elems[0]
210
                    ))
211
                })?
212
612
                .to_string();
213
612
            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
884
}