Skip to main content

shape_ast/parser/expressions/
primary.rs

1//! Primary expression parsing
2//!
3//! This module handles parsing of primary expressions and postfix operations:
4//! - Primary expressions (identifiers, literals, grouped expressions)
5//! - Postfix operations (property access, method calls, indexing)
6//! - Expression entry point
7
8use crate::ast::{
9    DataRef, EnumConstructorPayload, Expr, JoinBranch, JoinExpr, JoinKind, RangeKind, Span, Spanned,
10};
11use crate::error::{Result, ShapeError};
12use crate::parser::{Rule, pair_location};
13use pest::iterators::Pair;
14
15use super::super::pair_span;
16
17/// Main entry point for parsing expressions
18pub fn parse_expression(pair: Pair<Rule>) -> Result<Expr> {
19    let pair_loc = pair_location(&pair);
20    match pair.as_rule() {
21        Rule::expression => {
22            let inner = pair
23                .into_inner()
24                .next()
25                .ok_or_else(|| ShapeError::ParseError {
26                    message: "expected expression content".to_string(),
27                    location: Some(pair_loc),
28                })?;
29            parse_expression(inner)
30        }
31        Rule::assignment_expr => super::binary_ops::parse_assignment_expr(pair),
32        Rule::ternary_expr => super::binary_ops::parse_ternary_expr(pair),
33        Rule::null_coalesce_expr => super::binary_ops::parse_null_coalesce_expr(pair),
34        Rule::context_expr => super::binary_ops::parse_context_expr(pair),
35        Rule::or_expr => super::binary_ops::parse_or_expr(pair),
36        Rule::range_expr => super::binary_ops::parse_range_expr(pair),
37        _ => Err(ShapeError::ParseError {
38            message: format!("expected expression, got {:?}", pair.as_rule()),
39            location: Some(pair_loc),
40        }),
41    }
42}
43
44/// Parse postfix expression (property access, method calls, indexing)
45pub fn parse_postfix_expr(pair: Pair<Rule>) -> Result<Expr> {
46    let pair_loc = pair_location(&pair);
47    let mut inner = pair.into_inner();
48    let primary_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
49        message: "expected primary expression".to_string(),
50        location: Some(pair_loc),
51    })?;
52    let mut expr = parse_primary_expr(primary_pair)?;
53
54    // Collect all postfix operations to handle method calls
55    let postfix_ops: Vec<_> = inner.collect();
56    let mut i = 0;
57
58    while i < postfix_ops.len() {
59        let postfix = &postfix_ops[i];
60        match postfix.as_rule() {
61            Rule::property_access | Rule::optional_property_access => {
62                let is_optional = postfix.as_rule() == Rule::optional_property_access;
63                let postfix_loc = pair_location(postfix);
64                let property = postfix
65                    .clone()
66                    .into_inner()
67                    .next()
68                    .ok_or_else(|| ShapeError::ParseError {
69                        message: "expected property name after '.'".to_string(),
70                        location: Some(postfix_loc),
71                    })?
72                    .as_str()
73                    .to_string();
74
75                let full_span = Span::new(expr.span().start, pair_span(postfix).end);
76
77                // Check if next operation is a function call (method call)
78                if i + 1 < postfix_ops.len() && postfix_ops[i + 1].as_rule() == Rule::function_call
79                {
80                    let (args, named_args) =
81                        super::functions::parse_arg_list(postfix_ops[i + 1].clone())?;
82                    // For optional chaining, we'd need to add optional to MethodCall as well
83                    // For now, treating it as a regular method call
84                    expr = Expr::MethodCall {
85                        receiver: Box::new(expr),
86                        method: property,
87                        args,
88                        named_args,
89                        span: full_span,
90                    };
91                    i += 2; // Skip the function call we just processed
92                } else {
93                    expr = Expr::PropertyAccess {
94                        object: Box::new(expr),
95                        property,
96                        optional: is_optional,
97                        span: full_span,
98                    };
99                    i += 1;
100                }
101            }
102            Rule::index_access => {
103                let postfix_loc = pair_location(postfix);
104                let postfix_span = pair_span(postfix);
105                // Special case for data indexing (legacy "candle" identifier support)
106                if let Expr::Identifier(ref id, _) = expr {
107                    if id == "candle" {
108                        let index_expr = postfix.clone().into_inner().next().ok_or_else(|| {
109                            ShapeError::ParseError {
110                                message: "expected index expression in data access".to_string(),
111                                location: Some(postfix_loc.clone()),
112                            }
113                        })?;
114                        let (index, timeframe) = super::data_refs::parse_index_expr(index_expr)?;
115                        expr = Expr::DataRef(DataRef { index, timeframe }, postfix_span);
116                        i += 1;
117                        continue;
118                    }
119                }
120                // Special case for data with timeframe: data(5m)[0]
121                if let Expr::DataRef(ref mut data_ref, _) = expr {
122                    let index_expr = postfix.clone().into_inner().next().ok_or_else(|| {
123                        ShapeError::ParseError {
124                            message: "expected index expression in data access".to_string(),
125                            location: Some(postfix_loc.clone()),
126                        }
127                    })?;
128                    let (index, timeframe_override) =
129                        super::data_refs::parse_index_expr(index_expr)?;
130                    data_ref.index = index;
131                    // If there's a timeframe in the index, it overrides the one from data(5m)
132                    if timeframe_override.is_some() {
133                        data_ref.timeframe = timeframe_override;
134                    }
135                    i += 1;
136                    continue;
137                }
138                // General array/object indexing
139                let index_expr_pair =
140                    postfix
141                        .clone()
142                        .into_inner()
143                        .next()
144                        .ok_or_else(|| ShapeError::ParseError {
145                            message: "expected index expression".to_string(),
146                            location: Some(postfix_loc),
147                        })?;
148                let (index_expr, end_expr) =
149                    super::data_refs::parse_index_expr_general(index_expr_pair)?;
150                expr = Expr::IndexAccess {
151                    object: Box::new(expr),
152                    index: Box::new(index_expr),
153                    end_index: end_expr.map(Box::new),
154                    span: postfix_span,
155                };
156                i += 1;
157            }
158            Rule::function_call => {
159                let (args, named_args) = super::functions::parse_arg_list(postfix.clone())?;
160                if let Expr::Identifier(name, _) = expr {
161                    expr = Expr::FunctionCall {
162                        name,
163                        args,
164                        named_args,
165                        span: pair_span(postfix),
166                    };
167                } else {
168                    // Chained call: expr(args) where expr is not an identifier
169                    let full_span = Span::new(expr.span().start, pair_span(postfix).end);
170                    expr = Expr::MethodCall {
171                        receiver: Box::new(expr),
172                        method: "__call__".to_string(),
173                        args,
174                        named_args,
175                        span: full_span,
176                    };
177                }
178                i += 1;
179            }
180            Rule::type_assertion_suffix => {
181                let mut inner = postfix.clone().into_inner();
182
183                // Skip the as_keyword pair
184                let first = inner.next().ok_or_else(|| ShapeError::ParseError {
185                    message: "Type assertion missing type annotation".to_string(),
186                    location: None,
187                })?;
188                let type_pair = if first.as_rule() == Rule::as_keyword {
189                    inner.next().ok_or_else(|| ShapeError::ParseError {
190                        message: "Type assertion missing type annotation after 'as'".to_string(),
191                        location: None,
192                    })?
193                } else {
194                    first
195                };
196                let type_annotation = crate::parser::parse_type_annotation(type_pair)?;
197
198                // Check for optional comptime field overrides
199                let meta_param_overrides = if let Some(overrides_pair) = inner.next() {
200                    if overrides_pair.as_rule() == Rule::comptime_field_overrides {
201                        Some(crate::parser::types::parse_comptime_field_overrides(
202                            overrides_pair,
203                        )?)
204                    } else {
205                        None
206                    }
207                } else {
208                    None
209                };
210
211                expr = Expr::TypeAssertion {
212                    expr: Box::new(expr),
213                    type_annotation,
214                    meta_param_overrides,
215                    span: pair_span(postfix),
216                };
217                i += 1;
218            }
219            Rule::using_impl_suffix => {
220                let impl_name = postfix
221                    .clone()
222                    .into_inner()
223                    .next()
224                    .ok_or_else(|| ShapeError::ParseError {
225                        message: "Missing impl name after 'using'".to_string(),
226                        location: Some(pair_location(postfix)),
227                    })?
228                    .as_str()
229                    .to_string();
230                let full_span = Span::new(expr.span().start, pair_span(postfix).end);
231                expr = Expr::UsingImpl {
232                    expr: Box::new(expr),
233                    impl_name,
234                    span: full_span,
235                };
236                i += 1;
237            }
238            Rule::try_operator => {
239                // Try operator: expr? - unified fallible propagation (Result/Option)
240                expr = Expr::TryOperator(Box::new(expr), pair_span(postfix));
241                i += 1;
242            }
243            _ => {
244                return Err(ShapeError::ParseError {
245                    message: format!("Unexpected postfix operator: {:?}", postfix.as_rule()),
246                    location: None,
247                });
248            }
249        }
250    }
251
252    Ok(expr)
253}
254
255/// Parse primary expression
256pub fn parse_primary_expr(pair: Pair<Rule>) -> Result<Expr> {
257    let pair_loc = pair_location(&pair);
258    // Handle case where we might have nested primary_expr rules
259    match pair.as_rule() {
260        Rule::primary_expr => {
261            // Check if this is an empty array literal case
262            let inner_pairs: Vec<_> = pair.clone().into_inner().collect();
263            if inner_pairs.is_empty() {
264                // For cases that shouldn't happen
265                return Err(ShapeError::ParseError {
266                    message: "empty primary expression".to_string(),
267                    location: Some(pair_loc),
268                });
269            }
270            let first = inner_pairs
271                .into_iter()
272                .next()
273                .ok_or_else(|| ShapeError::ParseError {
274                    message: "expected primary expression content".to_string(),
275                    location: Some(pair_loc),
276                })?;
277            parse_primary_expr_inner(first)
278        }
279        _ => parse_primary_expr_inner(pair),
280    }
281}
282
283/// Parse the inner primary expression
284fn parse_primary_expr_inner(pair: Pair<Rule>) -> Result<Expr> {
285    let span = pair_span(&pair);
286    let pair_loc = pair_location(&pair);
287
288    match pair.as_rule() {
289        Rule::unit_literal => Ok(Expr::Literal(crate::ast::Literal::Unit, span)),
290        Rule::literal => super::literals::parse_literal(pair),
291        Rule::array_literal => super::literals::parse_array_literal(pair),
292        Rule::data_ref => super::data_refs::parse_data_ref(pair),
293        Rule::time_ref => Ok(Expr::TimeRef(super::temporal::parse_time_ref(pair)?, span)),
294        Rule::datetime_expr => Ok(Expr::DateTime(
295            super::temporal::parse_datetime_expr(pair)?,
296            span,
297        )),
298        Rule::pattern_name => {
299            let name_pair = pair
300                .into_inner()
301                .next()
302                .ok_or_else(|| ShapeError::ParseError {
303                    message: "expected pattern name".to_string(),
304                    location: Some(pair_loc),
305                })?;
306            Ok(Expr::PatternRef(name_pair.as_str().to_string(), span))
307        }
308        Rule::enum_constructor_expr => parse_enum_constructor_expr(pair),
309        Rule::ident => Ok(Expr::Identifier(pair.as_str().to_string(), span)),
310        Rule::expression => parse_expression(pair),
311        Rule::temporal_nav => super::temporal::parse_temporal_nav(pair),
312        Rule::timeframe_expr => super::temporal::parse_timeframe_expr(pair),
313        Rule::async_let_expr => super::control_flow::parse_async_let_expr(pair),
314        Rule::async_scope_expr => super::control_flow::parse_async_scope_expr(pair),
315        Rule::if_expr => super::control_flow::parse_if_expr(pair),
316        Rule::while_expr => super::control_flow::parse_while_expr(pair),
317        Rule::for_expr => super::control_flow::parse_for_expr(pair),
318        Rule::loop_expr => super::control_flow::parse_loop_expr(pair),
319        Rule::let_expr => super::control_flow::parse_let_expr(pair),
320        Rule::match_expr => super::control_flow::parse_match_expr(pair),
321        Rule::break_expr => super::control_flow::parse_break_expr(pair),
322        Rule::continue_expr => Ok(Expr::Continue(span)),
323        Rule::return_expr => super::control_flow::parse_return_expr(pair),
324        Rule::block_expr => super::control_flow::parse_block_expr(pair),
325        Rule::object_literal => super::literals::parse_object_literal(pair),
326        Rule::function_expr => super::functions::parse_function_expr(pair),
327        Rule::some_expr => parse_some_expr(pair),
328        Rule::duration => super::temporal::parse_duration(pair),
329        // Handle nested primary_expr (this should recursively call parse_primary_expr)
330        Rule::primary_expr => parse_primary_expr(pair),
331        // Handle postfix_expr (function calls, property access, etc.)
332        Rule::postfix_expr => parse_postfix_expr(pair),
333        Rule::list_comprehension => super::comprehensions::parse_list_comprehension(pair),
334        Rule::await_expr => parse_await_expr(pair),
335        Rule::struct_literal => parse_struct_literal(pair),
336        Rule::from_query_expr => super::control_flow::parse_from_query_expr(pair),
337        Rule::comptime_for_expr => parse_comptime_for_expr(pair),
338        Rule::comptime_block => parse_comptime_block(pair),
339        Rule::annotated_expr => parse_annotated_expr(pair),
340        Rule::datetime_range => {
341            // Parse datetime range - return Range expression if end is present
342            let (start, end) = super::temporal::parse_datetime_range(pair)?;
343            match end {
344                Some(end_expr) => Ok(Expr::Range {
345                    start: Some(Box::new(start)),
346                    end: Some(Box::new(end_expr)),
347                    kind: RangeKind::Inclusive, // DateTime ranges are inclusive
348                    span,
349                }),
350                None => Ok(start),
351            }
352        }
353        _ => Err(ShapeError::ParseError {
354            message: format!("Unexpected primary expression: {:?}", pair.as_rule()),
355            location: None,
356        }),
357    }
358}
359
360/// Parse Some expression: Some(value) constructor for Option type
361fn parse_some_expr(pair: Pair<Rule>) -> Result<Expr> {
362    let span = pair_span(&pair);
363    let pair_loc = pair_location(&pair);
364    let inner = pair
365        .into_inner()
366        .next()
367        .ok_or_else(|| ShapeError::ParseError {
368            message: "expected expression in Some()".to_string(),
369            location: Some(pair_loc),
370        })?;
371    let inner_expr = parse_expression(inner)?;
372    Ok(Expr::FunctionCall {
373        name: "Some".to_string(),
374        args: vec![inner_expr],
375        named_args: vec![],
376        span,
377    })
378}
379
380/// Parse await expression: await expr | await @annotation expr | await join kind { ... }
381fn parse_await_expr(pair: Pair<Rule>) -> Result<Expr> {
382    let span = pair_span(&pair);
383    let pair_loc = pair_location(&pair);
384    // Filter out the await_keyword atomic token
385    let inner_pairs: Vec<_> = pair
386        .into_inner()
387        .filter(|p| p.as_rule() != Rule::await_keyword)
388        .collect();
389
390    if inner_pairs.is_empty() {
391        return Err(ShapeError::ParseError {
392            message: "expected expression after 'await'".to_string(),
393            location: Some(pair_loc),
394        });
395    }
396
397    // Check what the first inner element is
398    let first = &inner_pairs[0];
399    match first.as_rule() {
400        Rule::join_expr => {
401            // await join all|race|any|settle { branches }
402            let join = parse_join_expr(first.clone())?;
403            let join_span = pair_span(first);
404            Ok(Expr::Await(
405                Box::new(Expr::Join(Box::new(join), join_span)),
406                span,
407            ))
408        }
409        Rule::annotation => {
410            // await @annotation ... expr — collect annotations and wrap target
411            let mut annotations = Vec::new();
412            let mut target_pair = None;
413
414            for p in &inner_pairs {
415                match p.as_rule() {
416                    Rule::annotation => {
417                        annotations.push(crate::parser::functions::parse_annotation(p.clone())?);
418                    }
419                    Rule::postfix_expr => {
420                        target_pair = Some(p.clone());
421                    }
422                    _ => {}
423                }
424            }
425
426            let target = target_pair.ok_or_else(|| ShapeError::ParseError {
427                message: "expected expression after annotations in await".to_string(),
428                location: Some(pair_loc),
429            })?;
430
431            let mut expr = parse_postfix_expr(target)?;
432
433            // Wrap in Annotated nodes right-to-left so the first annotation is outermost
434            for annotation in annotations.into_iter().rev() {
435                let anno_span = Span::new(annotation.span.start, expr.span().end);
436                expr = Expr::Annotated {
437                    annotation,
438                    target: Box::new(expr),
439                    span: anno_span,
440                };
441            }
442
443            Ok(Expr::Await(Box::new(expr), span))
444        }
445        Rule::postfix_expr => {
446            // await expr (simple case)
447            let expr = parse_postfix_expr(first.clone())?;
448            Ok(Expr::Await(Box::new(expr), span))
449        }
450        _ => Err(ShapeError::ParseError {
451            message: format!(
452                "unexpected token in await expression: {:?}",
453                first.as_rule()
454            ),
455            location: Some(pair_loc),
456        }),
457    }
458}
459
460/// Parse join expression: join all|race|any|settle { branch, ... }
461fn parse_join_expr(pair: Pair<Rule>) -> Result<JoinExpr> {
462    let span = pair_span(&pair);
463    let pair_loc = pair_location(&pair);
464    let mut inner = pair.into_inner();
465
466    // Parse join kind
467    let kind_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
468        message: "expected join strategy (all, race, any, settle)".to_string(),
469        location: Some(pair_loc.clone()),
470    })?;
471    let kind = match kind_pair.as_str() {
472        "all" => JoinKind::All,
473        "race" => JoinKind::Race,
474        "any" => JoinKind::Any,
475        "settle" => JoinKind::Settle,
476        other => {
477            return Err(ShapeError::ParseError {
478                message: format!(
479                    "unknown join strategy: '{}'. Expected all, race, any, or settle",
480                    other
481                ),
482                location: Some(pair_location(&kind_pair)),
483            });
484        }
485    };
486
487    // Parse branch list
488    let branch_list_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
489        message: "expected join branches".to_string(),
490        location: Some(pair_loc),
491    })?;
492
493    let mut branches = Vec::new();
494    for branch_pair in branch_list_pair.into_inner() {
495        if branch_pair.as_rule() == Rule::join_branch {
496            branches.push(parse_join_branch(branch_pair)?);
497        }
498    }
499
500    Ok(JoinExpr {
501        kind,
502        branches,
503        span,
504    })
505}
506
507/// Parse a single join branch: [annotations] [label:] expression
508fn parse_join_branch(pair: Pair<Rule>) -> Result<JoinBranch> {
509    let inner_pairs: Vec<_> = pair.into_inner().collect();
510
511    let mut annotations = Vec::new();
512    let mut label = None;
513    let mut expr = None;
514
515    // The grammar alternatives for join_branch produce different inner sequences:
516    // 1. annotation+ ident ":" expression -> annotations, label, expr
517    // 2. annotation+ expression -> annotations, expr
518    // 3. ident ":" expression -> label, expr
519    // 4. expression -> expr
520    //
521    // We can distinguish by checking types in order.
522    let mut i = 0;
523
524    // Collect leading annotations
525    while i < inner_pairs.len() && inner_pairs[i].as_rule() == Rule::annotation {
526        annotations.push(crate::parser::functions::parse_annotation(
527            inner_pairs[i].clone(),
528        )?);
529        i += 1;
530    }
531
532    // Check if next is ident followed by expression (label case)
533    if i < inner_pairs.len() && inner_pairs[i].as_rule() == Rule::ident {
534        // Could be label:expr or just an expression that starts with ident
535        // In the grammar, the ident alternative in join_branch is specifically
536        // `ident ~ ":" ~ expression`, so if we get Rule::ident here followed
537        // by Rule::expression, it's a labeled branch.
538        if i + 1 < inner_pairs.len() && inner_pairs[i + 1].as_rule() == Rule::expression {
539            label = Some(inner_pairs[i].as_str().to_string());
540            i += 1;
541            expr = Some(parse_expression(inner_pairs[i].clone())?);
542        } else {
543            // Shouldn't happen with correct grammar, but handle gracefully
544            expr = Some(parse_expression(inner_pairs[i].clone())?);
545        }
546    } else if i < inner_pairs.len() {
547        expr = Some(parse_expression(inner_pairs[i].clone())?);
548    }
549
550    let expr = expr.ok_or_else(|| ShapeError::ParseError {
551        message: "expected expression in join branch".to_string(),
552        location: None,
553    })?;
554
555    Ok(JoinBranch {
556        label,
557        expr,
558        annotations,
559    })
560}
561
562/// Parse struct literal: TypeName { field: value, ... }
563///
564/// Grammar: `ident ~ "{" ~ object_fields? ~ "}"`
565/// Reuses object_fields parsing (same as object literals and enum struct payloads)
566fn parse_struct_literal(pair: Pair<Rule>) -> Result<Expr> {
567    let span = pair_span(&pair);
568    let pair_loc = pair_location(&pair);
569    let mut inner = pair.into_inner();
570
571    let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
572        message: "expected struct type name".to_string(),
573        location: Some(pair_loc.clone()),
574    })?;
575    let type_name = name_pair.as_str().to_string();
576
577    let mut fields = Vec::new();
578
579    // Parse optional object_fields
580    if let Some(fields_pair) = inner.next() {
581        if fields_pair.as_rule() == Rule::object_fields {
582            for field_item_pair in fields_pair.into_inner() {
583                if field_item_pair.as_rule() != Rule::object_field_item {
584                    continue;
585                }
586                let field_item_loc = pair_location(&field_item_pair);
587                let field_item_inner =
588                    field_item_pair
589                        .into_inner()
590                        .next()
591                        .ok_or_else(|| ShapeError::ParseError {
592                            message: "expected struct field content".to_string(),
593                            location: Some(field_item_loc.clone()),
594                        })?;
595                match field_item_inner.as_rule() {
596                    Rule::object_field => {
597                        let mut field_inner = field_item_inner.into_inner();
598                        let field_kind =
599                            field_inner.next().ok_or_else(|| ShapeError::ParseError {
600                                message: "expected struct field content".to_string(),
601                                location: Some(field_item_loc.clone()),
602                            })?;
603                        match field_kind.as_rule() {
604                            Rule::object_value_field => {
605                                let mut value_inner = field_kind.into_inner();
606                                let key_pair =
607                                    value_inner.next().ok_or_else(|| ShapeError::ParseError {
608                                        message: "expected struct field key".to_string(),
609                                        location: Some(field_item_loc.clone()),
610                                    })?;
611                                let key_pair = if key_pair.as_rule() == Rule::object_field_name {
612                                    key_pair.into_inner().next().ok_or_else(|| {
613                                        ShapeError::ParseError {
614                                            message: "expected struct field key".to_string(),
615                                            location: Some(field_item_loc.clone()),
616                                        }
617                                    })?
618                                } else {
619                                    key_pair
620                                };
621                                let key = key_pair.as_str().to_string();
622                                let value_pair =
623                                    value_inner.next().ok_or_else(|| ShapeError::ParseError {
624                                        message: format!(
625                                            "expected value for struct field '{}'",
626                                            key
627                                        ),
628                                        location: Some(field_item_loc),
629                                    })?;
630                                let value = parse_expression(value_pair)?;
631                                fields.push((key, value));
632                            }
633                            _ => {
634                                return Err(ShapeError::ParseError {
635                                    message: "typed fields and spreads are not supported in struct literals".to_string(),
636                                    location: Some(field_item_loc),
637                                });
638                            }
639                        }
640                    }
641                    Rule::object_spread => {
642                        return Err(ShapeError::ParseError {
643                            message: "spread is not supported in struct literals".to_string(),
644                            location: Some(field_item_loc),
645                        });
646                    }
647                    _ => {}
648                }
649            }
650        }
651    }
652
653    Ok(Expr::StructLiteral {
654        type_name,
655        fields,
656        span,
657    })
658}
659
660fn parse_enum_constructor_expr(pair: Pair<Rule>) -> Result<Expr> {
661    let span = pair_span(&pair);
662    let pair_loc = pair_location(&pair);
663    let mut inner = pair.into_inner();
664
665    let path_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
666        message: "expected enum variant path".to_string(),
667        location: Some(pair_loc.clone()),
668    })?;
669    let (enum_name, variant) = parse_enum_variant_path(path_pair)?;
670
671    let payload = if let Some(payload_pair) = inner.next() {
672        match payload_pair.as_rule() {
673            Rule::enum_tuple_payload => {
674                let (args, named_args) = super::functions::parse_arg_list(payload_pair)?;
675                if !named_args.is_empty() {
676                    return Err(ShapeError::ParseError {
677                        message: "named arguments are not allowed in enum tuple constructors"
678                            .to_string(),
679                        location: Some(pair_loc),
680                    });
681                }
682                EnumConstructorPayload::Tuple(args)
683            }
684            Rule::enum_struct_payload => {
685                let fields = parse_enum_struct_payload(payload_pair)?;
686                EnumConstructorPayload::Struct(fields)
687            }
688            other => {
689                return Err(ShapeError::ParseError {
690                    message: format!("unexpected enum constructor payload: {:?}", other),
691                    location: Some(pair_loc),
692                });
693            }
694        }
695    } else {
696        EnumConstructorPayload::Unit
697    };
698
699    Ok(Expr::EnumConstructor {
700        enum_name,
701        variant,
702        payload,
703        span,
704    })
705}
706
707fn parse_enum_variant_path(pair: Pair<Rule>) -> Result<(String, String)> {
708    let pair_loc = pair_location(&pair);
709    let mut inner = pair.into_inner();
710    let enum_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
711        message: "expected enum name".to_string(),
712        location: Some(pair_loc.clone()),
713    })?;
714    let variant_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
715        message: "expected enum variant name".to_string(),
716        location: Some(pair_loc),
717    })?;
718    Ok((
719        enum_pair.as_str().to_string(),
720        variant_pair.as_str().to_string(),
721    ))
722}
723
724fn parse_enum_struct_payload(pair: Pair<Rule>) -> Result<Vec<(String, Expr)>> {
725    let mut fields = Vec::new();
726
727    for inner in pair.into_inner() {
728        if inner.as_rule() != Rule::object_fields {
729            continue;
730        }
731        for field_item_pair in inner.into_inner() {
732            if field_item_pair.as_rule() != Rule::object_field_item {
733                continue;
734            }
735            let field_item_loc = pair_location(&field_item_pair);
736            let field_item_inner =
737                field_item_pair
738                    .into_inner()
739                    .next()
740                    .ok_or_else(|| ShapeError::ParseError {
741                        message: "expected enum struct field content".to_string(),
742                        location: Some(field_item_loc.clone()),
743                    })?;
744            match field_item_inner.as_rule() {
745                Rule::object_field => {
746                    let mut field_inner = field_item_inner.into_inner();
747                    let field_kind = field_inner.next().ok_or_else(|| ShapeError::ParseError {
748                        message: "expected enum struct field content".to_string(),
749                        location: Some(field_item_loc.clone()),
750                    })?;
751                    match field_kind.as_rule() {
752                        Rule::object_value_field => {
753                            let mut value_inner = field_kind.into_inner();
754                            let key_pair =
755                                value_inner.next().ok_or_else(|| ShapeError::ParseError {
756                                    message: "expected enum struct field key".to_string(),
757                                    location: Some(field_item_loc.clone()),
758                                })?;
759                            let key_pair = if key_pair.as_rule() == Rule::object_field_name {
760                                key_pair.into_inner().next().ok_or_else(|| {
761                                    ShapeError::ParseError {
762                                        message: "expected enum struct field key".to_string(),
763                                        location: Some(field_item_loc.clone()),
764                                    }
765                                })?
766                            } else {
767                                key_pair
768                            };
769                            let key = match key_pair.as_rule() {
770                                Rule::ident | Rule::keyword => key_pair.as_str().to_string(),
771                                _ => {
772                                    return Err(ShapeError::ParseError {
773                                        message: "unexpected enum struct field key".to_string(),
774                                        location: Some(pair_location(&key_pair)),
775                                    });
776                                }
777                            };
778                            let value_pair =
779                                value_inner.next().ok_or_else(|| ShapeError::ParseError {
780                                    message: format!("expected value for enum field '{}'", key),
781                                    location: Some(field_item_loc),
782                                })?;
783                            let value = parse_expression(value_pair)?;
784                            fields.push((key, value));
785                        }
786                        Rule::object_typed_field => {
787                            return Err(ShapeError::ParseError {
788                                message: "typed fields are not allowed in enum constructors"
789                                    .to_string(),
790                                location: Some(field_item_loc),
791                            });
792                        }
793                        other => {
794                            return Err(ShapeError::ParseError {
795                                message: format!("unexpected enum field kind: {:?}", other),
796                                location: Some(field_item_loc),
797                            });
798                        }
799                    }
800                }
801                Rule::object_spread => {
802                    return Err(ShapeError::ParseError {
803                        message: "spread fields are not allowed in enum constructors".to_string(),
804                        location: Some(field_item_loc),
805                    });
806                }
807                other => {
808                    return Err(ShapeError::ParseError {
809                        message: format!("unexpected enum struct field: {:?}", other),
810                        location: Some(field_item_loc),
811                    });
812                }
813            }
814        }
815    }
816
817    Ok(fields)
818}
819
820/// Parse comptime block: `comptime { stmts }`
821///
822/// Grammar: `comptime_block = { "comptime" ~ block_expr }`
823/// Produces `Expr::Comptime(Vec<Statement>, Span)`.
824fn parse_comptime_block(pair: Pair<Rule>) -> Result<Expr> {
825    let span = pair_span(&pair);
826    let pair_loc = pair_location(&pair);
827    let block_pair = pair
828        .into_inner()
829        .next()
830        .ok_or_else(|| ShapeError::ParseError {
831            message: "expected block after 'comptime'".to_string(),
832            location: Some(pair_loc),
833        })?;
834
835    // Parse the block_expr into an Expr::Block, then extract statements
836    let block_expr = super::control_flow::parse_block_expr(block_pair)?;
837    let stmts = block_items_to_statements(block_expr, span);
838    Ok(Expr::Comptime(stmts, span))
839}
840
841/// Parse comptime for expression: `comptime for field in target.fields { stmts }`
842///
843/// Grammar: `comptime_for_expr = { "comptime" ~ "for" ~ ident ~ "in" ~ postfix_expr ~ "{" ~ statement* ~ "}" }`
844/// Produces `Expr::ComptimeFor(ComptimeForExpr, Span)`.
845fn parse_comptime_for_expr(pair: Pair<Rule>) -> Result<Expr> {
846    use crate::ast::expr_helpers::ComptimeForExpr;
847    use crate::parser::statements::parse_statement;
848
849    let span = pair_span(&pair);
850    let pair_loc = pair_location(&pair);
851    let mut inner = pair.into_inner();
852
853    // Parse loop variable name
854    let var_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
855        message: "expected loop variable in comptime for".to_string(),
856        location: Some(pair_loc.clone()),
857    })?;
858    let variable = var_pair.as_str().to_string();
859
860    // Parse iterable expression (postfix_expr in grammar)
861    let iter_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
862        message: "expected iterable expression in comptime for".to_string(),
863        location: Some(pair_loc),
864    })?;
865    let iterable = parse_postfix_expr(iter_pair)?;
866
867    // Parse body statements
868    let mut body = Vec::new();
869    for stmt_pair in inner {
870        if stmt_pair.as_rule() == Rule::statement {
871            body.push(parse_statement(stmt_pair)?);
872        }
873    }
874
875    Ok(Expr::ComptimeFor(
876        Box::new(ComptimeForExpr {
877            variable,
878            iterable: Box::new(iterable),
879            body,
880        }),
881        span,
882    ))
883}
884
885/// Convert a Block expression (from block_expr parsing) into a Vec<Statement>.
886/// The last expression in a block becomes a return statement so the comptime
887/// mini-VM can capture the result.
888pub(crate) fn block_items_to_statements(
889    block_expr: Expr,
890    span: Span,
891) -> Vec<crate::ast::Statement> {
892    use crate::ast::{BlockItem, Statement};
893
894    let items = match block_expr {
895        Expr::Block(block, _) => block.items,
896        // If it's a single expression (empty block rules can produce this), wrap it
897        other => return vec![Statement::Return(Some(other), span)],
898    };
899
900    let mut stmts = Vec::new();
901    let len = items.len();
902    for (i, item) in items.into_iter().enumerate() {
903        let is_last = i == len - 1;
904        match item {
905            BlockItem::VariableDecl(decl) => {
906                stmts.push(Statement::VariableDecl(decl, span));
907            }
908            BlockItem::Assignment(assign) => {
909                stmts.push(Statement::Assignment(assign, span));
910            }
911            BlockItem::Statement(stmt) => {
912                stmts.push(stmt);
913            }
914            BlockItem::Expression(expr) => {
915                if is_last {
916                    // Last expression becomes the return value
917                    stmts.push(Statement::Return(Some(expr), span));
918                } else {
919                    stmts.push(Statement::Expression(expr, span));
920                }
921            }
922        }
923    }
924    stmts
925}
926
927/// Parse annotated expression: `@annotation expr`
928///
929/// Grammar: `annotated_expr = { annotation+ ~ postfix_expr }`
930/// Produces nested `Expr::Annotated { annotation, target, span }`.
931/// Multiple annotations nest left-to-right:
932/// `@retry(3) @timeout(5s) fetch()` becomes
933/// `Annotated { @retry(3), target: Annotated { @timeout(5s), target: fetch() } }`
934fn parse_annotated_expr(pair: Pair<Rule>) -> Result<Expr> {
935    let span = pair_span(&pair);
936    let pair_loc = pair_location(&pair);
937    let inner_pairs: Vec<_> = pair.into_inner().collect();
938
939    if inner_pairs.is_empty() {
940        return Err(ShapeError::ParseError {
941            message: "expected annotations and expression".to_string(),
942            location: Some(pair_loc),
943        });
944    }
945
946    // Collect annotations (all but the last pair which is the target expression)
947    let mut annotations = Vec::new();
948    let mut target_pair = None;
949    for p in &inner_pairs {
950        match p.as_rule() {
951            Rule::annotation => {
952                annotations.push(crate::parser::functions::parse_annotation(p.clone())?);
953            }
954            _ => {
955                target_pair = Some(p.clone());
956            }
957        }
958    }
959
960    let target_pair = target_pair.ok_or_else(|| ShapeError::ParseError {
961        message: "expected expression after annotations".to_string(),
962        location: Some(pair_loc),
963    })?;
964    let target = parse_postfix_expr(target_pair)?;
965
966    // Wrap the target in nested Annotated expressions (right-to-left)
967    // @a @b expr -> Annotated(@a, Annotated(@b, expr))
968    let mut result = target;
969    for annotation in annotations.into_iter().rev() {
970        result = Expr::Annotated {
971            annotation,
972            target: Box::new(result),
973            span,
974        };
975    }
976
977    Ok(result)
978}
979
980// Note: backtest { } block syntax has been removed.
981// Use backtest(strategy, { config }) function instead.