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            _ => {}
88        }
89    }
90
91    Ok(VariableDecl {
92        kind,
93        is_mut,
94        pattern,
95        type_annotation,
96        value,
97        ownership,
98    })
99}
100
101/// Parse a pattern for destructuring
102pub fn parse_pattern(pair: Pair<Rule>) -> Result<DestructurePattern> {
103    let pair_loc = pair_location(&pair);
104
105    match pair.as_rule() {
106        Rule::destructure_pattern => {
107            // Pattern is a wrapper, get the inner pattern
108            let inner = pair
109                .into_inner()
110                .next()
111                .ok_or_else(|| ShapeError::ParseError {
112                    message: "expected pattern content".to_string(),
113                    location: Some(pair_loc),
114                })?;
115            parse_pattern(inner)
116        }
117        Rule::destructure_ident_pattern => {
118            let ident = pair
119                .into_inner()
120                .next()
121                .ok_or_else(|| ShapeError::ParseError {
122                    message: "expected identifier in pattern".to_string(),
123                    location: Some(pair_loc),
124                })?;
125            let ident_span = pair_span(&ident);
126            Ok(DestructurePattern::Identifier(
127                ident.as_str().to_string(),
128                ident_span,
129            ))
130        }
131        Rule::destructure_array_pattern => {
132            let mut patterns = Vec::new();
133            for inner in pair.into_inner() {
134                if inner.as_rule() == Rule::destructure_pattern {
135                    patterns.push(parse_pattern(inner)?);
136                }
137            }
138            Ok(DestructurePattern::Array(patterns))
139        }
140        Rule::destructure_object_pattern => {
141            let mut fields = Vec::new();
142            for field in pair.into_inner() {
143                if field.as_rule() == Rule::destructure_object_pattern_field {
144                    let field_loc = pair_location(&field);
145                    let field_str = field.as_str();
146                    let mut field_inner = field.into_inner();
147                    if field_str.trim_start().starts_with("...") {
148                        // Rest pattern
149                        let ident_pair =
150                            field_inner.next().ok_or_else(|| ShapeError::ParseError {
151                                message: "expected identifier after '...' in object pattern"
152                                    .to_string(),
153                                location: Some(field_loc),
154                            })?;
155                        let ident_span = pair_span(&ident_pair);
156                        let ident = ident_pair.as_str().to_string();
157                        fields.push(ObjectPatternField {
158                            key: "...".to_string(),
159                            pattern: DestructurePattern::Rest(Box::new(
160                                DestructurePattern::Identifier(ident, ident_span),
161                            )),
162                        });
163                    } else {
164                        // Regular field
165                        let key_pair =
166                            field_inner.next().ok_or_else(|| ShapeError::ParseError {
167                                message: "expected field name in object pattern".to_string(),
168                                location: Some(field_loc),
169                            })?;
170                        let key_span = pair_span(&key_pair);
171                        let key = key_pair.as_str().to_string();
172                        let pattern = if let Some(pattern_pair) = field_inner.next() {
173                            parse_pattern(pattern_pair)?
174                        } else {
175                            DestructurePattern::Identifier(key.clone(), key_span)
176                        };
177                        fields.push(ObjectPatternField { key, pattern });
178                    }
179                }
180            }
181            Ok(DestructurePattern::Object(fields))
182        }
183        Rule::destructure_rest_pattern => {
184            let ident_pair = pair
185                .into_inner()
186                .next()
187                .ok_or_else(|| ShapeError::ParseError {
188                    message: "expected identifier after '...' in rest pattern".to_string(),
189                    location: Some(pair_loc),
190                })?;
191            let ident_span = pair_span(&ident_pair);
192            let ident = ident_pair.as_str().to_string();
193            Ok(DestructurePattern::Rest(Box::new(
194                DestructurePattern::Identifier(ident, ident_span),
195            )))
196        }
197        Rule::destructure_decomposition_pattern => {
198            // Decomposition pattern: (name: Type, name: Type, ...)
199            // Used for extracting components from intersection types
200            let mut bindings = Vec::new();
201            for binding_pair in pair.into_inner() {
202                if binding_pair.as_rule() == Rule::decomposition_binding {
203                    let binding_span = pair_span(&binding_pair);
204                    let mut binding_inner = binding_pair.into_inner();
205
206                    let name_pair = binding_inner.next().ok_or_else(|| ShapeError::ParseError {
207                        message: "expected identifier in decomposition binding".to_string(),
208                        location: Some(pair_loc.clone()),
209                    })?;
210                    let name = name_pair.as_str().to_string();
211
212                    let type_pair = binding_inner.next().ok_or_else(|| ShapeError::ParseError {
213                        message: "expected type annotation in decomposition binding".to_string(),
214                        location: Some(pair_loc.clone()),
215                    })?;
216                    let type_annotation = if type_pair.as_rule() == Rule::decomposition_field_set {
217                        // Shorthand: {x, y, z} — field names only, types are placeholders
218                        let fields = type_pair
219                            .into_inner()
220                            .filter(|p| p.as_rule() == Rule::ident)
221                            .map(|p| ObjectTypeField {
222                                name: p.as_str().to_string(),
223                                optional: false,
224                                type_annotation: TypeAnnotation::Basic("_".into()),
225                                annotations: vec![],
226                            })
227                            .collect();
228                        TypeAnnotation::Object(fields)
229                    } else {
230                        parse_type_annotation(type_pair)?
231                    };
232
233                    bindings.push(DecompositionBinding {
234                        name,
235                        type_annotation,
236                        span: binding_span,
237                    });
238                }
239            }
240
241            if bindings.len() < 2 {
242                return Err(ShapeError::ParseError {
243                    message: "decomposition pattern requires at least 2 bindings".to_string(),
244                    location: Some(pair_loc),
245                });
246            }
247
248            Ok(DestructurePattern::Decomposition(bindings))
249        }
250        _ => Err(ShapeError::ParseError {
251            message: format!("invalid pattern rule: {:?}", pair.as_rule()),
252            location: Some(pair_loc),
253        }),
254    }
255}