Skip to main content

shape_ast/parser/
items.rs

1//! Item parsing for Shape
2//!
3//! This module handles parsing of:
4//! - Variable declarations (let, var, const)
5//! - Destructuring patterns (identifiers, arrays, objects, rest)
6
7use crate::ast::{
8    DecompositionBinding, DestructurePattern, ObjectPatternField, ObjectTypeField,
9    OwnershipModifier, TypeAnnotation, VarKind, VariableDecl,
10};
11use crate::error::{Result, ShapeError};
12use pest::iterators::Pair;
13
14use super::expressions;
15use super::types::parse_type_annotation;
16use super::{Rule, pair_location, pair_span};
17
18/// Parse a variable declaration
19pub fn parse_variable_decl(pair: Pair<Rule>) -> Result<VariableDecl> {
20    let pair_loc = pair_location(&pair);
21    let mut inner = pair.into_inner();
22
23    // Parse variable kind (let, var, const)
24    let keyword_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
25        message: "expected variable declaration keyword".to_string(),
26        location: Some(
27            pair_loc
28                .clone()
29                .with_hint("use 'let', 'var', or 'const' to declare a variable"),
30        ),
31    })?;
32    let kind_str = keyword_pair.as_str();
33    let kind = match kind_str {
34        "let" => VarKind::Let,
35        "var" => VarKind::Var,
36        "const" => VarKind::Const,
37        _ => {
38            return Err(ShapeError::ParseError {
39                message: format!("invalid variable declaration kind: '{}'", kind_str),
40                location: Some(
41                    pair_location(&keyword_pair).with_hint("use 'let', 'var', or 'const'"),
42                ),
43            });
44        }
45    };
46
47    // Parse optional 'mut' modifier
48    let next_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
49        message: "expected variable name or pattern after keyword".to_string(),
50        location: Some(
51            pair_loc
52                .clone()
53                .with_hint("provide a variable name, e.g., 'let x = 5'"),
54        ),
55    })?;
56    let (is_mut, pattern_pair) = if next_pair.as_rule() == Rule::var_mut_modifier {
57        let p = inner.next().ok_or_else(|| ShapeError::ParseError {
58            message: "expected variable name or pattern after 'mut'".to_string(),
59            location: Some(pair_loc.with_hint("provide a variable name, e.g., 'let mut x = 5'")),
60        })?;
61        (true, p)
62    } else {
63        (false, next_pair)
64    };
65    let pattern = parse_pattern(pattern_pair)?;
66
67    // Parse optional type annotation, ownership modifier, and value
68    let mut type_annotation = None;
69    let mut value = None;
70    let mut ownership = OwnershipModifier::Inferred;
71
72    for pair in inner {
73        match pair.as_rule() {
74            Rule::type_annotation => {
75                type_annotation = Some(parse_type_annotation(pair)?);
76            }
77            Rule::ownership_modifier => {
78                ownership = match pair.as_str() {
79                    "move" => OwnershipModifier::Move,
80                    "clone" => OwnershipModifier::Clone,
81                    _ => OwnershipModifier::Inferred,
82                };
83            }
84            Rule::expression => {
85                value = Some(expressions::parse_expression(pair)?);
86            }
87            Rule::table_row_init => {
88                value = Some(parse_table_row_init(pair)?);
89            }
90            _ => {}
91        }
92    }
93
94    Ok(VariableDecl {
95        kind,
96        is_mut,
97        pattern,
98        type_annotation,
99        value,
100        ownership,
101    })
102}
103
104/// Parse a table row initializer: `[a, b], [c, d], ...`
105/// Each bracket group becomes one row (Vec<Expr>).
106fn parse_table_row_init(pair: Pair<Rule>) -> Result<crate::ast::Expr> {
107    use crate::ast::{Expr, Span};
108    let span = pair_span(&pair);
109    let mut rows = Vec::new();
110
111    for inner in pair.into_inner() {
112        match inner.as_rule() {
113            Rule::array_elements => {
114                let mut elements = Vec::new();
115                for elem_pair in inner.into_inner() {
116                    if elem_pair.as_rule() == Rule::array_element {
117                        let mut elem_inner = elem_pair.into_inner();
118                        if let Some(elem) = elem_inner.next() {
119                            elements.push(expressions::parse_expression(elem)?);
120                        }
121                    }
122                }
123                rows.push(elements);
124            }
125            _ => {}
126        }
127    }
128
129    Ok(Expr::TableRows(rows, Span::new(span.start, span.end)))
130}
131
132/// Parse a pattern for destructuring
133pub fn parse_pattern(pair: Pair<Rule>) -> Result<DestructurePattern> {
134    let pair_loc = pair_location(&pair);
135
136    match pair.as_rule() {
137        Rule::destructure_pattern => {
138            // Pattern is a wrapper, get the inner pattern
139            let inner = pair
140                .into_inner()
141                .next()
142                .ok_or_else(|| ShapeError::ParseError {
143                    message: "expected pattern content".to_string(),
144                    location: Some(pair_loc),
145                })?;
146            parse_pattern(inner)
147        }
148        Rule::destructure_ident_pattern => {
149            let ident = pair
150                .into_inner()
151                .next()
152                .ok_or_else(|| ShapeError::ParseError {
153                    message: "expected identifier in pattern".to_string(),
154                    location: Some(pair_loc),
155                })?;
156            let ident_span = pair_span(&ident);
157            Ok(DestructurePattern::Identifier(
158                ident.as_str().to_string(),
159                ident_span,
160            ))
161        }
162        Rule::destructure_array_pattern => {
163            let mut patterns = Vec::new();
164            for inner in pair.into_inner() {
165                if inner.as_rule() == Rule::destructure_pattern {
166                    patterns.push(parse_pattern(inner)?);
167                }
168            }
169            Ok(DestructurePattern::Array(patterns))
170        }
171        Rule::destructure_object_pattern => {
172            let mut fields = Vec::new();
173            for field in pair.into_inner() {
174                if field.as_rule() == Rule::destructure_object_pattern_field {
175                    let field_loc = pair_location(&field);
176                    let field_str = field.as_str();
177                    let mut field_inner = field.into_inner();
178                    if field_str.trim_start().starts_with("...") {
179                        // Rest pattern
180                        let ident_pair =
181                            field_inner.next().ok_or_else(|| ShapeError::ParseError {
182                                message: "expected identifier after '...' in object pattern"
183                                    .to_string(),
184                                location: Some(field_loc),
185                            })?;
186                        let ident_span = pair_span(&ident_pair);
187                        let ident = ident_pair.as_str().to_string();
188                        fields.push(ObjectPatternField {
189                            key: "...".to_string(),
190                            pattern: DestructurePattern::Rest(Box::new(
191                                DestructurePattern::Identifier(ident, ident_span),
192                            )),
193                        });
194                    } else {
195                        // Regular field
196                        let key_pair =
197                            field_inner.next().ok_or_else(|| ShapeError::ParseError {
198                                message: "expected field name in object pattern".to_string(),
199                                location: Some(field_loc),
200                            })?;
201                        let key_span = pair_span(&key_pair);
202                        let key = key_pair.as_str().to_string();
203                        let pattern = if let Some(pattern_pair) = field_inner.next() {
204                            parse_pattern(pattern_pair)?
205                        } else {
206                            DestructurePattern::Identifier(key.clone(), key_span)
207                        };
208                        fields.push(ObjectPatternField { key, pattern });
209                    }
210                }
211            }
212            Ok(DestructurePattern::Object(fields))
213        }
214        Rule::destructure_rest_pattern => {
215            let ident_pair = pair
216                .into_inner()
217                .next()
218                .ok_or_else(|| ShapeError::ParseError {
219                    message: "expected identifier after '...' in rest pattern".to_string(),
220                    location: Some(pair_loc),
221                })?;
222            let ident_span = pair_span(&ident_pair);
223            let ident = ident_pair.as_str().to_string();
224            Ok(DestructurePattern::Rest(Box::new(
225                DestructurePattern::Identifier(ident, ident_span),
226            )))
227        }
228        Rule::destructure_decomposition_pattern => {
229            // Decomposition pattern: (name: Type, name: Type, ...)
230            // Used for extracting components from intersection types
231            let mut bindings = Vec::new();
232            for binding_pair in pair.into_inner() {
233                if binding_pair.as_rule() == Rule::decomposition_binding {
234                    let binding_span = pair_span(&binding_pair);
235                    let mut binding_inner = binding_pair.into_inner();
236
237                    let name_pair = binding_inner.next().ok_or_else(|| ShapeError::ParseError {
238                        message: "expected identifier in decomposition binding".to_string(),
239                        location: Some(pair_loc.clone()),
240                    })?;
241                    let name = name_pair.as_str().to_string();
242
243                    let type_pair = binding_inner.next().ok_or_else(|| ShapeError::ParseError {
244                        message: "expected type annotation in decomposition binding".to_string(),
245                        location: Some(pair_loc.clone()),
246                    })?;
247                    let type_annotation = if type_pair.as_rule() == Rule::decomposition_field_set {
248                        // Shorthand: {x, y, z} — field names only, types are placeholders
249                        let fields = type_pair
250                            .into_inner()
251                            .filter(|p| p.as_rule() == Rule::ident)
252                            .map(|p| ObjectTypeField {
253                                name: p.as_str().to_string(),
254                                optional: false,
255                                type_annotation: TypeAnnotation::Basic("_".into()),
256                                annotations: vec![],
257                            })
258                            .collect();
259                        TypeAnnotation::Object(fields)
260                    } else {
261                        parse_type_annotation(type_pair)?
262                    };
263
264                    bindings.push(DecompositionBinding {
265                        name,
266                        type_annotation,
267                        span: binding_span,
268                    });
269                }
270            }
271
272            if bindings.len() < 2 {
273                return Err(ShapeError::ParseError {
274                    message: "decomposition pattern requires at least 2 bindings".to_string(),
275                    location: Some(pair_loc),
276                });
277            }
278
279            Ok(DestructurePattern::Decomposition(bindings))
280        }
281        _ => Err(ShapeError::ParseError {
282            message: format!("invalid pattern rule: {:?}", pair.as_rule()),
283            location: Some(pair_loc),
284        }),
285    }
286}