Skip to main content

mimium_lang/compiler/parser/
lower.rs

1#![allow(dead_code)]
2
3use crate::ast::operators::Op;
4use crate::ast::program::{Program, ProgramStatement, QualifiedPath, UseTarget, Visibility};
5use crate::ast::statement::{Statement, into_then_expr, stmt_from_expr_top};
6use crate::ast::{Expr, Literal};
7use crate::compiler::parser::cst_parser::ParserError;
8use crate::compiler::parser::green::{GreenNode, GreenNodeArena, GreenNodeId, SyntaxKind};
9use crate::compiler::parser::token::{Token, TokenKind};
10use crate::interner::{ExprNodeId, Symbol, ToSymbol};
11use crate::pattern::{Pattern, TypedId, TypedPattern};
12use crate::types::Type;
13use crate::utils::metadata::{Location, Span};
14use std::ops::ControlFlow;
15use std::path::PathBuf;
16
17/// CST -> existing `Program`/`Expr` lowering.
18/// This is an initial, incomplete bridge used to incrementally replace
19/// the legacy chumsky parser. It intentionally handles a subset of
20/// syntax; unsupported constructs currently lower to `Expr::Error` to
21/// keep the compiler stable while we flesh out coverage.
22pub struct Lowerer<'a> {
23    source: &'a str,
24    tokens: &'a [Token],
25    arena: &'a GreenNodeArena,
26    file_path: PathBuf,
27}
28
29impl<'a> Lowerer<'a> {
30    pub fn new(
31        source: &'a str,
32        tokens: &'a [Token],
33        arena: &'a GreenNodeArena,
34        file_path: PathBuf,
35    ) -> Self {
36        Self {
37            source,
38            tokens,
39            arena,
40            file_path,
41        }
42    }
43
44    /// Recursively unwrap `Expr::Paren` nodes, returning the innermost expression.
45    fn unwrap_paren(expr: ExprNodeId) -> ExprNodeId {
46        match expr.to_expr() {
47            Expr::Paren(inner) => Self::unwrap_paren(inner),
48            _ => expr,
49        }
50    }
51
52    /// Lower a full program node into the existing `Program` structure.
53    pub fn lower_program(&self, root: GreenNodeId) -> Program {
54        let (mut program_statements, pending_statements, pending_span) = self
55            .arena
56            .children(root)
57            .map_or((Vec::new(), Vec::new(), 0..0), |children| {
58                children
59                    .iter()
60                    .copied()
61                    .filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
62                    .filter_map(|child| self.lower_statement(child))
63                    .fold(
64                        (Vec::new(), Vec::new(), 0..0),
65                        |(mut program_statements, mut pending_statements, mut pending_span),
66                         (stmt, span)| {
67                            match &stmt {
68                                ProgramStatement::GlobalStatement(Statement::Single(expr)) => {
69                                    // Collect global single statements for potential Then-chaining
70                                    let stmts = stmt_from_expr_top(*expr);
71                                    let new_pending = stmts
72                                        .into_iter()
73                                        .map(|s| (s, self.location_from_span(span.clone())));
74                                    pending_statements.extend(new_pending);
75                                    pending_span = span;
76                                }
77                                _ => {
78                                    // Non-global statement: flush pending, then add this
79                                    if !pending_statements.is_empty() {
80                                        if let Some(merged_expr) =
81                                            into_then_expr(&pending_statements)
82                                        {
83                                            program_statements.push((
84                                                ProgramStatement::GlobalStatement(
85                                                    Statement::Single(merged_expr),
86                                                ),
87                                                pending_span.clone(),
88                                            ));
89                                        }
90                                        pending_statements.clear();
91                                    }
92                                    program_statements.push((stmt, span));
93                                }
94                            }
95                            (program_statements, pending_statements, pending_span)
96                        },
97                    )
98            });
99
100        // Flush any remaining pending statements
101        if !pending_statements.is_empty()
102            && let Some(merged_expr) = into_then_expr(&pending_statements)
103        {
104            program_statements.push((
105                ProgramStatement::GlobalStatement(Statement::Single(merged_expr)),
106                pending_span.clone(),
107            ));
108        }
109
110        Program {
111            statements: program_statements,
112        }
113    }
114
115    /// Lower a statement node. Unknown forms become `Statement::Error`.
116    fn lower_statement(&self, node: GreenNodeId) -> Option<(ProgramStatement, Span)> {
117        let span = self.node_span(node)?;
118        let stmt = match self.arena.kind(node) {
119            Some(SyntaxKind::Statement) => {
120                // Check for visibility modifier
121                let visibility = self.extract_visibility(node);
122
123                // Inspect children to find the first meaningful node.
124                let (inner_kind, inner_id) = self
125                    .arena
126                    .children(node)
127                    .and_then(|children| {
128                        children.iter().copied().find_map(|child| {
129                            let kind = self.arena.kind(child)?;
130                            // Skip VisibilityPub when looking for the main statement kind
131                            if kind == SyntaxKind::VisibilityPub {
132                                None
133                            } else {
134                                Some((kind, child))
135                            }
136                        })
137                    })
138                    .map(|(kind, id)| (Some(kind), Some(id)))
139                    .unwrap_or((None, None));
140
141                match (inner_kind, inner_id) {
142                    (Some(SyntaxKind::FunctionDecl), Some(id)) => self
143                        .lower_function_decl(id, visibility)
144                        .unwrap_or(ProgramStatement::Error),
145                    (Some(SyntaxKind::LetDecl), Some(id)) => {
146                        self.lower_let_decl(id).unwrap_or(ProgramStatement::Error)
147                    }
148                    (Some(SyntaxKind::LetRecDecl), Some(id)) => self
149                        .lower_letrec_decl(id)
150                        .unwrap_or(ProgramStatement::Error),
151                    (Some(SyntaxKind::IncludeStmt), Some(id)) => {
152                        self.lower_include(id).unwrap_or(ProgramStatement::Error)
153                    }
154                    (Some(SyntaxKind::StageDecl), Some(id)) => {
155                        self.lower_stage_decl(id).unwrap_or(ProgramStatement::Error)
156                    }
157                    (Some(SyntaxKind::ModuleDecl), Some(id)) => self
158                        .lower_module_decl(id, visibility)
159                        .unwrap_or(ProgramStatement::Error),
160                    (Some(SyntaxKind::UseStmt), Some(id)) => self
161                        .lower_use_stmt(id, visibility)
162                        .unwrap_or(ProgramStatement::Error),
163                    (Some(SyntaxKind::TypeDecl), Some(id)) => self
164                        .lower_type_decl(id, visibility)
165                        .unwrap_or(ProgramStatement::Error),
166                    (Some(_), _) => {
167                        let expr_nodes = self.collect_expr_nodes(node);
168                        let expr = self.lower_expr_sequence(&expr_nodes);
169                        ProgramStatement::GlobalStatement(Statement::Single(expr))
170                    }
171                    _ => ProgramStatement::Error,
172                }
173            }
174            _ => ProgramStatement::Error,
175        };
176
177        Some((stmt, span))
178    }
179
180    /// Extract visibility modifier from a statement node
181    fn extract_visibility(&self, node: GreenNodeId) -> Visibility {
182        self.arena
183            .children(node)
184            .and_then(|children| {
185                children
186                    .iter()
187                    .find(|child| self.arena.kind(**child) == Some(SyntaxKind::VisibilityPub))
188            })
189            .map(|_| Visibility::Public)
190            .unwrap_or(Visibility::Private)
191    }
192
193    /// Lower module declaration: mod name { ... } or mod name;
194    fn lower_module_decl(
195        &self,
196        node: GreenNodeId,
197        visibility: Visibility,
198    ) -> Option<ProgramStatement> {
199        let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
200        let name = self.token_text(name_idx)?.to_symbol();
201
202        // Check if module has a body (inline) or not (external file)
203        let has_block = self
204            .find_token(node, |kind| matches!(kind, TokenKind::BlockBegin))
205            .is_some();
206
207        let body = if has_block {
208            // Inline module: lower the body statements
209            let stmts: Vec<(ProgramStatement, Span)> = self
210                .arena
211                .children(node)
212                .map(|children| {
213                    children
214                        .iter()
215                        .copied()
216                        .filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
217                        .filter_map(|child| self.lower_statement(child))
218                        .collect()
219                })
220                .unwrap_or_default();
221            Some(stmts)
222        } else {
223            // External file module: body is None
224            None
225        };
226
227        Some(ProgramStatement::ModuleDefinition {
228            visibility,
229            name,
230            body,
231        })
232    }
233
234    /// Lower use statement:
235    /// - `use path::to::module` (single import)
236    /// - `use path::{a, b, c}` (multiple imports)
237    /// - `use path::*` (wildcard import)
238    /// - `pub use ...` (re-export)
239    fn lower_use_stmt(
240        &self,
241        node: GreenNodeId,
242        visibility: Visibility,
243    ) -> Option<ProgramStatement> {
244        let path_node = self.find_child(node, |kind| kind == SyntaxKind::QualifiedPath)?;
245
246        // Check for wildcard or multiple import targets within the QualifiedPath
247        let (path, target) = self.lower_use_path(path_node)?;
248
249        Some(ProgramStatement::UseStatement {
250            visibility,
251            path,
252            target,
253        })
254    }
255
256    /// Lower use path, extracting the base path and target type
257    fn lower_use_path(&self, node: GreenNodeId) -> Option<(QualifiedPath, UseTarget)> {
258        use crate::ast::program::UseTarget;
259
260        let mut segments: Vec<Symbol> = Vec::new();
261        let mut target = UseTarget::Single;
262
263        if let Some(children) = self.arena.children(node) {
264            for child in children.iter() {
265                let child_kind = self.arena.kind(*child);
266
267                match child_kind {
268                    Some(SyntaxKind::UseTargetWildcard) => {
269                        target = UseTarget::Wildcard;
270                    }
271                    Some(SyntaxKind::UseTargetMultiple) => {
272                        // Extract identifiers from the multiple import block
273                        let symbols = self.lower_use_target_multiple(*child);
274                        target = UseTarget::Multiple(symbols);
275                    }
276                    _ => {
277                        // Check if this is an identifier token
278                        if let Some(token_idx) = self.get_token_index(*child)
279                            && let Some(token) = self.tokens.get(token_idx)
280                            && token.kind == TokenKind::Ident
281                            && let Some(text) = self.token_text(token_idx)
282                        {
283                            segments.push(text.to_symbol());
284                        }
285                    }
286                }
287            }
288        }
289
290        if segments.is_empty() && matches!(target, UseTarget::Single) {
291            None
292        } else {
293            Some((QualifiedPath::new(segments), target))
294        }
295    }
296
297    /// Extract identifiers from a UseTargetMultiple node: {a, b, c}
298    fn lower_use_target_multiple(&self, node: GreenNodeId) -> Vec<Symbol> {
299        let mut symbols = Vec::new();
300
301        if let Some(children) = self.arena.children(node) {
302            for child in children.iter() {
303                if let Some(token_idx) = self.get_token_index(*child)
304                    && let Some(token) = self.tokens.get(token_idx)
305                    && token.kind == TokenKind::Ident
306                    && let Some(text) = self.token_text(token_idx)
307                {
308                    symbols.push(text.to_symbol());
309                }
310            }
311        }
312
313        symbols
314    }
315
316    /// Lower qualified path: ident (:: ident)*
317    fn lower_qualified_path(&self, node: GreenNodeId) -> Option<QualifiedPath> {
318        let segments: Vec<Symbol> = self
319            .arena
320            .children(node)
321            .map(|children| {
322                children
323                    .iter()
324                    .filter_map(|child| {
325                        if let Some(token_idx) = self.get_token_index(*child)
326                            && let Some(token) = self.tokens.get(token_idx)
327                            && token.kind == TokenKind::Ident
328                        {
329                            self.token_text(token_idx).map(|s| s.to_symbol())
330                        } else {
331                            None
332                        }
333                    })
334                    .collect()
335            })
336            .unwrap_or_default();
337
338        if segments.is_empty() {
339            None
340        } else {
341            Some(QualifiedPath::new(segments))
342        }
343    }
344
345    /// Lower type declaration or type alias
346    /// Type alias: type Name = BaseType
347    /// Type declaration: type Name = Variant1 | Variant2(Type) | ...
348    fn lower_type_decl(
349        &self,
350        node: GreenNodeId,
351        visibility: Visibility,
352    ) -> Option<ProgramStatement> {
353        use crate::ast::program::VariantDef;
354
355        // Check if this is a recursive type declaration (has 'rec' keyword)
356        let is_recursive = self
357            .find_token(node, |kind| matches!(kind, TokenKind::Rec))
358            .is_some();
359
360        // Find the type name (first Ident token after 'type' keyword)
361        let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
362        let name = self.token_text(name_idx)?.to_symbol();
363
364        // Check if this is a type alias (has a type but no variant definitions)
365        // or a variant declaration (has variant definitions)
366        let variants: Vec<VariantDef> = self
367            .arena
368            .children(node)
369            .map(|children| {
370                children
371                    .iter()
372                    .copied()
373                    .filter(|child| self.arena.kind(*child) == Some(SyntaxKind::VariantDef))
374                    .filter_map(|child| self.lower_variant_def(child))
375                    .collect()
376            })
377            .unwrap_or_default();
378
379        if variants.is_empty() {
380            // This is a type alias - look for a type annotation
381            let target_type = self
382                .arena
383                .children(node)
384                .and_then(|children| {
385                    children
386                        .iter()
387                        .find(|c| {
388                            self.arena
389                                .kind(**c)
390                                .map(Self::is_type_kind)
391                                .unwrap_or(false)
392                        })
393                        .copied()
394                })
395                .map(|type_node| self.lower_type(type_node))?;
396
397            Some(ProgramStatement::TypeAlias {
398                visibility,
399                name,
400                target_type,
401            })
402        } else {
403            // This is a variant declaration
404            Some(ProgramStatement::TypeDeclaration {
405                visibility,
406                name,
407                variants,
408                is_recursive,
409            })
410        }
411    }
412
413    /// Lower a single variant definition: Name or Name(Type)
414    fn lower_variant_def(&self, node: GreenNodeId) -> Option<crate::ast::program::VariantDef> {
415        use crate::ast::program::VariantDef;
416
417        // Variant name is the first Ident token
418        let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
419        let name = self.token_text(name_idx)?.to_symbol();
420
421        // Check for optional payload type (any type node inside the VariantDef)
422        let payload = self.arena.children(node).and_then(|children| {
423            let type_nodes: Vec<_> = children
424                .iter()
425                .filter(|c| {
426                    self.arena
427                        .kind(**c)
428                        .map(Self::is_type_kind)
429                        .unwrap_or(false)
430                })
431                .collect();
432
433            match type_nodes.len() {
434                0 => None,
435                1 => {
436                    // Single type - use as-is
437                    Some(self.lower_type(*type_nodes[0]))
438                }
439                _ => {
440                    // Multiple types - treat as tuple (for Rectangle(float, float) syntax)
441                    let elem_types: Vec<_> = type_nodes
442                        .iter()
443                        .map(|node| self.lower_type(**node))
444                        .collect();
445
446                    // Use default location for implicit tuple
447                    let loc = Location::default();
448                    Some(Type::Tuple(elem_types).into_id_with_location(loc))
449                }
450            }
451        });
452
453        Some(VariantDef::new(name, payload))
454    }
455
456    fn lower_let_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
457        let pattern_node = self.find_child(node, Self::is_pattern_kind)?;
458        let (pat, pat_span) = self.lower_pattern(pattern_node)?;
459
460        // Check for type annotation
461        let type_annotation = if let Some(type_anno_node) =
462            self.find_child(node, |kind| kind == SyntaxKind::TypeAnnotation)
463        {
464            // Find the actual type node within the TypeAnnotation
465            if let Some(type_children) = self.arena.children(type_anno_node) {
466                type_children
467                    .iter()
468                    .find(|c| {
469                        self.arena
470                            .kind(**c)
471                            .map(Self::is_type_kind)
472                            .unwrap_or(false)
473                    })
474                    .map(|type_node| self.lower_type(*type_node))
475            } else {
476                None
477            }
478        } else {
479            None
480        };
481
482        let expr_nodes = self.collect_expr_nodes_after(node, pattern_node);
483        let value = self.lower_expr_sequence(&expr_nodes);
484        let loc = self.location_from_span(pat_span.clone());
485
486        let ty =
487            type_annotation.unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
488        let typed = TypedPattern::new(pat, ty);
489
490        Some(ProgramStatement::GlobalStatement(Statement::Let(
491            typed, value,
492        )))
493    }
494
495    fn lower_function_decl(
496        &self,
497        node: GreenNodeId,
498        visibility: Visibility,
499    ) -> Option<ProgramStatement> {
500        let name_idx = self.find_token(node, |kind| {
501            matches!(kind, TokenKind::IdentFunction | TokenKind::Ident)
502        })?;
503        let name = self.token_text(name_idx)?.to_symbol();
504
505        let (params, params_span) = self
506            .find_child(node, |kind| kind == SyntaxKind::ParamList)
507            .map(|id| self.lower_param_list(id))
508            .unwrap_or_else(|| (Vec::new(), self.node_span(node).unwrap_or(0..0)));
509
510        // Check for return type annotation
511        let return_type = self.arena.children(node).and_then(|children| {
512            children
513                .iter()
514                .find_map(|child| match self.arena.kind(*child) {
515                    Some(SyntaxKind::TypeAnnotation) => {
516                        self.arena.children(*child).and_then(|type_children| {
517                            type_children
518                                .iter()
519                                .find(|c| {
520                                    self.arena
521                                        .kind(**c)
522                                        .map(Self::is_type_kind)
523                                        .unwrap_or(false)
524                                })
525                                .map(|type_node| self.lower_type(*type_node))
526                        })
527                    }
528                    Some(kind) if Self::is_type_kind(kind) => Some(self.lower_type(*child)),
529                    _ => None,
530                })
531        });
532
533        let body_node = self
534            .find_child(node, |kind| kind == SyntaxKind::BlockExpr)
535            .or_else(|| self.find_child(node, Self::is_expr_kind))?;
536        let body = if self.arena.kind(body_node) == Some(SyntaxKind::BlockExpr) {
537            // Flatten block body to then-expression for function definitions
538            let stmts = self.lower_block_statements(body_node);
539            into_then_expr(&stmts).unwrap_or_else(|| Expr::Error.into_id_without_span())
540        } else {
541            self.lower_expr(body_node)
542        };
543
544        let arg_loc = self.location_from_span(params_span.clone());
545        Some(ProgramStatement::FnDefinition {
546            visibility,
547            name,
548            args: (params, arg_loc),
549            return_type,
550            body,
551        })
552    }
553
554    fn lower_letrec_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
555        let ident_token = self.find_token(node, |kind| matches!(kind, TokenKind::Ident))?;
556        let expr_nodes = self.collect_expr_nodes(node);
557
558        let name = self.token_text(ident_token)?.to_symbol();
559        let span = self.node_span(node)?;
560        let loc = self.location_from_span(span.clone());
561        let ty = Type::Unknown.into_id_with_location(loc.clone());
562        let id = TypedId::new(name, ty);
563        let value = self.lower_expr_sequence(&expr_nodes);
564        Some(ProgramStatement::GlobalStatement(Statement::LetRec(
565            id, value,
566        )))
567    }
568
569    fn lower_include(&self, node: GreenNodeId) -> Option<ProgramStatement> {
570        let string_token = self.find_token(node, |kind| matches!(kind, TokenKind::Str))?;
571        let raw = self.token_text(string_token)?;
572        // Keep the original quoted content and strip quotes for the symbol.
573        let content = raw.trim_matches('"').to_symbol();
574        Some(ProgramStatement::Import(content))
575    }
576
577    fn lower_stage_decl(&self, node: GreenNodeId) -> Option<ProgramStatement> {
578        use crate::ast::StageKind;
579        let stage_token = self.find_token(node, |kind| {
580            matches!(kind, TokenKind::Main | TokenKind::Macro)
581        })?;
582        let stage_kind = match self.tokens.get(stage_token)?.kind {
583            TokenKind::Main => StageKind::Main,
584            TokenKind::Macro => StageKind::Macro,
585            _ => StageKind::Main,
586        };
587        Some(ProgramStatement::StageDeclaration { stage: stage_kind })
588    }
589
590    /// Lower expressions; unsupported constructs become `Expr::Error`.
591    fn lower_expr(&self, node: GreenNodeId) -> ExprNodeId {
592        let loc = self
593            .node_span(node)
594            .map(|span| self.location_from_span(span))
595            .unwrap_or_default();
596
597        match self.arena.kind(node) {
598            Some(SyntaxKind::IntLiteral) => {
599                let text = self.text_of_first_token(node).unwrap_or("0");
600                Expr::Literal(Literal::Float(text.to_symbol())).into_id(loc)
601            }
602            Some(SyntaxKind::FloatLiteral) => {
603                let text = self.text_of_first_token(node).unwrap_or("0.0");
604                Expr::Literal(Literal::Float(text.to_symbol())).into_id(loc)
605            }
606            Some(SyntaxKind::StringLiteral) => {
607                let text = self.text_of_first_token(node).unwrap_or("\"");
608                Expr::Literal(Literal::String(text.trim_matches('"').to_symbol())).into_id(loc)
609            }
610            Some(SyntaxKind::SelfLiteral) => Expr::Literal(Literal::SelfLit).into_id(loc),
611            Some(SyntaxKind::NowLiteral) => Expr::Literal(Literal::Now).into_id(loc),
612            Some(SyntaxKind::SampleRateLiteral) => Expr::Literal(Literal::SampleRate).into_id(loc),
613            Some(SyntaxKind::PlaceHolderLiteral) => {
614                Expr::Literal(Literal::PlaceHolder).into_id(loc)
615            }
616            Some(SyntaxKind::Identifier) => {
617                let text = self.text_of_first_token(node).unwrap_or("");
618                Expr::Var(text.to_symbol()).into_id(loc)
619            }
620            Some(SyntaxKind::QualifiedPath) => {
621                // Qualified path in expression context: modA::funcB
622                if let Some(path) = self.lower_qualified_path(node) {
623                    // If it's a single-segment path, treat it as a simple Var
624                    if path.segments.len() == 1 {
625                        Expr::Var(path.segments[0]).into_id(loc)
626                    } else {
627                        Expr::QualifiedVar(path).into_id(loc)
628                    }
629                } else {
630                    Expr::Error.into_id(loc)
631                }
632            }
633            Some(SyntaxKind::TupleExpr) => {
634                let elems = self.lower_expr_list(node);
635                Expr::Tuple(elems).into_id(loc)
636            }
637            Some(SyntaxKind::ArrayExpr) => {
638                let elems = self.lower_expr_list(node);
639                Expr::ArrayLiteral(elems).into_id(loc)
640            }
641            Some(SyntaxKind::RecordExpr) => {
642                if self
643                    .find_token(node, |kind| kind == TokenKind::LeftArrow)
644                    .is_some()
645                {
646                    let base = self
647                        .child_exprs(node)
648                        .into_iter()
649                        .next()
650                        .map(|expr_node| self.lower_expr(expr_node))
651                        .unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
652                    let fields = self.lower_record_fields(node);
653                    Expr::RecordUpdate(base, fields).into_id(loc)
654                } else {
655                    let fields = self.lower_record_fields(node);
656                    if self
657                        .find_token(node, |kind| kind == TokenKind::DoubleDot)
658                        .is_some()
659                    {
660                        Expr::ImcompleteRecord(fields).into_id(loc)
661                    } else {
662                        Expr::RecordLiteral(fields).into_id(loc)
663                    }
664                }
665            }
666            Some(SyntaxKind::IfExpr) => {
667                let expr_children = self.child_exprs(node);
668                let raw_cond = expr_children
669                    .first()
670                    .map(|&id| self.lower_expr(id))
671                    .unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
672                // Unwrap parens around condition to match legacy AST
673                let cond = match raw_cond.to_expr() {
674                    Expr::Paren(inner) => inner,
675                    _ => raw_cond,
676                };
677                let then_expr = expr_children
678                    .get(1)
679                    .map(|&id| self.lower_expr(id))
680                    .unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
681                let else_expr = expr_children.get(2).map(|&id| self.lower_expr(id));
682                Expr::If(cond, then_expr, else_expr).into_id(loc)
683            }
684            Some(SyntaxKind::MatchExpr) => self.lower_match_expr(node, loc),
685            Some(SyntaxKind::BlockExpr) => {
686                let stmts = self.lower_block_statements(node);
687                Expr::Block(into_then_expr(&stmts)).into_id(loc)
688            }
689            Some(SyntaxKind::LambdaExpr) => {
690                let (params, body) = self.lower_lambda(node);
691                Expr::Lambda(params, None, body).into_id(loc)
692            }
693            Some(SyntaxKind::UnaryExpr) => {
694                let op = self.extract_unary_op(node).unwrap_or(Op::Minus);
695                let rhs_nodes = self.child_exprs(node);
696                let rhs = if rhs_nodes.is_empty() {
697                    Expr::Error.into_id(loc.clone())
698                } else {
699                    self.lower_expr_sequence(&rhs_nodes)
700                };
701                Expr::UniOp((op, loc.span.clone()), rhs).into_id(loc)
702            }
703            Some(SyntaxKind::ParenExpr) => {
704                let inner_nodes = self.child_exprs(node);
705                if inner_nodes.is_empty() {
706                    Expr::Error.into_id(loc.clone())
707                } else {
708                    // Unwrap parenthesized expressions directly
709                    self.lower_expr_sequence(&inner_nodes)
710                }
711            }
712            Some(SyntaxKind::MacroExpansion) => {
713                let (callee, args) = self.lower_macro_expand(node);
714                Expr::MacroExpand(callee, args).into_id(loc)
715            }
716            Some(SyntaxKind::BinaryExpr) => self.lower_binary(node),
717            Some(SyntaxKind::CallExpr) => self.lower_call(node),
718            Some(SyntaxKind::FieldAccess) => self.lower_field_access(node),
719            Some(SyntaxKind::IndexExpr) => self.lower_index(node),
720            Some(SyntaxKind::AssignExpr) => {
721                // AssignExpr still needs left-hand context from sequence
722                Expr::Error.into_id(loc)
723            }
724            Some(SyntaxKind::BracketExpr) => {
725                let body_nodes = self.child_exprs(node);
726                let body = if body_nodes.is_empty() {
727                    Expr::Error.into_id(loc.clone())
728                } else {
729                    self.lower_expr_sequence(&body_nodes)
730                };
731                Expr::Bracket(body).into_id(loc)
732            }
733            Some(SyntaxKind::EscapeExpr) => {
734                let body_nodes = self.child_exprs(node);
735                let body = if body_nodes.is_empty() {
736                    Expr::Error.into_id(loc.clone())
737                } else {
738                    self.lower_expr_sequence(&body_nodes)
739                };
740                Expr::Escape(body).into_id(loc)
741            }
742            _ => Expr::Error.into_id(loc),
743        }
744    }
745
746    /// Lower a match expression from CST to AST
747    fn lower_match_expr(&self, node: GreenNodeId, loc: Location) -> ExprNodeId {
748        use crate::ast::MatchArm;
749
750        let children: Vec<GreenNodeId> = self
751            .arena
752            .children(node)
753            .map(|c| c.to_vec())
754            .unwrap_or_default();
755
756        // Find scrutinee (first expression child)
757        let scrutinee = children
758            .iter()
759            .find(|&&c| self.arena.kind(c).map(Self::is_expr_kind) == Some(true))
760            .map(|&c| self.lower_expr(c))
761            .unwrap_or_else(|| Expr::Error.into_id(loc.clone()));
762
763        // Find MatchArmList
764        let arm_list = children
765            .iter()
766            .find(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchArmList));
767
768        let arms: Vec<MatchArm> = arm_list
769            .and_then(|&list| self.arena.children(list))
770            .map(|arm_nodes| {
771                arm_nodes
772                    .iter()
773                    .filter(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchArm))
774                    .map(|&arm| self.lower_match_arm(arm))
775                    .collect()
776            })
777            .unwrap_or_default();
778
779        Expr::Match(scrutinee, arms).into_id(loc)
780    }
781
782    /// Lower a single match arm from CST to AST
783    fn lower_match_arm(&self, node: GreenNodeId) -> crate::ast::MatchArm {
784        use crate::ast::{MatchArm, MatchPattern};
785
786        let children: Vec<GreenNodeId> = self
787            .arena
788            .children(node)
789            .map(|c| c.iter().copied().collect())
790            .unwrap_or_default();
791
792        // Find pattern
793        let pattern_node = children
794            .iter()
795            .find(|&&c| self.arena.kind(c) == Some(SyntaxKind::MatchPattern));
796
797        let pattern = pattern_node
798            .map(|&pat| self.lower_match_pattern(pat))
799            .unwrap_or(MatchPattern::Wildcard);
800
801        // Find body expression (skip MatchPattern node to avoid matching pattern's literals)
802        let body = children
803            .iter()
804            .filter(|&&c| self.arena.kind(c) != Some(SyntaxKind::MatchPattern))
805            .find(|&&c| self.arena.kind(c).map(Self::is_expr_kind) == Some(true))
806            .map(|&c| self.lower_expr(c))
807            .unwrap_or_else(|| {
808                Expr::Error.into_id(self.location_from_span(self.node_span(node).unwrap_or(0..0)))
809            });
810
811        MatchArm { pattern, body }
812    }
813
814    /// Lower a match pattern from CST to AST
815    fn lower_match_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
816        use crate::ast::{Literal, MatchPattern};
817        use crate::interner::ToSymbol;
818
819        let children: Vec<GreenNodeId> = self
820            .arena
821            .children(node)
822            .map(|c| c.to_vec())
823            .unwrap_or_default();
824
825        for &child in &children {
826            match self.arena.kind(child) {
827                Some(SyntaxKind::IntLiteral) => {
828                    if let Some(text) = self.text_of_first_token(child) {
829                        if let Ok(n) = text.parse::<i64>() {
830                            return MatchPattern::Literal(Literal::Int(n));
831                        }
832                    }
833                }
834                Some(SyntaxKind::FloatLiteral) => {
835                    if let Some(text) = self.text_of_first_token(child) {
836                        return MatchPattern::Literal(Literal::Float(text.to_symbol()));
837                    }
838                }
839                Some(SyntaxKind::PlaceHolderLiteral) => {
840                    return MatchPattern::Wildcard;
841                }
842                Some(SyntaxKind::ConstructorPattern) => {
843                    return self.lower_constructor_pattern(child);
844                }
845                Some(SyntaxKind::TuplePattern) => {
846                    // Tuple pattern for multi-scrutinee matching: (pat1, pat2, ...)
847                    return self.lower_match_tuple_pattern(child);
848                }
849                Some(SyntaxKind::Identifier) => {
850                    // Bare identifier in pattern: treat as constructor (e.g., One, Two)
851                    if let Some(text) = self.text_of_first_token(child) {
852                        return MatchPattern::Constructor(text.to_symbol(), None);
853                    }
854                }
855                _ => {}
856            }
857        }
858
859        MatchPattern::Wildcard
860    }
861
862    /// Lower a constructor pattern from CST to AST
863    /// e.g., Float(x), String(s), MyType(binding), MyType, Two((x, y))
864    fn lower_constructor_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
865        use crate::ast::MatchPattern;
866        use crate::interner::ToSymbol;
867
868        let children: Vec<GreenNodeId> = self
869            .arena
870            .children(node)
871            .map(|c| c.to_vec())
872            .unwrap_or_default();
873
874        let mut constructor_name: Option<Symbol> = None;
875        let mut inner_pattern: Option<Box<MatchPattern>> = None;
876
877        for &child in &children {
878            match self.arena.kind(child) {
879                Some(SyntaxKind::Identifier) => {
880                    if let Some(text) = self.text_of_first_token(child) {
881                        if constructor_name.is_none() {
882                            constructor_name = Some(text.to_symbol());
883                        } else {
884                            // Second identifier is a variable binding pattern
885                            inner_pattern =
886                                Some(Box::new(MatchPattern::Variable(text.to_symbol())));
887                        }
888                    }
889                }
890                Some(SyntaxKind::PlaceHolderLiteral) => {
891                    // Discard binding with _
892                    inner_pattern = Some(Box::new(MatchPattern::Wildcard));
893                }
894                Some(SyntaxKind::TuplePattern) => {
895                    // Nested tuple pattern like (x, y)
896                    inner_pattern = Some(Box::new(self.lower_tuple_pattern(child)));
897                }
898                _ => {}
899            }
900        }
901
902        if let Some(name) = constructor_name {
903            MatchPattern::Constructor(name, inner_pattern)
904        } else {
905            MatchPattern::Wildcard
906        }
907    }
908
909    /// Lower a tuple pattern from CST to AST
910    /// e.g., (x, y), (a, b, c)
911    /// Also handles unwrapping spurious nesting like ((x, y)) in constructor patterns
912    fn lower_tuple_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
913        use crate::ast::MatchPattern;
914        use crate::interner::ToSymbol;
915
916        let children: Vec<GreenNodeId> = self
917            .arena
918            .children(node)
919            .map(|c| c.to_vec())
920            .unwrap_or_default();
921
922        let mut patterns = Vec::new();
923
924        for &child in &children {
925            match self.arena.kind(child) {
926                Some(SyntaxKind::Identifier) => {
927                    if let Some(text) = self.text_of_first_token(child) {
928                        patterns.push(MatchPattern::Variable(text.to_symbol()));
929                    }
930                }
931                Some(SyntaxKind::SinglePattern) => {
932                    // SinglePattern contains an identifier for variable binding
933                    if let Some(text) = self.text_of_first_token(child) {
934                        patterns.push(MatchPattern::Variable(text.to_symbol()));
935                    }
936                }
937                Some(SyntaxKind::PlaceHolderLiteral) => {
938                    patterns.push(MatchPattern::Wildcard);
939                }
940                Some(SyntaxKind::TuplePattern) => {
941                    // Nested tuple pattern
942                    patterns.push(self.lower_tuple_pattern(child));
943                }
944                _ => {}
945            }
946        }
947
948        // Unwrap spurious single-element tuple nesting
949        // This handles cases like ((x, y)) in constructor patterns where
950        // the outer parens are just for the constructor call syntax
951        if patterns.len() == 1 {
952            if let MatchPattern::Tuple(_) = &patterns[0] {
953                return patterns.pop().unwrap();
954            }
955        }
956
957        MatchPattern::Tuple(patterns)
958    }
959
960    /// Lower a tuple pattern in match expression from CST to AST
961    /// This handles (pat1, pat2, ...) where each element is a full match pattern
962    /// (can be literals, constructors, wildcards, or nested tuples)
963    fn lower_match_tuple_pattern(&self, node: GreenNodeId) -> crate::ast::MatchPattern {
964        use crate::ast::MatchPattern;
965
966        let children: Vec<GreenNodeId> = self
967            .arena
968            .children(node)
969            .map(|c| c.to_vec())
970            .unwrap_or_default();
971
972        let mut patterns = Vec::new();
973
974        for &child in &children {
975            match self.arena.kind(child) {
976                Some(SyntaxKind::MatchPattern) => {
977                    // Recursively lower each match pattern
978                    patterns.push(self.lower_match_pattern(child));
979                }
980                _ => {}
981            }
982        }
983
984        MatchPattern::Tuple(patterns)
985    }
986
987    fn lower_lambda(&self, node: GreenNodeId) -> (Vec<TypedId>, ExprNodeId) {
988        let (params, body_nodes) = self
989            .arena
990            .children(node)
991            .map(|children| {
992                (0..children.len()).fold(
993                    (Vec::new(), Vec::new(), 0usize),
994                    |(mut params, mut body_nodes, mut next_index), i| {
995                        if i < next_index {
996                            return (params, body_nodes, next_index);
997                        }
998
999                        let child = children[i];
1000                        match self.arena.kind(child) {
1001                            None => {
1002                                if let Some(token_index) = self.get_token_index(child)
1003                                    && let Some(token) = self.tokens.get(token_index)
1004                                    && matches!(
1005                                        token.kind,
1006                                        TokenKind::Ident | TokenKind::IdentParameter
1007                                    )
1008                                {
1009                                    let name = token.text(self.source).to_symbol();
1010                                    let loc = self.location_from_span(token.start..token.end());
1011                                    let mut ty = Type::Unknown.into_id_with_location(loc.clone());
1012                                    let mut next = i + 1;
1013
1014                                    if next < children.len()
1015                                        && self.arena.kind(children[next])
1016                                            == Some(SyntaxKind::TypeAnnotation)
1017                                    {
1018                                        ty = self
1019                                            .arena
1020                                            .children(children[next])
1021                                            .and_then(|type_children| {
1022                                                type_children
1023                                                    .iter()
1024                                                    .find(|c| {
1025                                                        self.arena
1026                                                            .kind(**c)
1027                                                            .map(Self::is_type_kind)
1028                                                            .unwrap_or(false)
1029                                                    })
1030                                                    .copied()
1031                                            })
1032                                            .map(|type_node| self.lower_type(type_node))
1033                                            .unwrap_or_else(|| {
1034                                                Type::Unknown.into_id_with_location(loc.clone())
1035                                            });
1036                                        next += 1;
1037                                    }
1038
1039                                    params.push(TypedId::new(name, ty));
1040                                    next_index = next;
1041                                }
1042                            }
1043                            Some(kind) if Self::is_expr_kind(kind) => {
1044                                body_nodes.push(child);
1045                            }
1046                            _ => {}
1047                        }
1048                        (params, body_nodes, next_index.max(i + 1))
1049                    },
1050                )
1051            })
1052            .map(|(params, body_nodes, _)| (params, body_nodes))
1053            .unwrap_or_else(|| (Vec::new(), Vec::new()));
1054
1055        let body = self.lower_expr_sequence(&body_nodes);
1056
1057        (params, body)
1058    }
1059
1060    fn lower_record_fields(&self, node: GreenNodeId) -> Vec<crate::ast::RecordField> {
1061        let (mut fields, _) = self
1062            .arena
1063            .children(node)
1064            .map_or((Vec::new(), None), |children| {
1065                children.iter().copied().fold(
1066                    (Vec::new(), None),
1067                    |(mut fields, mut current), child| {
1068                        // Check if it's an identifier token (not =, comma, etc.)
1069                        if let Some(token_index) = self.get_token_index(child)
1070                            && let Some(token) = self.tokens.get(token_index)
1071                            && matches!(token.kind, TokenKind::Ident)
1072                        {
1073                            current = Some(token.text(self.source).to_symbol());
1074                            return (fields, current);
1075                        }
1076
1077                        if self.arena.kind(child).map(Self::is_expr_kind) == Some(true)
1078                            && let Some(name) = current.take()
1079                        {
1080                            fields.push(crate::ast::RecordField {
1081                                name,
1082                                expr: self.lower_expr(child),
1083                            });
1084                        }
1085                        (fields, current)
1086                    },
1087                )
1088            });
1089
1090        fields.sort_by(|a, b| a.name.as_ref().cmp(b.name.as_ref()));
1091        fields
1092    }
1093
1094    fn lower_block_statements(&self, node: GreenNodeId) -> Vec<(Statement, Location)> {
1095        self.arena
1096            .children(node)
1097            .into_iter()
1098            .flatten()
1099            .copied()
1100            .filter(|child| self.arena.kind(*child) == Some(SyntaxKind::Statement))
1101            .filter_map(|child| self.lower_statement(child))
1102            .map(|(stmt, span)| {
1103                (
1104                    match stmt {
1105                        ProgramStatement::GlobalStatement(s) => s,
1106                        ProgramStatement::StageDeclaration { stage } => {
1107                            Statement::DeclareStage(stage)
1108                        }
1109                        _ => Statement::Error,
1110                    },
1111                    self.location_from_span(span),
1112                )
1113            })
1114            .collect()
1115    }
1116
1117    fn lower_pattern(&self, node: GreenNodeId) -> Option<(Pattern, Span)> {
1118        let span = self.node_span(node)?;
1119        let pat = match self.arena.kind(node) {
1120            Some(SyntaxKind::Pattern) => {
1121                if let Some(child) = self.child_patterns(node).into_iter().next() {
1122                    // Forward to actual contained pattern node
1123                    return self.lower_pattern(child);
1124                } else {
1125                    Pattern::Error
1126                }
1127            }
1128            Some(SyntaxKind::SinglePattern) => {
1129                let name_text = self.text_of_first_token(node).unwrap_or("");
1130                if name_text == "_" {
1131                    Pattern::Placeholder
1132                } else {
1133                    Pattern::Single(name_text.to_symbol())
1134                }
1135            }
1136            Some(SyntaxKind::TuplePattern) => {
1137                let elems = self
1138                    .child_patterns(node)
1139                    .into_iter()
1140                    .filter_map(|id| self.lower_pattern(id))
1141                    .map(|(p, _)| p)
1142                    .collect();
1143                Pattern::Tuple(elems)
1144            }
1145            Some(SyntaxKind::RecordPattern) => {
1146                let (items, _) = self
1147                    .arena
1148                    .children(node)
1149                    .map_or((Vec::new(), None), |children| {
1150                        children.iter().copied().fold(
1151                            (Vec::new(), None),
1152                            |(mut items, mut current), child| {
1153                                if let Some(token_index) = self.get_token_index(child)
1154                                    && let Some(token) = self.tokens.get(token_index)
1155                                    && matches!(
1156                                        token.kind,
1157                                        TokenKind::Ident | TokenKind::IdentParameter
1158                                    )
1159                                {
1160                                    current = Some(token.text(self.source).to_symbol());
1161                                    return (items, current);
1162                                }
1163
1164                                if self.arena.kind(child).map(Self::is_pattern_kind) == Some(true)
1165                                    && let Some((p, _)) = self.lower_pattern(child)
1166                                    && let Some(name) = current.take()
1167                                {
1168                                    items.push((name, p));
1169                                }
1170                                (items, current)
1171                            },
1172                        )
1173                    });
1174                Pattern::Record(items)
1175            }
1176            _ => Pattern::Error,
1177        };
1178
1179        Some((pat, span))
1180    }
1181
1182    fn lower_expr_sequence(&self, nodes: &[GreenNodeId]) -> ExprNodeId {
1183        let result = (0..nodes.len()).try_fold(
1184            (Option::<ExprNodeId>::None, false),
1185            |(acc, skip_next), i| {
1186                if skip_next {
1187                    return ControlFlow::Continue((acc, false));
1188                }
1189
1190                let node = nodes[i];
1191                match self.arena.kind(node) {
1192                    Some(SyntaxKind::BinaryExpr) => {
1193                        ControlFlow::Continue((Some(self.lower_binary(node)), false))
1194                    }
1195                    Some(SyntaxKind::CallExpr) => {
1196                        ControlFlow::Continue((Some(self.lower_call(node)), false))
1197                    }
1198                    Some(SyntaxKind::FieldAccess) => {
1199                        ControlFlow::Continue((Some(self.lower_field_access(node)), false))
1200                    }
1201                    Some(SyntaxKind::IndexExpr) => {
1202                        ControlFlow::Continue((Some(self.lower_index(node)), false))
1203                    }
1204                    Some(SyntaxKind::AssignExpr) => {
1205                        if let Some(lhs) = acc {
1206                            let assign = self.lower_assign(lhs, node);
1207                            // Check if there's a continuation
1208                            if i + 1 < nodes.len() {
1209                                let cont = self.lower_expr_sequence(&nodes[(i + 1)..]);
1210                                let loc = self.location_from_span(merge_spans(
1211                                    assign.to_span(),
1212                                    cont.to_span(),
1213                                ));
1214                                return ControlFlow::Break(
1215                                    Expr::Then(assign, Some(cont)).into_id(loc),
1216                                );
1217                            }
1218                            // No continuation in this sequence; just return bare assign
1219                            // Multi-statement chaining will be handled by lower_program's into_then_expr
1220                            ControlFlow::Continue((Some(assign), false))
1221                        } else {
1222                            ControlFlow::Continue((Some(self.lower_expr(node)), false))
1223                        }
1224                    }
1225                    Some(_) => {
1226                        // If we previously built a Then(assign, None), attach the remainder as continuation
1227                        if let Some(prev) = acc
1228                            && let Expr::Then(first, None) = prev.to_expr()
1229                        {
1230                            let rhs = self.lower_expr_sequence(&nodes[i..]);
1231                            let loc =
1232                                self.location_from_span(merge_spans(prev.to_span(), rhs.to_span()));
1233                            return ControlFlow::Break(Expr::Then(first, Some(rhs)).into_id(loc));
1234                        }
1235                        ControlFlow::Continue((Some(self.lower_expr(node)), false))
1236                    }
1237                    None => ControlFlow::Continue((acc, false)),
1238                }
1239            },
1240        );
1241
1242        match result {
1243            ControlFlow::Break(expr) => expr,
1244            ControlFlow::Continue((acc, _)) => {
1245                acc.unwrap_or_else(|| Expr::Error.into_id_without_span())
1246            }
1247        }
1248    }
1249
1250    /// Lower a BinaryExpr where LHS is a child of the node
1251    fn lower_binary(&self, node: GreenNodeId) -> ExprNodeId {
1252        let (op, op_span) = self
1253            .extract_binary_op(node)
1254            .unwrap_or((Op::Unknown("".to_string()), 0..0));
1255
1256        let expr_children = self.child_exprs(node);
1257
1258        // With the new CST structure, BinaryExpr contains exactly:
1259        // - LHS expression (first child expression)
1260        // - operator token (handled by extract_binary_op)
1261        // - RHS expression (second child expression)
1262        // The LHS might itself be a BinaryExpr for chained operations like a + b + c
1263
1264        let (lhs, rhs) = if expr_children.len() >= 2 {
1265            // Standard case: first expression is LHS, second is RHS
1266            let lhs_node = expr_children[0];
1267            let rhs_node = expr_children[expr_children.len() - 1];
1268            (self.lower_expr(lhs_node), self.lower_expr(rhs_node))
1269        } else if expr_children.len() == 1 {
1270            // Edge case: only one expression child (RHS), LHS might be missing
1271            (
1272                Expr::Error.into_id_without_span(),
1273                self.lower_expr(expr_children[0]),
1274            )
1275        } else {
1276            // No expression children
1277            (
1278                Expr::Error.into_id_without_span(),
1279                Expr::Error.into_id_without_span(),
1280            )
1281        };
1282
1283        let loc = self.location_from_span(merge_spans(lhs.to_span(), rhs.to_span()));
1284        Expr::BinOp(lhs, (op, op_span), rhs).into_id(loc)
1285    }
1286
1287    /// Lower a CallExpr where callee is a child of the node
1288    fn lower_call(&self, node: GreenNodeId) -> ExprNodeId {
1289        let expr_children = self.child_exprs(node);
1290
1291        // CallExpr contains: callee expression, then ArgList
1292        let callee = if !expr_children.is_empty() {
1293            self.lower_expr(expr_children[0])
1294        } else {
1295            Expr::Error.into_id_without_span()
1296        };
1297
1298        let args = self
1299            .lower_arg_list(node)
1300            .into_iter()
1301            .map(Self::unwrap_paren)
1302            .collect();
1303        let call_span = self.node_span(node).unwrap_or_else(|| callee.to_span());
1304        let loc = self.location_from_span(merge_spans(callee.to_span(), call_span));
1305        Expr::Apply(callee, args).into_id(loc)
1306    }
1307
1308    /// Lower a FieldAccess where LHS is a child of the node
1309    fn lower_field_access(&self, node: GreenNodeId) -> ExprNodeId {
1310        let expr_children = self.child_exprs(node);
1311
1312        let lhs = if !expr_children.is_empty() {
1313            self.lower_expr(expr_children[0])
1314        } else {
1315            Expr::Error.into_id_without_span()
1316        };
1317
1318        let lhs_span = lhs.to_span();
1319
1320        // Find the field name or index token (direct children only)
1321        let field_token = self
1322            .arena
1323            .children(node)
1324            .into_iter()
1325            .flatten()
1326            .filter_map(|&child| self.get_token_index(child))
1327            .filter_map(|idx| self.tokens.get(idx))
1328            .filter(|tok| matches!(tok.kind, TokenKind::Ident | TokenKind::Int))
1329            .next_back();
1330
1331        let expr = field_token
1332            .and_then(|tok| match tok.kind {
1333                TokenKind::Ident => Some(Expr::FieldAccess(lhs, tok.text(self.source).to_symbol())),
1334                TokenKind::Int => tok
1335                    .text(self.source)
1336                    .parse::<i64>()
1337                    .ok()
1338                    .map(|n| Expr::Proj(lhs, n)),
1339                _ => None,
1340            })
1341            .unwrap_or(Expr::Error);
1342
1343        let span = self
1344            .node_span(node)
1345            .map(|s| merge_spans(lhs_span.clone(), s))
1346            .unwrap_or(lhs_span);
1347        let loc = self.location_from_span(span);
1348        match expr {
1349            Expr::FieldAccess(_, _) | Expr::Proj(_, _) => expr.into_id(loc),
1350            _ => Expr::Error.into_id(loc),
1351        }
1352    }
1353
1354    /// Lower an IndexExpr where LHS is a child of the node
1355    fn lower_index(&self, node: GreenNodeId) -> ExprNodeId {
1356        let expr_children = self.child_exprs(node);
1357
1358        // IndexExpr contains: LHS expression, index expression (between [ and ])
1359        let (lhs, index) = if expr_children.len() >= 2 {
1360            (
1361                self.lower_expr(expr_children[0]),
1362                self.lower_expr(expr_children[1]),
1363            )
1364        } else if expr_children.len() == 1 {
1365            (
1366                self.lower_expr(expr_children[0]),
1367                Expr::Error.into_id_without_span(),
1368            )
1369        } else {
1370            (
1371                Expr::Error.into_id_without_span(),
1372                Expr::Error.into_id_without_span(),
1373            )
1374        };
1375
1376        let lhs_span = lhs.to_span();
1377        let index_span = index.to_span();
1378        let loc = self.location_from_span(merge_spans(lhs_span, index_span));
1379        Expr::ArrayAccess(lhs, index).into_id(loc)
1380    }
1381
1382    fn lower_assign(&self, lhs: ExprNodeId, node: GreenNodeId) -> ExprNodeId {
1383        let rhs_nodes = self.child_exprs(node);
1384        let rhs = self.lower_expr_sequence(&rhs_nodes);
1385        let loc = self.location_from_span(merge_spans(lhs.to_span(), rhs.to_span()));
1386        Expr::Assign(lhs, rhs).into_id(loc)
1387    }
1388
1389    /// Calculate span for macro expansion, extending base_span to include the `!` token
1390    fn macro_expand_span(&self, node: GreenNodeId, base_span: Span) -> Span {
1391        let bang_end = self
1392            .find_token(node, |kind| matches!(kind, TokenKind::MacroExpand))
1393            .and_then(|idx| self.tokens.get(idx).map(|t| t.end()))
1394            .unwrap_or(base_span.end);
1395        base_span.start..bang_end
1396    }
1397
1398    fn lower_macro_expand(&self, node: GreenNodeId) -> (ExprNodeId, Vec<ExprNodeId>) {
1399        let args = self.lower_arg_list(node);
1400
1401        // Check for qualified path first (e.g., mod::macrofn!())
1402        if let Some(path_node) = self.find_child(node, |kind| kind == SyntaxKind::QualifiedPath)
1403            && let Some(path) = self.lower_qualified_path(path_node)
1404        {
1405            let path_span = self.node_span(path_node).unwrap_or(0..0);
1406            let loc = self.location_from_span(self.macro_expand_span(node, path_span));
1407            return (Expr::QualifiedVar(path).into_id(loc), args);
1408        }
1409
1410        // Simple identifier macro (e.g., macrofn!())
1411        let name_idx = self.find_token(node, |kind| matches!(kind, TokenKind::Ident));
1412        let name_text = name_idx.and_then(|idx| self.token_text(idx)).unwrap_or("");
1413        let name = name_text.to_symbol();
1414        let ident_span = name_idx
1415            .and_then(|idx| self.tokens.get(idx).map(|t| t.start..t.end()))
1416            .unwrap_or(0..0);
1417        let loc = self.location_from_span(self.macro_expand_span(node, ident_span));
1418        (Expr::Var(name).into_id(loc), args)
1419    }
1420
1421    fn lower_param_list(&self, node: GreenNodeId) -> (Vec<TypedId>, Span) {
1422        let params = if let Some(children) = self.arena.children(node) {
1423            let (new_params, _) =
1424                (0..children.len()).fold((Vec::new(), 0usize), |(mut acc, mut next_index), i| {
1425                    if i < next_index {
1426                        return (acc, next_index);
1427                    }
1428
1429                    let child = children[i];
1430                    // Check for identifier token
1431                    if let GreenNode::Token { token_index, .. } = self.arena.get(child)
1432                        && let Some(token) = self.tokens.get(*token_index)
1433                        && matches!(token.kind, TokenKind::Ident | TokenKind::IdentParameter)
1434                    {
1435                        let name = token.text(self.source).to_symbol();
1436                        let loc = self.location_from_span(token.start..token.end());
1437                        let mut next = i + 1;
1438                        let mut ty = Type::Unknown.into_id_with_location(loc.clone());
1439                        let mut default_value = None;
1440
1441                        // Optional type annotation
1442                        if next < children.len()
1443                            && self.arena.kind(children[next]) == Some(SyntaxKind::TypeAnnotation)
1444                        {
1445                            if let Some(type_children) = self.arena.children(children[next]) {
1446                                ty = type_children
1447                                    .iter()
1448                                    .find(|c| {
1449                                        self.arena
1450                                            .kind(**c)
1451                                            .map(Self::is_type_kind)
1452                                            .unwrap_or(false)
1453                                    })
1454                                    .map(|type_node| self.lower_type(*type_node))
1455                                    .unwrap_or_else(|| {
1456                                        Type::Unknown.into_id_with_location(loc.clone())
1457                                    });
1458                            }
1459                            next += 1;
1460                        }
1461
1462                        // Optional default value
1463                        if next < children.len()
1464                            && self.arena.kind(children[next]) == Some(SyntaxKind::ParamDefault)
1465                        {
1466                            let expr_nodes = self.child_exprs(children[next]);
1467                            if !expr_nodes.is_empty() {
1468                                default_value = Some(self.lower_expr_sequence(&expr_nodes));
1469                            }
1470                            next += 1;
1471                        }
1472
1473                        let tid = match default_value {
1474                            Some(default_value) => TypedId::with_default(name, ty, default_value),
1475                            None => TypedId::new(name, ty),
1476                        };
1477
1478                        acc.push(tid);
1479                        next_index = next;
1480                    }
1481
1482                    (acc, next_index.max(i + 1))
1483                });
1484            new_params
1485        } else {
1486            Vec::new()
1487        };
1488
1489        let span = self.node_span(node).unwrap_or(0..0);
1490        (params, span)
1491    }
1492
1493    fn child_exprs(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
1494        self.arena
1495            .children(node)
1496            .into_iter()
1497            .flatten()
1498            .copied()
1499            .filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
1500            .collect()
1501    }
1502
1503    fn child_patterns(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
1504        self.arena
1505            .children(node)
1506            .into_iter()
1507            .flatten()
1508            .copied()
1509            .filter(|child| self.arena.kind(*child).map(Self::is_pattern_kind) == Some(true))
1510            .collect()
1511    }
1512
1513    fn find_child(
1514        &self,
1515        node: GreenNodeId,
1516        predicate: impl Fn(SyntaxKind) -> bool,
1517    ) -> Option<GreenNodeId> {
1518        self.arena
1519            .children(node)?
1520            .iter()
1521            .copied()
1522            .find(|child| match self.arena.kind(*child) {
1523                Some(kind) => predicate(kind),
1524                None => false,
1525            })
1526    }
1527
1528    fn collect_expr_nodes(&self, node: GreenNodeId) -> Vec<GreenNodeId> {
1529        self.arena
1530            .children(node)
1531            .into_iter()
1532            .flatten()
1533            .copied()
1534            .filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
1535            .collect()
1536    }
1537
1538    fn collect_expr_nodes_after(&self, node: GreenNodeId, after: GreenNodeId) -> Vec<GreenNodeId> {
1539        self.arena
1540            .children(node)
1541            .into_iter()
1542            .flatten()
1543            .copied()
1544            .skip_while(|child| *child != after)
1545            .skip(1)
1546            .filter(|child| self.arena.kind(*child).map(Self::is_expr_kind) == Some(true))
1547            .collect()
1548    }
1549
1550    fn find_token(
1551        &self,
1552        node: GreenNodeId,
1553        predicate: impl Fn(TokenKind) -> bool,
1554    ) -> Option<usize> {
1555        self.walk_tokens(node).into_iter().find(|&idx| {
1556            self.tokens
1557                .get(idx)
1558                .map(|t| predicate(t.kind))
1559                .unwrap_or(false)
1560        })
1561    }
1562
1563    fn walk_tokens(&self, node: GreenNodeId) -> Vec<usize> {
1564        match self.arena.get(node) {
1565            GreenNode::Token { token_index, .. } => vec![*token_index],
1566            GreenNode::Internal { children, .. } => children
1567                .iter()
1568                .flat_map(|child| self.walk_tokens(*child))
1569                .collect(),
1570        }
1571    }
1572
1573    fn text_of_first_token(&self, node: GreenNodeId) -> Option<&'a str> {
1574        self.walk_tokens(node)
1575            .into_iter()
1576            .next()
1577            .and_then(|idx| self.token_text(idx))
1578    }
1579
1580    fn text_of_token_node(&self, node: GreenNodeId) -> Option<&'a str> {
1581        match self.arena.get(node) {
1582            GreenNode::Token { token_index, .. } => self.token_text(*token_index),
1583            _ => None,
1584        }
1585    }
1586
1587    fn get_token_index(&self, node: GreenNodeId) -> Option<usize> {
1588        match self.arena.get(node) {
1589            GreenNode::Token { token_index, .. } => Some(*token_index),
1590            _ => None,
1591        }
1592    }
1593
1594    fn token_text(&self, index: usize) -> Option<&'a str> {
1595        self.tokens.get(index).map(|t| t.text(self.source))
1596    }
1597
1598    fn node_span(&self, node: GreenNodeId) -> Option<Span> {
1599        match self.arena.get(node) {
1600            GreenNode::Token { token_index, .. } => {
1601                self.tokens.get(*token_index).map(|t| t.start..t.end())
1602            }
1603            GreenNode::Internal { children, .. } => {
1604                let (start, end) = children
1605                    .iter()
1606                    .filter_map(|child| self.node_span(*child))
1607                    .fold(
1608                        (Option::<usize>::None, Option::<usize>::None),
1609                        |(start, end), span| {
1610                            let next_start = Some(start.map_or(span.start, |s| s.min(span.start)));
1611                            let next_end = Some(end.map_or(span.end, |e| e.max(span.end)));
1612                            (next_start, next_end)
1613                        },
1614                    );
1615
1616                start.zip(end).map(|(s, e)| s..e)
1617            }
1618        }
1619    }
1620
1621    fn location_from_span(&self, span: Span) -> Location {
1622        Location {
1623            span,
1624            path: self.file_path.clone(),
1625        }
1626    }
1627
1628    fn lower_arg_list(&self, node: GreenNodeId) -> Vec<ExprNodeId> {
1629        let collect_args = |children: &[GreenNodeId]| {
1630            let (mut args, current) = children.iter().copied().fold(
1631                (Vec::new(), Vec::new()),
1632                |(mut args, mut current), child| {
1633                    match self.arena.get(child) {
1634                        GreenNode::Token { token_index, .. }
1635                            if self.tokens.get(*token_index).map(|t| t.kind)
1636                                == Some(TokenKind::Comma) =>
1637                        {
1638                            if !current.is_empty() {
1639                                args.push(self.lower_expr_sequence(&current));
1640                                current.clear();
1641                            }
1642                        }
1643                        _ => {
1644                            if self.arena.kind(child).map(Self::is_expr_kind) == Some(true) {
1645                                current.push(child);
1646                            }
1647                        }
1648                    }
1649                    (args, current)
1650                },
1651            );
1652            if !current.is_empty() {
1653                args.push(self.lower_expr_sequence(&current));
1654            }
1655            args
1656        };
1657
1658        // Prefer extracting from an explicit ArgList child if present.
1659        if let Some(arg_node) = self.find_child(node, |kind| kind == SyntaxKind::ArgList) {
1660            if let Some(children) = self.arena.children(arg_node) {
1661                return collect_args(children);
1662            }
1663            return Vec::new();
1664        }
1665
1666        // Fallback: scan direct children if no ArgList exists.
1667        if let Some(children) = self.arena.children(node) {
1668            return collect_args(children);
1669        }
1670        Vec::new()
1671    }
1672
1673    fn lower_expr_list(&self, node: GreenNodeId) -> Vec<ExprNodeId> {
1674        let collect_elems = |children: &[GreenNodeId]| {
1675            let (mut elems, current) = children.iter().copied().fold(
1676                (Vec::new(), Vec::new()),
1677                |(mut elems, mut current), child| {
1678                    match self.arena.get(child) {
1679                        GreenNode::Token { token_index, .. }
1680                            if self.tokens.get(*token_index).map(|t| t.kind)
1681                                == Some(TokenKind::Comma) =>
1682                        {
1683                            if !current.is_empty() {
1684                                elems.push(self.lower_expr_sequence(&current));
1685                                current.clear();
1686                            }
1687                        }
1688                        _ => {
1689                            if self.arena.kind(child).map(Self::is_expr_kind) == Some(true) {
1690                                current.push(child);
1691                            }
1692                        }
1693                    }
1694                    (elems, current)
1695                },
1696            );
1697            if !current.is_empty() {
1698                elems.push(self.lower_expr_sequence(&current));
1699            }
1700            elems
1701        };
1702
1703        self.arena
1704            .children(node)
1705            .map_or_else(Vec::new, collect_elems)
1706    }
1707
1708    fn is_expr_kind(kind: SyntaxKind) -> bool {
1709        matches!(
1710            kind,
1711            SyntaxKind::BinaryExpr
1712                | SyntaxKind::UnaryExpr
1713                | SyntaxKind::ParenExpr
1714                | SyntaxKind::CallExpr
1715                | SyntaxKind::FieldAccess
1716                | SyntaxKind::IndexExpr
1717                | SyntaxKind::AssignExpr
1718                | SyntaxKind::ArrayExpr
1719                | SyntaxKind::MacroExpansion
1720                | SyntaxKind::BracketExpr
1721                | SyntaxKind::EscapeExpr
1722                | SyntaxKind::LambdaExpr
1723                | SyntaxKind::IfExpr
1724                | SyntaxKind::MatchExpr
1725                | SyntaxKind::BlockExpr
1726                | SyntaxKind::TupleExpr
1727                | SyntaxKind::RecordExpr
1728                | SyntaxKind::IntLiteral
1729                | SyntaxKind::FloatLiteral
1730                | SyntaxKind::StringLiteral
1731                | SyntaxKind::SelfLiteral
1732                | SyntaxKind::NowLiteral
1733                | SyntaxKind::SampleRateLiteral
1734                | SyntaxKind::PlaceHolderLiteral
1735                | SyntaxKind::Identifier
1736                | SyntaxKind::QualifiedPath
1737        )
1738    }
1739
1740    fn is_pattern_kind(kind: SyntaxKind) -> bool {
1741        matches!(
1742            kind,
1743            SyntaxKind::Pattern
1744                | SyntaxKind::SinglePattern
1745                | SyntaxKind::TuplePattern
1746                | SyntaxKind::RecordPattern
1747        )
1748    }
1749
1750    fn extract_unary_op(&self, node: GreenNodeId) -> Option<Op> {
1751        self.walk_tokens(node)
1752            .into_iter()
1753            .find_map(|idx| match self.tokens.get(idx)?.kind {
1754                TokenKind::OpMinus => Some(Op::Minus),
1755                TokenKind::OpSum => Some(Op::Sum),
1756                _ => None,
1757            })
1758    }
1759
1760    fn extract_binary_op(&self, node: GreenNodeId) -> Option<(Op, Span)> {
1761        // Only look at direct children of this node, not recursively
1762        // This is important because nested BinaryExpr would also contain operator tokens
1763        let children = self.arena.children(node)?;
1764        for &child in children {
1765            if let Some(idx) = self.get_token_index(child)
1766                && let Some(tok) = self.tokens.get(idx)
1767            {
1768                let op = match tok.kind {
1769                    TokenKind::OpSum => Some(Op::Sum),
1770                    TokenKind::OpMinus => Some(Op::Minus),
1771                    TokenKind::OpProduct => Some(Op::Product),
1772                    TokenKind::OpDivide => Some(Op::Divide),
1773                    TokenKind::OpEqual => Some(Op::Equal),
1774                    TokenKind::OpNotEqual => Some(Op::NotEqual),
1775                    TokenKind::OpLessThan => Some(Op::LessThan),
1776                    TokenKind::OpLessEqual => Some(Op::LessEqual),
1777                    TokenKind::OpGreaterThan => Some(Op::GreaterThan),
1778                    TokenKind::OpGreaterEqual => Some(Op::GreaterEqual),
1779                    TokenKind::OpModulo => Some(Op::Modulo),
1780                    TokenKind::OpExponent => Some(Op::Exponent),
1781                    TokenKind::OpAnd => Some(Op::And),
1782                    TokenKind::OpOr => Some(Op::Or),
1783                    TokenKind::OpAt => Some(Op::At),
1784                    TokenKind::OpPipe => Some(Op::Pipe),
1785                    TokenKind::OpPipeMacro => Some(Op::PipeMacro),
1786                    _ => None,
1787                };
1788                if let Some(op) = op {
1789                    return Some((op, tok.start..tok.end()));
1790                }
1791            }
1792        }
1793        None
1794    }
1795}
1796
1797/// Full pipeline helper: tokenize, parse CST, then lower into `Program`.
1798pub fn parse_program(source: &str, file_path: PathBuf) -> (Program, Vec<ParserError>) {
1799    let tokens = crate::compiler::parser::tokenize(source);
1800    let preparsed = crate::compiler::parser::preparse(&tokens);
1801    let (root, arena, tokens, errors) = crate::compiler::parser::parse_cst(tokens, &preparsed);
1802    let lowerer = Lowerer::new(source, &tokens, &arena, file_path);
1803    let program = lowerer.lower_program(root);
1804    (program, errors)
1805}
1806
1807/// Parse source to ExprNodeId with error collection.
1808/// This is a compatibility function for the old parser API.
1809pub fn parse_to_expr(
1810    source: &str,
1811    file_path: Option<PathBuf>,
1812) -> (
1813    ExprNodeId,
1814    crate::ast::program::ModuleInfo,
1815    Vec<Box<dyn crate::utils::error::ReportableError>>,
1816) {
1817    let path = file_path.unwrap_or_default();
1818    let (prog, parse_errs) = parse_program(source, path.clone());
1819    let errs =
1820        crate::compiler::parser::parser_errors_to_reportable(source, path.clone(), parse_errs);
1821
1822    if prog.statements.is_empty() {
1823        return (
1824            Expr::Error.into_id_without_span(),
1825            crate::ast::program::ModuleInfo::new(),
1826            errs,
1827        );
1828    }
1829
1830    let (expr, module_info, mut new_errs) = crate::ast::program::expr_from_program(prog, path);
1831    let mut all_errs = errs;
1832    all_errs.append(&mut new_errs);
1833    (expr, module_info, all_errs)
1834}
1835
1836/// Add global context wrapper around AST.
1837/// This is a compatibility function for the old parser API.
1838pub fn add_global_context(ast: ExprNodeId, file_path: PathBuf) -> ExprNodeId {
1839    let span = ast.to_span();
1840    let loc = crate::utils::metadata::Location {
1841        span: span.clone(),
1842        path: file_path,
1843    };
1844    let res = Expr::Let(
1845        TypedPattern::new(
1846            Pattern::Single(crate::utils::metadata::GLOBAL_LABEL.to_symbol()),
1847            Type::Unknown.into_id_with_location(loc.clone()),
1848        ),
1849        Expr::Lambda(vec![], None, ast).into_id(loc.clone()),
1850        None,
1851    );
1852    res.into_id(loc)
1853}
1854
1855/// Check if a SyntaxKind represents a type node
1856impl<'a> Lowerer<'a> {
1857    fn is_type_kind(kind: SyntaxKind) -> bool {
1858        matches!(
1859            kind,
1860            SyntaxKind::PrimitiveType
1861                | SyntaxKind::UnitType
1862                | SyntaxKind::TupleType
1863                | SyntaxKind::RecordType
1864                | SyntaxKind::FunctionType
1865                | SyntaxKind::ArrayType
1866                | SyntaxKind::CodeType
1867                | SyntaxKind::UnionType
1868                | SyntaxKind::TypeIdent
1869        )
1870    }
1871
1872    /// Lower a type node to TypeNodeId
1873    fn lower_type(&self, node: GreenNodeId) -> crate::interner::TypeNodeId {
1874        use crate::types::{PType, RecordTypeField, Type};
1875
1876        let span = self.node_span(node).unwrap_or(0..0);
1877        let loc = self.location_from_span(span);
1878
1879        match self.arena.kind(node) {
1880            Some(SyntaxKind::PrimitiveType) => {
1881                let text = self.text_of_first_token(node).unwrap_or("float");
1882                let ptype = match text {
1883                    "float" => PType::Numeric,
1884                    "int" => PType::Int,
1885                    "string" => PType::String,
1886                    _ => PType::Numeric,
1887                };
1888                Type::Primitive(ptype).into_id_with_location(loc)
1889            }
1890            Some(SyntaxKind::UnitType) => Type::Primitive(PType::Unit).into_id_with_location(loc),
1891            Some(SyntaxKind::TupleType) => {
1892                let elem_types = self
1893                    .arena
1894                    .children(node)
1895                    .into_iter()
1896                    .flatten()
1897                    .filter(|child| {
1898                        self.arena
1899                            .kind(**child)
1900                            .map(Self::is_type_kind)
1901                            .unwrap_or(false)
1902                    })
1903                    .map(|child| self.lower_type(*child))
1904                    .collect::<Vec<_>>();
1905                Type::Tuple(elem_types).into_id_with_location(loc)
1906            }
1907            Some(SyntaxKind::ArrayType) => {
1908                let elem_type = self
1909                    .arena
1910                    .children(node)
1911                    .into_iter()
1912                    .flatten()
1913                    .find(|child| {
1914                        self.arena
1915                            .kind(**child)
1916                            .map(Self::is_type_kind)
1917                            .unwrap_or(false)
1918                    })
1919                    .map(|child| self.lower_type(*child))
1920                    .unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
1921                Type::Array(elem_type).into_id_with_location(loc)
1922            }
1923            Some(SyntaxKind::FunctionType) => {
1924                // Function type: (T1, T2) -> R
1925                let children: Vec<_> = self
1926                    .arena
1927                    .children(node)
1928                    .into_iter()
1929                    .flatten()
1930                    .filter(|child| {
1931                        self.arena
1932                            .kind(**child)
1933                            .map(Self::is_type_kind)
1934                            .unwrap_or(false)
1935                    })
1936                    .copied()
1937                    .collect();
1938
1939                if children.len() >= 2 {
1940                    let lowered = children
1941                        .iter()
1942                        .map(|child| self.lower_type(*child))
1943                        .collect::<Vec<_>>();
1944
1945                    let return_type = *lowered
1946                        .last()
1947                        .unwrap_or(&Type::Unknown.into_id_with_location(loc.clone()));
1948                    let param_type = if lowered.len() == 2 {
1949                        lowered[0]
1950                    } else {
1951                        Type::Tuple(lowered[..lowered.len() - 1].to_vec())
1952                            .into_id_with_location(loc.clone())
1953                    };
1954
1955                    Type::Function {
1956                        arg: param_type,
1957                        ret: return_type,
1958                    }
1959                    .into_id_with_location(loc)
1960                } else {
1961                    Type::Unknown.into_id_with_location(loc)
1962                }
1963            }
1964            Some(SyntaxKind::RecordType) => {
1965                // Record type: {field: Type, ...}
1966                let mut fields = Vec::new();
1967                let mut current_field: Option<Symbol> = None;
1968
1969                if let Some(children) = self.arena.children(node) {
1970                    for child in children {
1971                        // Check if it's an identifier token (not =, comma, etc.)
1972                        if let Some(token_index) = self.get_token_index(*child)
1973                            && let Some(token) = self.tokens.get(token_index)
1974                            && matches!(token.kind, TokenKind::Ident | TokenKind::IdentParameter)
1975                        {
1976                            current_field = Some(token.text(self.source).to_symbol());
1977                            continue;
1978                        }
1979
1980                        if self.arena.kind(*child).map(Self::is_type_kind) == Some(true)
1981                            && let Some(name) = current_field.take()
1982                        {
1983                            fields.push(RecordTypeField::new(name, self.lower_type(*child), false));
1984                        }
1985                    }
1986                }
1987                Type::Record(fields).into_id_with_location(loc)
1988            }
1989            Some(SyntaxKind::CodeType) => {
1990                let inner = self
1991                    .arena
1992                    .children(node)
1993                    .into_iter()
1994                    .flatten()
1995                    .find(|child| self.arena.kind(**child).map(Self::is_type_kind) == Some(true))
1996                    .map(|child| self.lower_type(*child))
1997                    .unwrap_or_else(|| Type::Unknown.into_id_with_location(loc.clone()));
1998                Type::Code(inner).into_id_with_location(loc)
1999            }
2000            Some(SyntaxKind::UnionType) => {
2001                let elem_types = self
2002                    .arena
2003                    .children(node)
2004                    .into_iter()
2005                    .flatten()
2006                    .filter(|child| {
2007                        self.arena
2008                            .kind(**child)
2009                            .map(Self::is_type_kind)
2010                            .unwrap_or(false)
2011                    })
2012                    .map(|child| self.lower_type(*child))
2013                    .collect::<Vec<_>>();
2014                Type::Union(elem_types).into_id_with_location(loc)
2015            }
2016            Some(SyntaxKind::TypeIdent) => {
2017                // Type name reference in type annotation (e.g., MyEnum or mymath::PrivateNum)
2018                // TypeIdent node contains child Ident tokens and DoubleColon tokens for qualified paths
2019                if let Some(children) = self.arena.children(node) {
2020                    let mut path_segments = Vec::new();
2021
2022                    for child in children {
2023                        if let Some(token_index) = self.get_token_index(*child)
2024                            && let Some(token) = self.tokens.get(token_index)
2025                        {
2026                            if matches!(token.kind, TokenKind::Ident) {
2027                                path_segments.push(token.text(self.source));
2028                            }
2029                            // Skip DoubleColon tokens - they're just separators
2030                        }
2031                    }
2032
2033                    if !path_segments.is_empty() {
2034                        // Join path segments with $ for mangled name
2035                        let type_name = path_segments.join("$").to_symbol();
2036                        return Type::TypeAlias(type_name).into_id_with_location(loc);
2037                    }
2038                }
2039                Type::Unknown.into_id_with_location(loc)
2040            }
2041            _ => {
2042                // Check if this is an identifier token (type name reference)
2043                if let Some(token_index) = self.get_token_index(node)
2044                    && let Some(token) = self.tokens.get(token_index)
2045                    && matches!(token.kind, TokenKind::Ident)
2046                {
2047                    // Type name - store as TypeAlias for later resolution
2048                    let type_name = token.text(self.source).to_symbol();
2049                    Type::TypeAlias(type_name).into_id_with_location(loc)
2050                } else {
2051                    Type::Unknown.into_id_with_location(loc)
2052                }
2053            }
2054        }
2055    }
2056}
2057
2058fn merge_spans(a: Span, b: Span) -> Span {
2059    a.start.min(b.start)..a.end.max(b.end)
2060}
2061
2062#[cfg(test)]
2063mod tests {
2064    use super::*;
2065    use crate::compiler::parser::{parse_cst, preparse, tokenize};
2066
2067    fn parse_source(source: &str) -> Program {
2068        let tokens = tokenize(source);
2069        let preparsed = preparse(&tokens);
2070        let (green_id, arena, tokens, _errors) = parse_cst(tokens, &preparsed);
2071        let lowerer = Lowerer::new(source, &tokens, &arena, PathBuf::new());
2072        lowerer.lower_program(green_id)
2073    }
2074
2075    #[test]
2076    fn test_parse_module_declaration() {
2077        let source = "mod mymod { fn foo() { 42 } }";
2078        let prog = parse_source(source);
2079
2080        assert!(!prog.statements.is_empty());
2081
2082        let stmt = &prog.statements[0].0;
2083        match stmt {
2084            ProgramStatement::ModuleDefinition {
2085                visibility,
2086                name,
2087                body,
2088            } => {
2089                assert_eq!(*visibility, Visibility::Private);
2090                assert_eq!(name.as_str(), "mymod");
2091                assert!(body.as_ref().map(|b| !b.is_empty()).unwrap_or(false));
2092            }
2093            _ => panic!("Expected ModuleDefinition, got {:?}", stmt),
2094        }
2095    }
2096
2097    #[test]
2098    fn test_parse_pub_module_declaration() {
2099        let source = "pub mod mymod { fn bar() { 1 } }";
2100        let prog = parse_source(source);
2101
2102        assert!(!prog.statements.is_empty());
2103
2104        let stmt = &prog.statements[0].0;
2105        match stmt {
2106            ProgramStatement::ModuleDefinition {
2107                visibility,
2108                name,
2109                body,
2110            } => {
2111                assert_eq!(*visibility, Visibility::Public);
2112                assert_eq!(name.as_str(), "mymod");
2113                assert!(body.as_ref().map(|b| !b.is_empty()).unwrap_or(false));
2114            }
2115            _ => panic!("Expected ModuleDefinition, got {:?}", stmt),
2116        }
2117    }
2118
2119    #[test]
2120    fn test_parse_use_statement() {
2121        let source = "use modA::funcB";
2122        let prog = parse_source(source);
2123
2124        assert!(!prog.statements.is_empty());
2125
2126        let stmt = &prog.statements[0].0;
2127        match stmt {
2128            ProgramStatement::UseStatement {
2129                visibility,
2130                path,
2131                target,
2132            } => {
2133                assert_eq!(*visibility, Visibility::Private);
2134                assert_eq!(path.segments.len(), 2);
2135                assert_eq!(path.segments[0].as_str(), "modA");
2136                assert_eq!(path.segments[1].as_str(), "funcB");
2137                assert!(matches!(target, UseTarget::Single));
2138            }
2139            _ => panic!("Expected UseStatement, got {:?}", stmt),
2140        }
2141    }
2142
2143    #[test]
2144    fn test_parse_use_multiple() {
2145        let source = "use modA::{funcB, funcC}";
2146        let prog = parse_source(source);
2147
2148        assert!(!prog.statements.is_empty());
2149
2150        let stmt = &prog.statements[0].0;
2151        match stmt {
2152            ProgramStatement::UseStatement {
2153                visibility,
2154                path,
2155                target,
2156            } => {
2157                assert_eq!(*visibility, Visibility::Private);
2158                assert_eq!(path.segments.len(), 1);
2159                assert_eq!(path.segments[0].as_str(), "modA");
2160                match target {
2161                    UseTarget::Multiple(names) => {
2162                        assert_eq!(names.len(), 2);
2163                        assert_eq!(names[0].as_str(), "funcB");
2164                        assert_eq!(names[1].as_str(), "funcC");
2165                    }
2166                    _ => panic!("Expected UseTarget::Multiple"),
2167                }
2168            }
2169            _ => panic!("Expected UseStatement, got {:?}", stmt),
2170        }
2171    }
2172
2173    #[test]
2174    fn test_parse_use_wildcard() {
2175        let source = "use modA::*";
2176        let prog = parse_source(source);
2177
2178        assert!(!prog.statements.is_empty());
2179
2180        let stmt = &prog.statements[0].0;
2181        match stmt {
2182            ProgramStatement::UseStatement {
2183                visibility,
2184                path,
2185                target,
2186            } => {
2187                assert_eq!(*visibility, Visibility::Private);
2188                assert_eq!(path.segments.len(), 1);
2189                assert_eq!(path.segments[0].as_str(), "modA");
2190                assert!(matches!(target, UseTarget::Wildcard));
2191            }
2192            _ => panic!("Expected UseStatement, got {:?}", stmt),
2193        }
2194    }
2195
2196    #[test]
2197    fn test_parse_nested_module() {
2198        let source = "mod outer { mod inner { fn baz() { 0 } } }";
2199        let prog = parse_source(source);
2200
2201        assert!(!prog.statements.is_empty());
2202
2203        let stmt = &prog.statements[0].0;
2204        match stmt {
2205            ProgramStatement::ModuleDefinition {
2206                visibility,
2207                name,
2208                body,
2209            } => {
2210                assert_eq!(*visibility, Visibility::Private);
2211                assert_eq!(name.as_str(), "outer");
2212                let body = body.as_ref().expect("Expected inline module body");
2213                assert!(!body.is_empty());
2214
2215                // Check inner module
2216                let inner_stmt = &body[0].0;
2217                match inner_stmt {
2218                    ProgramStatement::ModuleDefinition {
2219                        name: inner_name, ..
2220                    } => {
2221                        assert_eq!(inner_name.as_str(), "inner");
2222                    }
2223                    _ => panic!("Expected inner ModuleDefinition"),
2224                }
2225            }
2226            _ => panic!("Expected ModuleDefinition, got {:?}", stmt),
2227        }
2228    }
2229
2230    #[test]
2231    fn test_parse_use_with_long_path() {
2232        let source = "use a::b::c::d";
2233        let prog = parse_source(source);
2234
2235        assert!(!prog.statements.is_empty());
2236
2237        let stmt = &prog.statements[0].0;
2238        match stmt {
2239            ProgramStatement::UseStatement { path, .. } => {
2240                assert_eq!(path.segments.len(), 4);
2241                assert_eq!(path.segments[0].as_str(), "a");
2242                assert_eq!(path.segments[1].as_str(), "b");
2243                assert_eq!(path.segments[2].as_str(), "c");
2244                assert_eq!(path.segments[3].as_str(), "d");
2245            }
2246            _ => panic!("Expected UseStatement, got {:?}", stmt),
2247        }
2248    }
2249
2250    #[test]
2251    fn test_module_with_multiple_items() {
2252        let source = r#"
2253mod mymod {
2254    pub fn add(x, y) { x + y }
2255    fn private_func() { 42 }
2256    use other_mod
2257}
2258"#;
2259        let prog = parse_source(source);
2260
2261        assert!(!prog.statements.is_empty());
2262
2263        let stmt = &prog.statements[0].0;
2264        match stmt {
2265            ProgramStatement::ModuleDefinition { body, .. } => {
2266                // Should have at least 2 items
2267                let body = body.as_ref().expect("Expected inline module body");
2268                assert!(body.len() >= 2);
2269            }
2270            _ => panic!("Expected ModuleDefinition"),
2271        }
2272    }
2273
2274    #[test]
2275    fn test_parse_external_module() {
2276        // External file module: mod foo; (no body)
2277        let source = "mod external_module";
2278        let prog = parse_source(source);
2279
2280        assert!(!prog.statements.is_empty());
2281
2282        let stmt = &prog.statements[0].0;
2283        match stmt {
2284            ProgramStatement::ModuleDefinition {
2285                visibility,
2286                name,
2287                body,
2288            } => {
2289                assert_eq!(*visibility, Visibility::Private);
2290                assert_eq!(name.as_str(), "external_module");
2291                // External module should have None body
2292                assert!(body.is_none(), "External module should have None body");
2293            }
2294            _ => panic!("Expected ModuleDefinition, got {:?}", stmt),
2295        }
2296    }
2297
2298    #[test]
2299    fn test_external_module_with_use_generates_correct_ast() {
2300        // Simulate external module + use statement
2301        // This test verifies that the AST is correctly generated when combining
2302        // external file modules with use statements
2303        use crate::ast::program::expr_from_program;
2304
2305        // First, test an inline module with use statement (this should work)
2306        let inline_source = r#"
2307mod mymod {
2308    pub fn add(x, y) { x + y }
2309}
2310use mymod::add
2311fn dsp() { add(1.0, 2.0) }
2312"#;
2313        let inline_prog = parse_source(inline_source);
2314        let (expr, module_info, errs) = expr_from_program(inline_prog, PathBuf::from("test.mmm"));
2315
2316        // Check that module_info has the use alias
2317        assert!(
2318            module_info.use_alias_map.contains_key(&"add".to_symbol()),
2319            "use_alias_map should contain 'add'"
2320        );
2321        assert_eq!(
2322            module_info.use_alias_map.get(&"add".to_symbol()).copied(),
2323            Some("mymod$add".to_symbol())
2324        );
2325
2326        // Check that visibility_map has the function
2327        assert!(
2328            module_info
2329                .visibility_map
2330                .contains_key(&"mymod$add".to_symbol()),
2331            "visibility_map should contain 'mymod$add'"
2332        );
2333        assert!(
2334            *module_info
2335                .visibility_map
2336                .get(&"mymod$add".to_symbol())
2337                .unwrap(),
2338            "mymod$add should be public"
2339        );
2340
2341        // The AST should contain 'mymod$add' as a LetRec
2342        let ast_string = format!("{:?}", expr.to_expr());
2343        assert!(
2344            ast_string.contains("mymod$add"),
2345            "AST should contain mymod$add, but got: {}",
2346            ast_string
2347        );
2348    }
2349
2350    #[test]
2351    fn test_parse_pub_use() {
2352        let source = "pub use modA::funcB";
2353        let prog = parse_source(source);
2354
2355        assert!(!prog.statements.is_empty());
2356
2357        let stmt = &prog.statements[0].0;
2358        match stmt {
2359            ProgramStatement::UseStatement {
2360                visibility,
2361                path,
2362                target,
2363            } => {
2364                assert_eq!(*visibility, Visibility::Public);
2365                assert_eq!(path.segments.len(), 2);
2366                assert_eq!(path.segments[0].as_str(), "modA");
2367                assert_eq!(path.segments[1].as_str(), "funcB");
2368                assert!(matches!(target, UseTarget::Single));
2369            }
2370            _ => panic!("Expected UseStatement, got {:?}", stmt),
2371        }
2372    }
2373
2374    #[test]
2375    fn test_parse_match_expr() {
2376        let source = "fn test_match(num) { match num { 0 => 100, 1 => 200, _ => 300 } }";
2377        let prog = parse_source(source);
2378
2379        assert!(!prog.statements.is_empty());
2380        let stmt = &prog.statements[0].0;
2381        match stmt {
2382            ProgramStatement::FnDefinition { body, .. } => {
2383                let body_expr = body.to_expr();
2384                match body_expr {
2385                    Expr::Match(scrutinee, arms) => {
2386                        // Check scrutinee is a variable
2387                        match scrutinee.to_expr() {
2388                            Expr::Var(name) => assert_eq!(name.as_str(), "num"),
2389                            other => panic!("Expected Var, got {other:?}"),
2390                        }
2391
2392                        // Check arms
2393                        assert_eq!(arms.len(), 3);
2394
2395                        // Check first arm: 0 => 100
2396                        match &arms[0].pattern {
2397                            crate::ast::MatchPattern::Literal(crate::ast::Literal::Int(0)) => {}
2398                            other => panic!("Expected pattern Literal(Int(0)), got {:?}", other),
2399                        }
2400                        // Body literals are converted to Float in mimium
2401                        match arms[0].body.to_expr() {
2402                            Expr::Literal(crate::ast::Literal::Float(s)) => {
2403                                assert_eq!(s.as_str(), "100");
2404                            }
2405                            other => panic!("Expected body Literal(Float(100)), got {:?}", other),
2406                        }
2407
2408                        // Check second arm: 1 => 200
2409                        match &arms[1].pattern {
2410                            crate::ast::MatchPattern::Literal(crate::ast::Literal::Int(1)) => {}
2411                            other => panic!("Expected pattern Literal(Int(1)), got {:?}", other),
2412                        }
2413                        match arms[1].body.to_expr() {
2414                            Expr::Literal(crate::ast::Literal::Float(s)) => {
2415                                assert_eq!(s.as_str(), "200");
2416                            }
2417                            other => panic!("Expected body Literal(Float(200)), got {:?}", other),
2418                        }
2419
2420                        // Check third arm: _ => 300
2421                        match &arms[2].pattern {
2422                            crate::ast::MatchPattern::Wildcard => {}
2423                            other => panic!("Expected Wildcard, got {:?}", other),
2424                        }
2425                        match arms[2].body.to_expr() {
2426                            Expr::Literal(crate::ast::Literal::Float(s)) => {
2427                                assert_eq!(s.as_str(), "300");
2428                            }
2429                            other => panic!("Expected body Literal(Float(300)), got {:?}", other),
2430                        }
2431                    }
2432                    other => panic!("Expected Match expr, got {:?}", other),
2433                }
2434            }
2435            _ => panic!("Expected FnDefinition, got {:?}", stmt),
2436        }
2437    }
2438
2439    #[test]
2440    fn test_parse_pipe_and_macro_pipe_share_precedence_and_associate_left() {
2441        let source = "fn test(a, b, c) { a ||> b |> c }";
2442        let prog = parse_source(source);
2443
2444        let stmt = &prog.statements[0].0;
2445        match stmt {
2446            ProgramStatement::FnDefinition { body, .. } => match body.to_expr() {
2447                Expr::BinOp(lhs, (Op::Pipe, _), rhs) => {
2448                    assert!(matches!(rhs.to_expr(), Expr::Var(name) if name.as_str() == "c"));
2449                    match lhs.to_expr() {
2450                        Expr::BinOp(inner_lhs, (Op::PipeMacro, _), inner_rhs) => {
2451                            assert!(matches!(
2452                                inner_lhs.to_expr(),
2453                                Expr::Var(name) if name.as_str() == "a"
2454                            ));
2455                            assert!(matches!(
2456                                inner_rhs.to_expr(),
2457                                Expr::Var(name) if name.as_str() == "b"
2458                            ));
2459                        }
2460                        other => panic!("Expected nested PipeMacro on LHS, got {other:?}"),
2461                    }
2462                }
2463                other => panic!("Expected top-level Pipe, got {other:?}"),
2464            },
2465            _ => panic!("Expected FnDefinition, got {stmt:?}"),
2466        }
2467    }
2468}