Skip to main content

tupa_parser/
lib.rs

1use serde::Serialize;
2use thiserror::Error;
3pub use tupa_lexer::{lex_with_spans, LexerError, Span, Token, TokenSpan};
4
5#[derive(Debug, Clone, PartialEq, Serialize)]
6pub struct Program {
7    pub items: Vec<Item>,
8}
9
10#[derive(Debug, Clone, PartialEq, Serialize)]
11pub enum Item {
12    Function(Function),
13    Enum(EnumDef),
14    Trait(TraitDef),
15    Pipeline(PipelineDecl),
16}
17
18#[derive(Debug, Clone, PartialEq, Serialize)]
19pub struct Attribute {
20    pub name: String,
21    pub args: Vec<String>,
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize)]
25pub struct ExternalSpec {
26    pub python: Option<String>,
27    pub effects: Vec<String>,
28}
29
30#[derive(Debug, Clone, PartialEq, Serialize)]
31pub struct EnumDef {
32    pub name: String,
33    pub generics: Vec<String>,
34    pub variants: Vec<EnumVariant>,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize)]
38pub struct EnumVariant {
39    pub name: String,
40    pub args: Vec<Type>,
41}
42
43#[derive(Debug, Clone, PartialEq, Serialize)]
44pub struct TraitDef {
45    pub name: String,
46    pub methods: Vec<Function>,
47}
48
49#[derive(Debug, Clone, PartialEq, Serialize)]
50pub struct PipelineDecl {
51    pub name: String,
52    pub attrs: Vec<Attribute>,
53    pub seed: Option<u64>,
54    pub input_ty: Type,
55    pub output_ty: Option<Type>,
56    pub constraints: Vec<Constraint>,
57    pub steps: Vec<PipelineStep>,
58    pub validation: Option<Block>,
59    pub span: Span,
60}
61
62#[derive(Debug, Clone, PartialEq, Serialize)]
63pub enum Comparator {
64    Lt,
65    Le,
66    Eq,
67    Ge,
68    Gt,
69}
70
71#[derive(Debug, Clone, PartialEq, Serialize)]
72pub struct Constraint {
73    pub metric: String,
74    pub comparator: Comparator,
75    pub threshold: f64,
76    pub span: Span,
77}
78
79#[derive(Debug, Clone, PartialEq, Serialize)]
80pub struct PipelineStep {
81    pub name: String,
82    pub body: Expr,
83    pub span: Span,
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize)]
87pub struct Function {
88    pub name: String,
89    pub params: Vec<Param>,
90    pub return_type: Option<Type>,
91    pub body: Vec<Stmt>,
92    pub attrs: Vec<Attribute>,
93    pub external_spec: Option<ExternalSpec>,
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize)]
97pub struct Param {
98    pub name: String,
99    pub ty: Type,
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize)]
103pub enum Stmt {
104    Let {
105        name: String,
106        ty: Option<Type>,
107        expr: Expr,
108    },
109    Return(Option<Expr>),
110    While {
111        condition: Expr,
112        body: Block,
113    },
114    For {
115        name: String,
116        iter: Expr,
117        body: Block,
118    },
119    Break,
120    Continue,
121    Expr(Expr),
122    Lambda {
123        params: Vec<String>,
124        body: Box<Expr>,
125    },
126}
127#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
128pub struct TensorType {
129    pub dtype: String,
130    pub shape: Vec<Option<i64>>,
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize)]
134pub enum Type {
135    Ident(String),
136    Generic {
137        name: String,
138        args: Vec<Type>,
139    },
140    Record(Vec<(String, Type)>),
141    Tuple(Vec<Type>),
142    Safe {
143        base: Box<Type>,
144        constraints: Vec<String>,
145    },
146    Array {
147        elem: Box<Type>,
148        len: i64,
149    },
150    Slice {
151        elem: Box<Type>,
152    },
153    Func {
154        params: Vec<Type>,
155        ret: Box<Type>,
156    },
157    Tensor(TensorType),
158    Unit,
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize)]
162pub struct Expr {
163    pub kind: ExprKind,
164    pub span: Span,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize)]
168pub enum ExprKind {
169    Lambda {
170        params: Vec<String>,
171        body: Box<Expr>,
172    },
173    Int(i64),
174    Float(f64),
175    Str(String),
176    Bool(bool),
177    Null,
178    Ident(String),
179    Tuple(Vec<Expr>),
180    RecordLiteral(Vec<(String, Expr)>),
181    Assign {
182        name: String,
183        expr: Box<Expr>,
184    },
185    AssignIndex {
186        expr: Box<Expr>,
187        index: Box<Expr>,
188        value: Box<Expr>,
189    },
190    ArrayLiteral(Vec<Expr>),
191    Call {
192        callee: Box<Expr>,
193        args: Vec<Expr>,
194    },
195    Field {
196        expr: Box<Expr>,
197        field: FieldAccess,
198    },
199    Index {
200        expr: Box<Expr>,
201        index: Box<Expr>,
202    },
203    Await(Box<Expr>),
204    Block(Block),
205    If {
206        condition: Box<Expr>,
207        then_branch: Block,
208        else_branch: Option<ElseBranch>,
209    },
210    Match {
211        expr: Box<Expr>,
212        arms: Vec<MatchArm>,
213    },
214    Unary {
215        op: UnaryOp,
216        expr: Box<Expr>,
217    },
218    Binary {
219        op: BinaryOp,
220        left: Box<Expr>,
221        right: Box<Expr>,
222    },
223}
224
225pub type Block = Vec<Stmt>;
226
227#[derive(Debug, Clone, PartialEq, Serialize)]
228pub enum ElseBranch {
229    Block(Block),
230    If(Box<Expr>),
231}
232
233#[derive(Debug, Clone, PartialEq, Serialize)]
234pub struct MatchArm {
235    pub pattern: Pattern,
236    pub pattern_span: Span,
237    pub guard: Option<Expr>,
238    pub expr: Expr,
239}
240
241#[derive(Debug, Clone, PartialEq, Serialize)]
242pub enum Pattern {
243    Wildcard,
244    Int(i64),
245    Str(String),
246    Bool(bool),
247    Ident(String),
248    Tuple(Vec<Pattern>),
249    Constructor { name: String, args: Vec<Pattern> },
250}
251
252#[derive(Debug, Clone, PartialEq, Serialize)]
253pub enum FieldAccess {
254    Ident(String),
255    Index(i64),
256}
257
258#[derive(Debug, Clone, PartialEq, Serialize)]
259pub enum UnaryOp {
260    Not,
261    Neg,
262}
263
264#[derive(Debug, Clone, PartialEq, Serialize)]
265pub enum BinaryOp {
266    Range,
267    Or,
268    And,
269    Equal,
270    NotEqual,
271    Less,
272    LessEqual,
273    Greater,
274    GreaterEqual,
275    Add,
276    Sub,
277    Mul,
278    Div,
279    Mod,
280    Pow,
281}
282
283#[derive(Debug, Error)]
284pub enum ParserError {
285    #[error("lexer error: {0}")]
286    Lexer(#[from] LexerError),
287    #[error("unexpected token {0:?} at {1:?}")]
288    Unexpected(Token, Span),
289    #[error("expected ';' after expression")]
290    MissingSemicolon(Span),
291    #[error("unexpected end of input at position {0}")]
292    Eof(usize),
293}
294
295impl Expr {
296    fn new(kind: ExprKind, span: Span) -> Self {
297        Self { kind, span }
298    }
299}
300
301fn merge_span(start: Span, end: Span) -> Span {
302    Span {
303        start: start.start,
304        end: end.end,
305    }
306}
307
308fn token_to_string(token: &Token) -> String {
309    match token {
310        Token::Ident(s) | Token::Int(s) | Token::Float(s) | Token::Str(s) => s.clone(),
311        Token::Fn => "fn".to_string(),
312        Token::Enum => "enum".to_string(),
313        Token::Trait => "trait".to_string(),
314        Token::Pipeline => "pipeline".to_string(),
315        Token::Step => "step".to_string(),
316        Token::Let => "let".to_string(),
317        Token::Return => "return".to_string(),
318        Token::If => "if".to_string(),
319        Token::Else => "else".to_string(),
320        Token::Match => "match".to_string(),
321        Token::While => "while".to_string(),
322        Token::For => "for".to_string(),
323        Token::Break => "break".to_string(),
324        Token::Continue => "continue".to_string(),
325        Token::In => "in".to_string(),
326        Token::Await => "await".to_string(),
327        Token::True => "true".to_string(),
328        Token::False => "false".to_string(),
329        Token::Null => "null".to_string(),
330        Token::LParen => "(".to_string(),
331        Token::RParen => ")".to_string(),
332        Token::LBrace => "{".to_string(),
333        Token::RBrace => "}".to_string(),
334        Token::LBracket => "[".to_string(),
335        Token::RBracket => "]".to_string(),
336        Token::Semicolon => ";".to_string(),
337        Token::Comma => ",".to_string(),
338        Token::Colon => ":".to_string(),
339        Token::Equal => "=".to_string(),
340        Token::Arrow => "=>".to_string(),
341        Token::ThinArrow => "->".to_string(),
342        Token::EqualEqual => "==".to_string(),
343        Token::BangEqual => "!=".to_string(),
344        Token::Less => "<".to_string(),
345        Token::LessEqual => "<=".to_string(),
346        Token::Greater => ">".to_string(),
347        Token::GreaterEqual => ">=".to_string(),
348        Token::AndAnd => "&&".to_string(),
349        Token::OrOr => "||".to_string(),
350        Token::Plus => "+".to_string(),
351        Token::PlusEqual => "+=".to_string(),
352        Token::Minus => "-".to_string(),
353        Token::MinusEqual => "-=".to_string(),
354        Token::Star => "*".to_string(),
355        Token::StarEqual => "*=".to_string(),
356        Token::Slash => "/".to_string(),
357        Token::SlashEqual => "/=".to_string(),
358        Token::DoubleStar => "**".to_string(),
359        Token::DotDot => "..".to_string(),
360        Token::Dot => ".".to_string(),
361        Token::Bang => "!".to_string(),
362        Token::Pipe => "|".to_string(),
363        Token::At => "@".to_string(),
364        Token::Percent => "%".to_string(),
365        Token::PercentEqual => "%=".to_string(),
366    }
367}
368
369fn parse_external_effect_item(tokens: &[String]) -> Option<String> {
370    if tokens.is_empty() {
371        return None;
372    }
373
374    if tokens.len() == 1 {
375        return Some(tokens[0].clone());
376    }
377
378    if tokens[0] == "ExternalCall"
379        && tokens.get(1).is_some_and(|t| t == "(")
380        && tokens.last().is_some_and(|t| t == ")")
381        && tokens.len() >= 4
382    {
383        let inner = tokens[2..tokens.len() - 1].join("");
384        return Some(format!("ExternalCall({inner})"));
385    }
386
387    Some(tokens.join(""))
388}
389
390fn parse_external_spec(args: &[String]) -> ExternalSpec {
391    let mut spec = ExternalSpec {
392        python: None,
393        effects: Vec::new(),
394    };
395
396    if args.is_empty() {
397        return spec;
398    }
399
400    if !args.iter().any(|a| a == "=") {
401        spec.python = args.first().cloned();
402        return spec;
403    }
404
405    let mut i = 0;
406    while i < args.len() {
407        let key = &args[i];
408
409        if i + 1 >= args.len() || args[i + 1] != "=" {
410            i += 1;
411            continue;
412        }
413
414        if key == "python" {
415            if let Some(value) = args.get(i + 2) {
416                spec.python = Some(value.clone());
417            }
418            i += 3;
419            continue;
420        }
421
422        if key == "effects" {
423            if args.get(i + 2).is_some_and(|t| t == "[") {
424                let mut j = i + 3;
425                let mut bracket_depth = 1;
426                let mut paren_depth = 0;
427                let mut current = Vec::new();
428
429                while j < args.len() {
430                    let tok = &args[j];
431                    match tok.as_str() {
432                        "[" => {
433                            bracket_depth += 1;
434                            if bracket_depth > 1 {
435                                current.push(tok.clone());
436                            }
437                        }
438                        "]" => {
439                            bracket_depth -= 1;
440                            if bracket_depth == 0 {
441                                if let Some(effect) = parse_external_effect_item(&current) {
442                                    spec.effects.push(effect);
443                                }
444                                break;
445                            }
446                            current.push(tok.clone());
447                        }
448                        "," if bracket_depth == 1 && paren_depth == 0 => {
449                            if let Some(effect) = parse_external_effect_item(&current) {
450                                spec.effects.push(effect);
451                            }
452                            current.clear();
453                        }
454                        "(" => {
455                            paren_depth += 1;
456                            current.push(tok.clone());
457                        }
458                        ")" => {
459                            if paren_depth > 0 {
460                                paren_depth -= 1;
461                            }
462                            current.push(tok.clone());
463                        }
464                        _ => current.push(tok.clone()),
465                    }
466                    j += 1;
467                }
468
469                i = j + 1;
470                continue;
471            }
472
473            if let Some(value) = args.get(i + 2) {
474                spec.effects.push(value.clone());
475            }
476            i += 3;
477            continue;
478        }
479
480        i += 1;
481    }
482
483    spec
484}
485pub fn parse_program(input: &str) -> Result<Program, ParserError> {
486    let tokens = lex_with_spans(input)?;
487    let mut parser = Parser::new(tokens, input.len());
488    let mut items = Vec::new();
489
490    while !parser.is_eof() {
491        if parser.is_type_decl_start() {
492            parser.skip_type_decl()?;
493            continue;
494        }
495        if parser.is_extern_decl_start() {
496            parser.skip_extern_decl()?;
497            continue;
498        }
499        items.push(parser.parse_item()?);
500    }
501
502    Ok(Program { items })
503}
504
505struct Parser {
506    tokens: Vec<TokenSpan>,
507    pos: usize,
508    eof_pos: usize,
509}
510
511impl Parser {
512    fn new(tokens: Vec<TokenSpan>, eof_pos: usize) -> Self {
513        Self {
514            tokens,
515            pos: 0,
516            eof_pos,
517        }
518    }
519
520    fn is_eof(&self) -> bool {
521        self.pos >= self.tokens.len()
522    }
523
524    fn peek(&self) -> Option<&Token> {
525        self.tokens.get(self.pos).map(|t| &t.token)
526    }
527
528    fn peek_next(&self) -> Option<&Token> {
529        self.tokens.get(self.pos + 1).map(|t| &t.token)
530    }
531
532    fn is_type_decl_start(&self) -> bool {
533        matches!(
534            (self.peek(), self.peek_next()),
535            (Some(Token::Ident(keyword)), Some(Token::Ident(_))) if keyword == "type"
536        )
537    }
538
539    fn skip_type_decl(&mut self) -> Result<(), ParserError> {
540        match self.next() {
541            Some(TokenSpan {
542                token: Token::Ident(keyword),
543                ..
544            }) if keyword == "type" => {}
545            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
546            None => return Err(ParserError::Eof(self.eof_pos)),
547        }
548
549        match self.next() {
550            Some(TokenSpan {
551                token: Token::Ident(_),
552                ..
553            }) => {}
554            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
555            None => return Err(ParserError::Eof(self.eof_pos)),
556        }
557
558        self.expect(Token::LBrace)?;
559        let mut depth = 1usize;
560        while depth > 0 {
561            match self.next() {
562                Some(TokenSpan {
563                    token: Token::LBrace,
564                    ..
565                }) => depth += 1,
566                Some(TokenSpan {
567                    token: Token::RBrace,
568                    ..
569                }) => depth = depth.saturating_sub(1),
570                Some(_) => {}
571                None => return Err(ParserError::Eof(self.eof_pos)),
572            }
573        }
574
575        if matches!(self.peek(), Some(Token::Comma | Token::Semicolon)) {
576            self.next();
577        }
578
579        Ok(())
580    }
581
582    fn is_extern_decl_start(&self) -> bool {
583        matches!(
584            (self.peek(), self.peek_next()),
585            (Some(Token::Ident(_)), Some(Token::Fn))
586        )
587    }
588    fn skip_extern_decl(&mut self) -> Result<(), ParserError> {
589        match self.next() {
590            Some(TokenSpan {
591                token: Token::Ident(_),
592                ..
593            }) => {}
594            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
595            None => return Err(ParserError::Eof(self.eof_pos)),
596        }
597        self.expect(Token::Fn)?;
598        loop {
599            match self.next() {
600                Some(TokenSpan {
601                    token: Token::Semicolon,
602                    ..
603                }) => break,
604                Some(_) => {}
605                None => return Err(ParserError::Eof(self.eof_pos)),
606            }
607        }
608        Ok(())
609    }
610    fn is_index_assignment(&self) -> bool {
611        if !matches!(self.peek(), Some(Token::Ident(_))) {
612            return false;
613        }
614        if !matches!(self.peek_next(), Some(Token::LBracket)) {
615            return false;
616        }
617        let mut depth = 0usize;
618        let mut idx = self.pos + 1;
619        while idx < self.tokens.len() {
620            match &self.tokens[idx].token {
621                Token::LBracket => depth += 1,
622                Token::RBracket => {
623                    if depth == 1 {
624                        return matches!(
625                            self.tokens.get(idx + 1).map(|t| &t.token),
626                            Some(Token::Equal)
627                        );
628                    }
629                    depth = depth.saturating_sub(1);
630                }
631                _ => {}
632            }
633            idx += 1;
634        }
635        false
636    }
637
638    fn next(&mut self) -> Option<TokenSpan> {
639        let tok = self.tokens.get(self.pos).cloned();
640        self.pos += 1;
641        tok
642    }
643
644    fn expect(&mut self, expected: Token) -> Result<(), ParserError> {
645        match self.next() {
646            Some(TokenSpan { token, .. }) if token == expected => Ok(()),
647            Some(TokenSpan { token, span }) => Err(ParserError::Unexpected(token, span)),
648            None => Err(ParserError::Eof(self.eof_pos)),
649        }
650    }
651
652    fn expect_span(&mut self, expected: Token) -> Result<Span, ParserError> {
653        match self.next() {
654            Some(TokenSpan { token, span }) if token == expected => Ok(span),
655            Some(TokenSpan { token, span }) => Err(ParserError::Unexpected(token, span)),
656            None => Err(ParserError::Eof(self.eof_pos)),
657        }
658    }
659
660    fn parse_item(&mut self) -> Result<Item, ParserError> {
661        let mut attrs = Vec::new();
662        while matches!(self.peek(), Some(Token::At)) {
663            attrs.push(self.parse_attribute()?);
664        }
665        match self.peek() {
666            Some(Token::Fn) => Ok(Item::Function(self.parse_function(attrs)?)),
667            Some(Token::Enum) => Ok(Item::Enum(self.parse_enum()?)),
668            Some(Token::Trait) => Ok(Item::Trait(self.parse_trait()?)),
669            Some(Token::Pipeline) => Ok(Item::Pipeline(self.parse_pipeline(attrs)?)),
670            Some(token) => {
671                let span = self.tokens.get(self.pos).map(|t| t.span).unwrap_or(Span {
672                    start: self.eof_pos,
673                    end: self.eof_pos,
674                });
675                Err(ParserError::Unexpected(token.clone(), span))
676            }
677            None => Err(ParserError::Eof(self.eof_pos)),
678        }
679    }
680
681    fn parse_attribute(&mut self) -> Result<Attribute, ParserError> {
682        self.expect(Token::At)?;
683        let name = match self.next() {
684            Some(TokenSpan {
685                token: Token::Ident(name),
686                ..
687            }) => name,
688            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
689            None => return Err(ParserError::Eof(self.eof_pos)),
690        };
691        let mut args = Vec::new();
692        if matches!(self.peek(), Some(Token::LParen)) {
693            self.expect(Token::LParen)?;
694
695            let mut depth = 0;
696            while let Some(tok) = self.peek() {
697                if depth == 0 && *tok == Token::RParen {
698                    break;
699                }
700
701                let token_span = self.next().ok_or(ParserError::Eof(self.eof_pos))?;
702                let token = token_span.token;
703
704                match &token {
705                    Token::LParen | Token::LBracket | Token::LBrace => {
706                        depth += 1;
707                        args.push(token_to_string(&token));
708                    }
709                    Token::RParen | Token::RBracket | Token::RBrace => {
710                        if depth > 0 {
711                            depth -= 1;
712                        }
713                        args.push(token_to_string(&token));
714                    }
715                    Token::Comma => {
716                        if depth > 0 {
717                            args.push(",".to_string());
718                        }
719                    }
720                    _ => {
721                        args.push(token_to_string(&token));
722                    }
723                }
724            }
725
726            self.expect(Token::RParen)?;
727        }
728        Ok(Attribute { name, args })
729    }
730
731    fn parse_function(&mut self, attrs: Vec<Attribute>) -> Result<Function, ParserError> {
732        self.expect(Token::Fn)?;
733        let name = match self.next() {
734            Some(TokenSpan {
735                token: Token::Ident(name),
736                ..
737            }) => name,
738            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
739            None => return Err(ParserError::Eof(self.eof_pos)),
740        };
741        self.expect(Token::LParen)?;
742        let params = self.parse_params()?;
743        self.expect(Token::RParen)?;
744        let return_type = if matches!(self.peek(), Some(Token::Colon)) {
745            self.next();
746            Some(self.parse_type()?)
747        } else {
748            None
749        };
750        let body = self.parse_block()?;
751        let external_spec = attrs
752            .iter()
753            .find(|attr| attr.name == "external")
754            .map(|attr| parse_external_spec(&attr.args));
755
756        Ok(Function {
757            name,
758            params,
759            return_type,
760            body,
761            attrs,
762            external_spec,
763        })
764    }
765
766    fn parse_enum(&mut self) -> Result<EnumDef, ParserError> {
767        self.expect(Token::Enum)?;
768        let name = match self.next() {
769            Some(TokenSpan {
770                token: Token::Ident(name),
771                ..
772            }) => name,
773            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
774            None => return Err(ParserError::Eof(self.eof_pos)),
775        };
776        let mut generics = Vec::new();
777        if matches!(self.peek(), Some(Token::Less)) {
778            self.next();
779            loop {
780                let param = match self.next() {
781                    Some(TokenSpan {
782                        token: Token::Ident(name),
783                        ..
784                    }) => name,
785                    Some(TokenSpan { token, span }) => {
786                        return Err(ParserError::Unexpected(token, span))
787                    }
788                    None => return Err(ParserError::Eof(self.eof_pos)),
789                };
790                generics.push(param);
791                if matches!(self.peek(), Some(Token::Comma)) {
792                    self.next();
793                    if matches!(self.peek(), Some(Token::Greater)) {
794                        break;
795                    }
796                } else {
797                    break;
798                }
799            }
800            self.expect(Token::Greater)?;
801        }
802        self.expect(Token::LBrace)?;
803        let mut variants = Vec::new();
804        while let Some(tok) = self.peek() {
805            if *tok == Token::RBrace {
806                break;
807            }
808            match self.next() {
809                Some(TokenSpan {
810                    token: Token::Ident(variant),
811                    ..
812                }) => {
813                    let args = if matches!(self.peek(), Some(Token::LParen)) {
814                        self.next();
815                        let mut args = Vec::new();
816                        if !matches!(self.peek(), Some(Token::RParen)) {
817                            loop {
818                                args.push(self.parse_type()?);
819                                if matches!(self.peek(), Some(Token::Comma)) {
820                                    self.next();
821                                    if matches!(self.peek(), Some(Token::RParen)) {
822                                        break;
823                                    }
824                                } else {
825                                    break;
826                                }
827                            }
828                        }
829                        self.expect(Token::RParen)?;
830                        args
831                    } else {
832                        Vec::new()
833                    };
834                    variants.push(EnumVariant {
835                        name: variant,
836                        args,
837                    });
838                }
839                Some(TokenSpan { token, span }) => {
840                    return Err(ParserError::Unexpected(token, span))
841                }
842                None => return Err(ParserError::Eof(self.eof_pos)),
843            }
844            if let Some(Token::Comma) = self.peek() {
845                self.next();
846            }
847        }
848        self.expect(Token::RBrace)?;
849        Ok(EnumDef {
850            name,
851            generics,
852            variants,
853        })
854    }
855
856    fn parse_trait(&mut self) -> Result<TraitDef, ParserError> {
857        self.expect(Token::Trait)?;
858        let name = match self.next() {
859            Some(TokenSpan {
860                token: Token::Ident(name),
861                ..
862            }) => name,
863            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
864            None => return Err(ParserError::Eof(self.eof_pos)),
865        };
866        self.expect(Token::LBrace)?;
867        let mut methods = Vec::new();
868        while let Some(token) = self.peek().cloned() {
869            if token == Token::RBrace {
870                break;
871            }
872
873            let mut attrs = Vec::new();
874            while matches!(self.peek(), Some(Token::At)) {
875                attrs.push(self.parse_attribute()?);
876            }
877
878            if let Some(Token::Fn) = self.peek() {
879                methods.push(self.parse_function(attrs)?);
880            } else {
881                let span = self.tokens.get(self.pos).map(|t| t.span).unwrap_or(Span {
882                    start: self.eof_pos,
883                    end: self.eof_pos,
884                });
885                return Err(ParserError::Unexpected(token, span));
886            }
887        }
888        self.expect(Token::RBrace)?;
889        Ok(TraitDef { name, methods })
890    }
891
892    fn parse_pipeline(&mut self, mut attrs: Vec<Attribute>) -> Result<PipelineDecl, ParserError> {
893        self.expect(Token::Pipeline)?;
894        let (name, _name_span) = match self.next() {
895            Some(TokenSpan {
896                token: Token::Ident(name),
897                span,
898            }) => (name, span),
899            Some(TokenSpan { token, span }) => return Err(ParserError::Unexpected(token, span)),
900            None => return Err(ParserError::Eof(self.eof_pos)),
901        };
902
903        while matches!(self.peek(), Some(Token::At)) {
904            attrs.push(self.parse_attribute()?);
905        }
906
907        let mut seed = None;
908        for attr in &attrs {
909            if attr.name == "deterministic" {
910                for (i, arg) in attr.args.iter().enumerate() {
911                    if arg == "seed" {
912                        if let Some(eq) = attr.args.get(i + 1) {
913                            if eq == "=" {
914                                if let Some(val) = attr.args.get(i + 2) {
915                                    if let Ok(n) = val.parse::<u64>() {
916                                        seed = Some(n);
917                                    } else if let Ok(f) = val.parse::<f64>() {
918                                        seed = Some(f as u64);
919                                    }
920                                }
921                            }
922                        }
923                    }
924                }
925            }
926        }
927
928        let start_span = self.expect_span(Token::LBrace)?;
929
930        let mut input_ty = Type::Unit;
931        let mut output_ty = None;
932        let mut constraints = Vec::new();
933        let mut steps = Vec::new();
934        let mut validation = None;
935
936        while !matches!(self.peek(), Some(Token::RBrace)) {
937            let field_name = match self.peek() {
938                Some(Token::Ident(n)) => n.clone(),
939                Some(token) => {
940                    let span = self.tokens.get(self.pos).map(|t| t.span).unwrap_or(Span {
941                        start: self.eof_pos,
942                        end: self.eof_pos,
943                    });
944                    return Err(ParserError::Unexpected(token.clone(), span));
945                }
946                None => return Err(ParserError::Eof(self.eof_pos)),
947            };
948            self.next(); // consume field name
949            self.expect(Token::Colon)?;
950
951            match field_name.as_str() {
952                "input" => {
953                    input_ty = self.parse_type()?;
954                }
955                "output" => {
956                    output_ty = Some(self.parse_type()?);
957                }
958                "constraints" => {
959                    self.expect(Token::LBracket)?;
960                    while !matches!(self.peek(), Some(Token::RBracket)) {
961                        let start = if let Some(ts) = self.peek() {
962                            match ts {
963                                Token::LBrace => self.expect_span(Token::LBrace)?,
964                                _ => {
965                                    let span =
966                                        self.tokens.get(self.pos).map(|t| t.span).unwrap_or(Span {
967                                            start: self.eof_pos,
968                                            end: self.eof_pos,
969                                        });
970                                    return Err(ParserError::Unexpected(ts.clone(), span));
971                                }
972                            }
973                        } else {
974                            return Err(ParserError::Eof(self.eof_pos));
975                        };
976
977                        // metric: "..."
978                        match self.next() {
979                            Some(TokenSpan {
980                                token: Token::Ident(name),
981                                ..
982                            }) if name == "metric" => {}
983                            Some(TokenSpan { token, span }) => {
984                                return Err(ParserError::Unexpected(token, span))
985                            }
986                            None => return Err(ParserError::Eof(self.eof_pos)),
987                        }
988                        self.expect(Token::Colon)?;
989                        let metric = match self.next() {
990                            Some(TokenSpan {
991                                token: Token::Str(value),
992                                ..
993                            }) => value,
994                            Some(TokenSpan { token, span }) => {
995                                return Err(ParserError::Unexpected(token, span))
996                            }
997                            None => return Err(ParserError::Eof(self.eof_pos)),
998                        };
999
1000                        if matches!(self.peek(), Some(Token::Comma)) {
1001                            self.next();
1002                        }
1003
1004                        // comparator: threshold
1005                        let (comparator, threshold) = match self.next() {
1006                            Some(TokenSpan {
1007                                token: Token::Ident(key),
1008                                span,
1009                            }) => {
1010                                self.expect(Token::Colon)?;
1011                                let value = match self.next() {
1012                                    Some(TokenSpan {
1013                                        token: Token::Float(v),
1014                                        ..
1015                                    }) => v.parse::<f64>().unwrap_or(0.0),
1016                                    Some(TokenSpan {
1017                                        token: Token::Int(v),
1018                                        ..
1019                                    }) => v.parse::<f64>().unwrap_or(0.0),
1020                                    Some(TokenSpan { token, span }) => {
1021                                        return Err(ParserError::Unexpected(token, span))
1022                                    }
1023                                    None => return Err(ParserError::Eof(self.eof_pos)),
1024                                };
1025                                let cmp = match key.as_str() {
1026                                    "lt" => Comparator::Lt,
1027                                    "le" => Comparator::Le,
1028                                    "eq" => Comparator::Eq,
1029                                    "ge" => Comparator::Ge,
1030                                    "gt" => Comparator::Gt,
1031                                    _ => {
1032                                        return Err(ParserError::Unexpected(
1033                                            Token::Ident(key),
1034                                            span,
1035                                        ))
1036                                    }
1037                                };
1038                                (cmp, value)
1039                            }
1040                            Some(TokenSpan { token, span }) => {
1041                                return Err(ParserError::Unexpected(token, span))
1042                            }
1043                            None => return Err(ParserError::Eof(self.eof_pos)),
1044                        };
1045
1046                        self.expect(Token::RBrace)?;
1047                        constraints.push(Constraint {
1048                            metric,
1049                            comparator,
1050                            threshold,
1051                            span: start, // simplified span
1052                        });
1053
1054                        if matches!(self.peek(), Some(Token::Comma)) {
1055                            self.next();
1056                        }
1057                    }
1058                    self.expect(Token::RBracket)?;
1059                }
1060                "steps" => {
1061                    self.expect(Token::LBracket)?;
1062                    while !matches!(self.peek(), Some(Token::RBracket)) {
1063                        match self.next() {
1064                            Some(TokenSpan {
1065                                token: Token::Ident(s),
1066                                ..
1067                            }) if s == "step" => {} // allow "step" ident
1068                            Some(TokenSpan {
1069                                token: Token::Step, ..
1070                            }) => {} // allow Step token if exists
1071                            Some(TokenSpan { token, span }) => {
1072                                return Err(ParserError::Unexpected(token, span))
1073                            }
1074                            None => return Err(ParserError::Eof(self.eof_pos)),
1075                        }
1076                        self.expect(Token::LParen)?;
1077                        let (step_name, name_span) = match self.next() {
1078                            Some(TokenSpan {
1079                                token: Token::Str(value),
1080                                span,
1081                            }) => (value, span),
1082                            Some(TokenSpan {
1083                                token: Token::Ident(value),
1084                                span,
1085                            }) => (value, span),
1086                            Some(TokenSpan { token, span }) => {
1087                                return Err(ParserError::Unexpected(token, span))
1088                            }
1089                            None => return Err(ParserError::Eof(self.eof_pos)),
1090                        };
1091                        self.expect(Token::RParen)?;
1092                        let _ = self.expect_span(Token::LBrace)?;
1093                        let body = self.parse_expr()?;
1094                        let _ = self.expect_span(Token::RBrace)?;
1095
1096                        let body_span = body.span;
1097                        steps.push(PipelineStep {
1098                            name: step_name,
1099                            body,
1100                            span: merge_span(name_span, body_span),
1101                        });
1102
1103                        if matches!(self.peek(), Some(Token::Comma)) {
1104                            self.next();
1105                        }
1106                    }
1107                    self.expect(Token::RBracket)?;
1108                }
1109                "validation" => {
1110                    validation = Some(self.parse_block()?);
1111                }
1112                _ => {
1113                    let span = self.tokens.get(self.pos).map(|t| t.span).unwrap_or(Span {
1114                        start: self.eof_pos,
1115                        end: self.eof_pos,
1116                    });
1117                    return Err(ParserError::Unexpected(Token::Ident(field_name), span));
1118                }
1119            }
1120
1121            if matches!(self.peek(), Some(Token::Comma)) {
1122                self.next();
1123            }
1124        }
1125
1126        let end_span = self.expect_span(Token::RBrace)?;
1127
1128        Ok(PipelineDecl {
1129            name,
1130            attrs,
1131            seed,
1132            input_ty,
1133            output_ty,
1134            constraints,
1135            steps,
1136            validation,
1137            span: merge_span(start_span, end_span),
1138        })
1139    }
1140
1141    fn parse_block(&mut self) -> Result<Block, ParserError> {
1142        let (body, _) = self.parse_block_with_span()?;
1143        Ok(body)
1144    }
1145
1146    fn parse_block_with_span(&mut self) -> Result<(Block, Span), ParserError> {
1147        let start = self.expect_span(Token::LBrace)?;
1148        let mut body = Vec::new();
1149        while let Some(tok) = self.peek() {
1150            if *tok == Token::RBrace {
1151                break;
1152            }
1153            body.push(self.parse_stmt_in_block()?);
1154        }
1155        let end = self.expect_span(Token::RBrace)?;
1156        Ok((body, merge_span(start, end)))
1157    }
1158
1159    fn parse_stmt_in_block(&mut self) -> Result<Stmt, ParserError> {
1160        match self.peek() {
1161            Some(Token::Let) => {
1162                self.next();
1163                let name = match self.next() {
1164                    Some(TokenSpan {
1165                        token: Token::Ident(name),
1166                        ..
1167                    }) => name,
1168                    Some(TokenSpan { token, span }) => {
1169                        return Err(ParserError::Unexpected(token, span))
1170                    }
1171                    None => return Err(ParserError::Eof(self.eof_pos)),
1172                };
1173                let ty = if matches!(self.peek(), Some(Token::Colon)) {
1174                    self.next();
1175                    Some(self.parse_type()?)
1176                } else {
1177                    None
1178                };
1179                self.expect(Token::Equal)?;
1180                let expr = self.parse_expr()?;
1181                self.expect(Token::Semicolon)?;
1182                Ok(Stmt::Let { name, ty, expr })
1183            }
1184            Some(Token::Return) => {
1185                self.next();
1186                let expr = if matches!(self.peek(), Some(Token::Semicolon)) {
1187                    None
1188                } else {
1189                    Some(self.parse_expr()?)
1190                };
1191                self.expect(Token::Semicolon)?;
1192                Ok(Stmt::Return(expr))
1193            }
1194            Some(Token::While) => {
1195                self.next();
1196                let condition = self.parse_expr()?;
1197                let body = self.parse_block()?;
1198                Ok(Stmt::While { condition, body })
1199            }
1200            Some(Token::For) => {
1201                self.next();
1202                let name = match self.next() {
1203                    Some(TokenSpan {
1204                        token: Token::Ident(name),
1205                        ..
1206                    }) => name,
1207                    Some(TokenSpan { token, span }) => {
1208                        return Err(ParserError::Unexpected(token, span))
1209                    }
1210                    None => return Err(ParserError::Eof(self.eof_pos)),
1211                };
1212                self.expect(Token::In)?;
1213                let iter = self.parse_expr()?;
1214                let body = self.parse_block()?;
1215                Ok(Stmt::For { name, iter, body })
1216            }
1217            Some(Token::Break) => {
1218                self.next();
1219                self.expect(Token::Semicolon)?;
1220                Ok(Stmt::Break)
1221            }
1222            Some(Token::Continue) => {
1223                self.next();
1224                self.expect(Token::Semicolon)?;
1225                Ok(Stmt::Continue)
1226            }
1227            Some(Token::LBrace) => {
1228                let (block, span) = self.parse_block_with_span()?;
1229                Ok(Stmt::Expr(Expr::new(ExprKind::Block(block), span)))
1230            }
1231            Some(Token::If) | Some(Token::Match) => {
1232                let expr = self.parse_expr()?;
1233                if matches!(self.peek(), Some(Token::Semicolon)) {
1234                    self.next();
1235                }
1236                Ok(Stmt::Expr(expr))
1237            }
1238            _ => {
1239                let expr = self.parse_expr()?;
1240                if matches!(self.peek(), Some(Token::Semicolon)) {
1241                    self.next();
1242                    return Ok(Stmt::Expr(expr));
1243                }
1244                if matches!(self.peek(), Some(Token::RBrace)) {
1245                    return Ok(Stmt::Expr(expr));
1246                }
1247                Err(ParserError::MissingSemicolon(expr.span))
1248            }
1249        }
1250    }
1251
1252    fn parse_params(&mut self) -> Result<Vec<Param>, ParserError> {
1253        let mut params = Vec::new();
1254        if matches!(self.peek(), Some(Token::RParen)) {
1255            return Ok(params);
1256        }
1257        loop {
1258            let name = match self.next() {
1259                Some(TokenSpan {
1260                    token: Token::Ident(name),
1261                    ..
1262                }) => name,
1263                Some(TokenSpan { token, span }) => {
1264                    return Err(ParserError::Unexpected(token, span))
1265                }
1266                None => return Err(ParserError::Eof(self.eof_pos)),
1267            };
1268            self.expect(Token::Colon)?;
1269            let ty = self.parse_type()?;
1270            params.push(Param { name, ty });
1271
1272            if matches!(self.peek(), Some(Token::Comma)) {
1273                self.next();
1274                if matches!(self.peek(), Some(Token::RParen)) {
1275                    break;
1276                }
1277            } else {
1278                break;
1279            }
1280        }
1281        Ok(params)
1282    }
1283
1284    fn parse_type(&mut self) -> Result<Type, ParserError> {
1285        match self.next() {
1286            Some(TokenSpan {
1287                token: Token::Ident(name),
1288                ..
1289            }) => {
1290                if name == "Safe" {
1291                    self.expect(Token::Less)?;
1292                    let base = self.parse_type()?;
1293                    self.expect(Token::Comma)?;
1294                    let mut constraints = Vec::new();
1295                    self.expect(Token::Bang)?;
1296                    let first = match self.next() {
1297                        Some(TokenSpan {
1298                            token: Token::Ident(constraint),
1299                            ..
1300                        }) => constraint,
1301                        Some(TokenSpan { token, span }) => {
1302                            return Err(ParserError::Unexpected(token, span))
1303                        }
1304                        None => return Err(ParserError::Eof(self.eof_pos)),
1305                    };
1306                    constraints.push(first);
1307                    while matches!(self.peek(), Some(Token::Comma)) {
1308                        self.next();
1309                        self.expect(Token::Bang)?;
1310                        let constraint = match self.next() {
1311                            Some(TokenSpan {
1312                                token: Token::Ident(constraint),
1313                                ..
1314                            }) => constraint,
1315                            Some(TokenSpan { token, span }) => {
1316                                return Err(ParserError::Unexpected(token, span))
1317                            }
1318                            None => return Err(ParserError::Eof(self.eof_pos)),
1319                        };
1320                        constraints.push(constraint);
1321                    }
1322                    self.expect(Token::Greater)?;
1323                    Ok(Type::Safe {
1324                        base: Box::new(base),
1325                        constraints,
1326                    })
1327                } else if matches!(self.peek(), Some(Token::Less)) {
1328                    self.next();
1329                    let mut args = Vec::new();
1330                    args.push(self.parse_type()?);
1331                    while matches!(self.peek(), Some(Token::Comma)) {
1332                        self.next();
1333                        if matches!(self.peek(), Some(Token::Greater)) {
1334                            break;
1335                        }
1336                        args.push(self.parse_type()?);
1337                    }
1338                    self.expect(Token::Greater)?;
1339                    Ok(Type::Generic { name, args })
1340                } else {
1341                    Ok(Type::Ident(name))
1342                }
1343            }
1344            Some(TokenSpan {
1345                token: Token::LBrace,
1346                ..
1347            }) => {
1348                let mut fields = Vec::new();
1349                while !matches!(self.peek(), Some(Token::RBrace)) {
1350                    let field_name = match self.next() {
1351                        Some(TokenSpan {
1352                            token: Token::Ident(name),
1353                            ..
1354                        }) => name,
1355                        Some(TokenSpan { token, span }) => {
1356                            return Err(ParserError::Unexpected(token, span))
1357                        }
1358                        None => return Err(ParserError::Eof(self.eof_pos)),
1359                    };
1360                    self.expect(Token::Colon)?;
1361                    let field_ty = self.parse_type()?;
1362                    fields.push((field_name, field_ty));
1363
1364                    if matches!(self.peek(), Some(Token::Comma)) {
1365                        self.next();
1366                        if matches!(self.peek(), Some(Token::RBrace)) {
1367                            break;
1368                        }
1369                    } else {
1370                        break;
1371                    }
1372                }
1373                self.expect(Token::RBrace)?;
1374                Ok(Type::Record(fields))
1375            }
1376            Some(TokenSpan {
1377                token: Token::Fn, ..
1378            }) => {
1379                self.expect(Token::LParen)?;
1380                let mut params = Vec::new();
1381                if !matches!(self.peek(), Some(Token::RParen)) {
1382                    params.push(self.parse_type()?);
1383                    while matches!(self.peek(), Some(Token::Comma)) {
1384                        self.next();
1385                        if matches!(self.peek(), Some(Token::RParen)) {
1386                            break;
1387                        }
1388                        params.push(self.parse_type()?);
1389                    }
1390                }
1391                self.expect(Token::RParen)?;
1392                self.expect(Token::ThinArrow)?;
1393                let ret = self.parse_type()?;
1394                Ok(Type::Func {
1395                    params,
1396                    ret: Box::new(ret),
1397                })
1398            }
1399            Some(TokenSpan {
1400                token: Token::LBracket,
1401                ..
1402            }) => {
1403                let elem = self.parse_type()?;
1404                if matches!(self.peek(), Some(Token::Semicolon)) {
1405                    self.next();
1406                    let len = match self.next() {
1407                        Some(TokenSpan {
1408                            token: Token::Int(value),
1409                            span,
1410                        }) => value
1411                            .parse::<i64>()
1412                            .map_err(|_| ParserError::Unexpected(Token::Int(value), span))?,
1413                        Some(TokenSpan { token, span }) => {
1414                            return Err(ParserError::Unexpected(token, span))
1415                        }
1416                        None => return Err(ParserError::Eof(self.eof_pos)),
1417                    };
1418                    self.expect(Token::RBracket)?;
1419                    Ok(Type::Array {
1420                        elem: Box::new(elem),
1421                        len,
1422                    })
1423                } else {
1424                    self.expect(Token::RBracket)?;
1425                    Ok(Type::Slice {
1426                        elem: Box::new(elem),
1427                    })
1428                }
1429            }
1430            Some(TokenSpan {
1431                token: Token::LParen,
1432                ..
1433            }) => {
1434                if matches!(self.peek(), Some(Token::RParen)) {
1435                    let end = self.expect_span(Token::RParen)?;
1436                    return Err(ParserError::Unexpected(Token::RParen, end));
1437                }
1438                let first = self.parse_type()?;
1439                if matches!(self.peek(), Some(Token::Comma)) {
1440                    self.next();
1441                    let mut items = vec![first];
1442                    while !matches!(self.peek(), Some(Token::RParen)) {
1443                        items.push(self.parse_type()?);
1444                        if !matches!(self.peek(), Some(Token::Comma)) {
1445                            break;
1446                        }
1447                        self.next();
1448                        if matches!(self.peek(), Some(Token::RParen)) {
1449                            break;
1450                        }
1451                    }
1452                    self.expect(Token::RParen)?;
1453                    Ok(Type::Tuple(items))
1454                } else {
1455                    self.expect(Token::RParen)?;
1456                    Ok(first)
1457                }
1458            }
1459            Some(TokenSpan { token, span }) => Err(ParserError::Unexpected(token, span)),
1460            None => Err(ParserError::Eof(self.eof_pos)),
1461        }
1462    }
1463
1464    fn parse_expr(&mut self) -> Result<Expr, ParserError> {
1465        if self.is_index_assignment() {
1466            let ident = self.next().ok_or(ParserError::Eof(self.eof_pos))?;
1467            let (name, ident_span) = match ident {
1468                TokenSpan {
1469                    token: Token::Ident(name),
1470                    span,
1471                } => (name, span),
1472                TokenSpan { token, span } => return Err(ParserError::Unexpected(token, span)),
1473            };
1474            self.expect(Token::LBracket)?;
1475            let index = self.parse_expr()?;
1476            self.expect(Token::RBracket)?;
1477            self.expect(Token::Equal)?;
1478            let value = self.parse_expr()?;
1479            let base = Expr::new(ExprKind::Ident(name), ident_span);
1480            let span = merge_span(ident_span, value.span);
1481            return Ok(Expr::new(
1482                ExprKind::AssignIndex {
1483                    expr: Box::new(base),
1484                    index: Box::new(index),
1485                    value: Box::new(value),
1486                },
1487                span,
1488            ));
1489        }
1490        if matches!(self.peek(), Some(Token::Ident(_)))
1491            && matches!(
1492                self.peek_next(),
1493                Some(
1494                    Token::Equal
1495                        | Token::PlusEqual
1496                        | Token::MinusEqual
1497                        | Token::StarEqual
1498                        | Token::SlashEqual
1499                )
1500            )
1501        {
1502            let ident = self.next().ok_or(ParserError::Eof(self.eof_pos))?;
1503            let name = match ident {
1504                TokenSpan {
1505                    token: Token::Ident(name),
1506                    span,
1507                } => (name, span),
1508                TokenSpan { token, span } => return Err(ParserError::Unexpected(token, span)),
1509            };
1510            let op = self.next().ok_or(ParserError::Eof(self.eof_pos))?;
1511            match op {
1512                TokenSpan {
1513                    token: Token::Equal,
1514                    ..
1515                } => {
1516                    let expr = self.parse_expr()?;
1517                    let span = merge_span(name.1, expr.span);
1518                    return Ok(Expr::new(
1519                        ExprKind::Assign {
1520                            name: name.0,
1521                            expr: Box::new(expr),
1522                        },
1523                        span,
1524                    ));
1525                }
1526                TokenSpan {
1527                    token: Token::PlusEqual,
1528                    ..
1529                } => {
1530                    let rhs = self.parse_expr()?;
1531                    let lhs = Expr::new(ExprKind::Ident(name.0.clone()), name.1);
1532                    let bin_span = merge_span(lhs.span, rhs.span);
1533                    let bin = Expr::new(
1534                        ExprKind::Binary {
1535                            op: BinaryOp::Add,
1536                            left: Box::new(lhs),
1537                            right: Box::new(rhs),
1538                        },
1539                        bin_span,
1540                    );
1541                    let span = merge_span(name.1, bin.span);
1542                    return Ok(Expr::new(
1543                        ExprKind::Assign {
1544                            name: name.0,
1545                            expr: Box::new(bin),
1546                        },
1547                        span,
1548                    ));
1549                }
1550                TokenSpan {
1551                    token: Token::MinusEqual,
1552                    ..
1553                } => {
1554                    let rhs = self.parse_expr()?;
1555                    let lhs = Expr::new(ExprKind::Ident(name.0.clone()), name.1);
1556                    let bin_span = merge_span(lhs.span, rhs.span);
1557                    let bin = Expr::new(
1558                        ExprKind::Binary {
1559                            op: BinaryOp::Sub,
1560                            left: Box::new(lhs),
1561                            right: Box::new(rhs),
1562                        },
1563                        bin_span,
1564                    );
1565                    let span = merge_span(name.1, bin.span);
1566                    return Ok(Expr::new(
1567                        ExprKind::Assign {
1568                            name: name.0,
1569                            expr: Box::new(bin),
1570                        },
1571                        span,
1572                    ));
1573                }
1574                TokenSpan {
1575                    token: Token::StarEqual,
1576                    ..
1577                } => {
1578                    let rhs = self.parse_expr()?;
1579                    let lhs = Expr::new(ExprKind::Ident(name.0.clone()), name.1);
1580                    let bin_span = merge_span(lhs.span, rhs.span);
1581                    let bin = Expr::new(
1582                        ExprKind::Binary {
1583                            op: BinaryOp::Mul,
1584                            left: Box::new(lhs),
1585                            right: Box::new(rhs),
1586                        },
1587                        bin_span,
1588                    );
1589                    let span = merge_span(name.1, bin.span);
1590                    return Ok(Expr::new(
1591                        ExprKind::Assign {
1592                            name: name.0,
1593                            expr: Box::new(bin),
1594                        },
1595                        span,
1596                    ));
1597                }
1598                TokenSpan {
1599                    token: Token::SlashEqual,
1600                    ..
1601                } => {
1602                    let rhs = self.parse_expr()?;
1603                    let lhs = Expr::new(ExprKind::Ident(name.0.clone()), name.1);
1604                    let bin_span = merge_span(lhs.span, rhs.span);
1605                    let bin = Expr::new(
1606                        ExprKind::Binary {
1607                            op: BinaryOp::Div,
1608                            left: Box::new(lhs),
1609                            right: Box::new(rhs),
1610                        },
1611                        bin_span,
1612                    );
1613                    let span = merge_span(name.1, bin.span);
1614                    return Ok(Expr::new(
1615                        ExprKind::Assign {
1616                            name: name.0,
1617                            expr: Box::new(bin),
1618                        },
1619                        span,
1620                    ));
1621                }
1622                TokenSpan { token, span } => return Err(ParserError::Unexpected(token, span)),
1623            }
1624        }
1625        self.parse_precedence(0)
1626    }
1627
1628    fn parse_precedence(&mut self, min_prec: u8) -> Result<Expr, ParserError> {
1629        let mut left = self.parse_unary()?;
1630
1631        while let Some((op, prec, right_assoc)) = self.peek().and_then(Self::token_to_binary_op) {
1632            if prec < min_prec {
1633                break;
1634            }
1635
1636            self.next();
1637            let next_min_prec = if right_assoc { prec } else { prec + 1 };
1638            let right = self.parse_precedence(next_min_prec)?;
1639            let span = merge_span(left.span, right.span);
1640            left = Expr::new(
1641                ExprKind::Binary {
1642                    op,
1643                    left: Box::new(left),
1644                    right: Box::new(right),
1645                },
1646                span,
1647            );
1648        }
1649
1650        Ok(left)
1651    }
1652
1653    fn parse_unary(&mut self) -> Result<Expr, ParserError> {
1654        if matches!(self.peek(), Some(Token::Pipe)) {
1655            // Lambda: |x, y| expr
1656            let start = self.next().unwrap().span;
1657            let mut params = Vec::new();
1658            loop {
1659                match self.next() {
1660                    Some(TokenSpan {
1661                        token: Token::Ident(name),
1662                        ..
1663                    }) => params.push(name),
1664                    Some(TokenSpan {
1665                        token: Token::Pipe, ..
1666                    }) => break,
1667                    Some(TokenSpan { token, span }) => {
1668                        return Err(ParserError::Unexpected(token, span))
1669                    }
1670                    None => return Err(ParserError::Eof(self.eof_pos)),
1671                }
1672                if matches!(self.peek(), Some(Token::Comma)) {
1673                    self.next();
1674                }
1675            }
1676            let body = self.parse_expr()?;
1677            let span = merge_span(start, body.span);
1678            Ok(Expr::new(
1679                ExprKind::Lambda {
1680                    params,
1681                    body: Box::new(body),
1682                },
1683                span,
1684            ))
1685        } else {
1686            match self.peek() {
1687                Some(Token::Bang) => {
1688                    let span = self.next().unwrap().span;
1689                    let expr = self.parse_unary()?;
1690                    let full = merge_span(span, expr.span);
1691                    Ok(Expr::new(
1692                        ExprKind::Unary {
1693                            op: UnaryOp::Not,
1694                            expr: Box::new(expr),
1695                        },
1696                        full,
1697                    ))
1698                }
1699                Some(Token::Minus) => {
1700                    let span = self.next().unwrap().span;
1701                    let expr = self.parse_unary()?;
1702                    let full = merge_span(span, expr.span);
1703                    Ok(Expr::new(
1704                        ExprKind::Unary {
1705                            op: UnaryOp::Neg,
1706                            expr: Box::new(expr),
1707                        },
1708                        full,
1709                    ))
1710                }
1711                _ => self.parse_primary(),
1712            }
1713        }
1714    }
1715
1716    fn parse_primary(&mut self) -> Result<Expr, ParserError> {
1717        if self.is_record_literal_start() {
1718            return self.parse_record_literal();
1719        }
1720        if matches!(self.peek(), Some(Token::LBrace)) {
1721            let (block, span) = self.parse_block_with_span()?;
1722            return Ok(Expr::new(ExprKind::Block(block), span));
1723        }
1724
1725        let mut expr = match self.next() {
1726            Some(TokenSpan {
1727                token: Token::Int(value),
1728                span,
1729            }) => value
1730                .parse::<i64>()
1731                .map(|v| Expr::new(ExprKind::Int(v), span))
1732                .map_err(|_| ParserError::Unexpected(Token::Int(value), span)),
1733            Some(TokenSpan {
1734                token: Token::Float(value),
1735                span,
1736            }) => value
1737                .parse::<f64>()
1738                .map(|v| Expr::new(ExprKind::Float(v), span))
1739                .map_err(|_| ParserError::Unexpected(Token::Float(value), span)),
1740            Some(TokenSpan {
1741                token: Token::Str(value),
1742                span,
1743            }) => Ok(Expr::new(ExprKind::Str(value), span)),
1744            Some(TokenSpan {
1745                token: Token::True,
1746                span,
1747            }) => Ok(Expr::new(ExprKind::Bool(true), span)),
1748            Some(TokenSpan {
1749                token: Token::False,
1750                span,
1751            }) => Ok(Expr::new(ExprKind::Bool(false), span)),
1752            Some(TokenSpan {
1753                token: Token::Null,
1754                span,
1755            }) => Ok(Expr::new(ExprKind::Null, span)),
1756            Some(TokenSpan {
1757                token: Token::Ident(name),
1758                span,
1759            }) => Ok(Expr::new(ExprKind::Ident(name), span)),
1760            Some(TokenSpan {
1761                token: Token::LParen,
1762                span,
1763            }) => {
1764                let first = self.parse_expr()?;
1765                if matches!(self.peek(), Some(Token::Comma)) {
1766                    self.next();
1767                    let mut items = vec![first];
1768                    while !matches!(self.peek(), Some(Token::RParen)) {
1769                        items.push(self.parse_expr()?);
1770                        if !matches!(self.peek(), Some(Token::Comma)) {
1771                            break;
1772                        }
1773                        self.next();
1774                        if matches!(self.peek(), Some(Token::RParen)) {
1775                            break;
1776                        }
1777                    }
1778                    let end = self.expect_span(Token::RParen)?;
1779                    Ok(Expr::new(ExprKind::Tuple(items), merge_span(span, end)))
1780                } else {
1781                    let end = self.expect_span(Token::RParen)?;
1782                    Ok(Expr::new(first.kind, merge_span(span, end)))
1783                }
1784            }
1785            Some(TokenSpan {
1786                token: Token::LBracket,
1787                span,
1788            }) => {
1789                let mut items = Vec::new();
1790                if !matches!(self.peek(), Some(Token::RBracket)) {
1791                    items.push(self.parse_expr()?);
1792                    while matches!(self.peek(), Some(Token::Comma)) {
1793                        self.next();
1794                        if matches!(self.peek(), Some(Token::RBracket)) {
1795                            break;
1796                        }
1797                        items.push(self.parse_expr()?);
1798                    }
1799                }
1800                let end = self.expect_span(Token::RBracket)?;
1801                Ok(Expr::new(
1802                    ExprKind::ArrayLiteral(items),
1803                    merge_span(span, end),
1804                ))
1805            }
1806            Some(TokenSpan {
1807                token: Token::Await,
1808                span,
1809            }) => {
1810                let expr = self.parse_expr()?;
1811                Ok(Expr::new(
1812                    ExprKind::Await(Box::new(expr.clone())),
1813                    merge_span(span, expr.span),
1814                ))
1815            }
1816            Some(TokenSpan {
1817                token: Token::If,
1818                span,
1819            }) => {
1820                let condition = self.parse_expr()?;
1821                let (then_branch, then_span) = self.parse_block_with_span()?;
1822                let (else_branch, end_span) = if matches!(self.peek(), Some(Token::Else)) {
1823                    self.next();
1824                    if matches!(self.peek(), Some(Token::If)) {
1825                        let else_if = self.parse_expr()?;
1826                        let end = else_if.span;
1827                        (Some(ElseBranch::If(Box::new(else_if))), end)
1828                    } else {
1829                        let (block, block_span) = self.parse_block_with_span()?;
1830                        (Some(ElseBranch::Block(block)), block_span)
1831                    }
1832                } else {
1833                    (None, then_span)
1834                };
1835                Ok(Expr::new(
1836                    ExprKind::If {
1837                        condition: Box::new(condition),
1838                        then_branch,
1839                        else_branch,
1840                    },
1841                    merge_span(span, end_span),
1842                ))
1843            }
1844            Some(TokenSpan {
1845                token: Token::Match,
1846                span,
1847            }) => {
1848                let expr = self.parse_expr()?;
1849                self.expect(Token::LBrace)?;
1850                let mut arms = Vec::new();
1851                while let Some(tok) = self.peek() {
1852                    if *tok == Token::RBrace {
1853                        break;
1854                    }
1855                    let (pattern, pattern_span) = self.parse_pattern()?;
1856                    let guard = if matches!(self.peek(), Some(Token::If)) {
1857                        self.next();
1858                        Some(self.parse_expr()?)
1859                    } else {
1860                        None
1861                    };
1862                    self.expect(Token::Arrow)?;
1863                    let arm_expr = self.parse_expr()?;
1864                    if matches!(self.peek(), Some(Token::Comma)) {
1865                        self.next();
1866                    }
1867                    arms.push(MatchArm {
1868                        pattern,
1869                        pattern_span,
1870                        guard,
1871                        expr: arm_expr,
1872                    });
1873                }
1874                let end = self.expect_span(Token::RBrace)?;
1875                Ok(Expr::new(
1876                    ExprKind::Match {
1877                        expr: Box::new(expr),
1878                        arms,
1879                    },
1880                    merge_span(span, end),
1881                ))
1882            }
1883            Some(TokenSpan { token, span }) => Err(ParserError::Unexpected(token, span)),
1884            None => Err(ParserError::Eof(self.eof_pos)),
1885        }?;
1886
1887        loop {
1888            match self.peek() {
1889                Some(Token::LParen) => {
1890                    self.next();
1891                    let mut args = Vec::new();
1892                    if !matches!(self.peek(), Some(Token::RParen)) {
1893                        args.push(self.parse_expr()?);
1894                        while matches!(self.peek(), Some(Token::Comma)) {
1895                            self.next();
1896                            args.push(self.parse_expr()?);
1897                        }
1898                    }
1899                    let end = self.expect_span(Token::RParen)?;
1900                    let span = merge_span(expr.span, end);
1901                    expr = Expr::new(
1902                        ExprKind::Call {
1903                            callee: Box::new(expr),
1904                            args,
1905                        },
1906                        span,
1907                    );
1908                }
1909                Some(Token::Dot) => {
1910                    self.next();
1911                    let field = match self.next() {
1912                        Some(TokenSpan {
1913                            token: Token::Ident(name),
1914                            span,
1915                        }) => (FieldAccess::Ident(name), span),
1916                        Some(TokenSpan {
1917                            token: Token::Int(value),
1918                            span,
1919                        }) => value
1920                            .parse::<i64>()
1921                            .map(FieldAccess::Index)
1922                            .map_err(|_| ParserError::Unexpected(Token::Int(value), span))
1923                            .map(|f| (f, span))?,
1924                        Some(TokenSpan { token, span }) => {
1925                            return Err(ParserError::Unexpected(token, span))
1926                        }
1927                        None => return Err(ParserError::Eof(self.eof_pos)),
1928                    };
1929                    let span = merge_span(expr.span, field.1);
1930                    expr = Expr::new(
1931                        ExprKind::Field {
1932                            expr: Box::new(expr),
1933                            field: field.0,
1934                        },
1935                        span,
1936                    );
1937                }
1938                Some(Token::LBracket) => {
1939                    self.next();
1940                    let index = self.parse_expr()?;
1941                    let end = self.expect_span(Token::RBracket)?;
1942                    let span = merge_span(expr.span, end);
1943                    expr = Expr::new(
1944                        ExprKind::Index {
1945                            expr: Box::new(expr),
1946                            index: Box::new(index),
1947                        },
1948                        span,
1949                    );
1950                }
1951                _ => break,
1952            }
1953        }
1954
1955        Ok(expr)
1956    }
1957
1958    fn is_record_literal_start(&self) -> bool {
1959        matches!(
1960            (
1961                self.peek(),
1962                self.tokens.get(self.pos + 1).map(|t| &t.token),
1963                self.tokens.get(self.pos + 2).map(|t| &t.token),
1964            ),
1965            (
1966                Some(Token::LBrace),
1967                Some(Token::Ident(_)),
1968                Some(Token::Colon)
1969            )
1970        )
1971    }
1972
1973    fn parse_record_literal(&mut self) -> Result<Expr, ParserError> {
1974        let start = self.expect_span(Token::LBrace)?;
1975        let mut fields = Vec::new();
1976
1977        loop {
1978            let field_name = match self.next() {
1979                Some(TokenSpan {
1980                    token: Token::Ident(name),
1981                    ..
1982                }) => name,
1983                Some(TokenSpan { token, span }) => {
1984                    return Err(ParserError::Unexpected(token, span))
1985                }
1986                None => return Err(ParserError::Eof(self.eof_pos)),
1987            };
1988            self.expect(Token::Colon)?;
1989            let field_expr = self.parse_expr()?;
1990            fields.push((field_name, field_expr));
1991
1992            if matches!(self.peek(), Some(Token::Comma)) {
1993                self.next();
1994                if matches!(self.peek(), Some(Token::RBrace)) {
1995                    break;
1996                }
1997            } else {
1998                break;
1999            }
2000        }
2001
2002        let end = self.expect_span(Token::RBrace)?;
2003        Ok(Expr::new(
2004            ExprKind::RecordLiteral(fields),
2005            merge_span(start, end),
2006        ))
2007    }
2008
2009    fn token_to_binary_op(token: &Token) -> Option<(BinaryOp, u8, bool)> {
2010        match token {
2011            Token::DotDot => Some((BinaryOp::Range, 0, false)),
2012            Token::OrOr => Some((BinaryOp::Or, 1, false)),
2013            Token::AndAnd => Some((BinaryOp::And, 2, false)),
2014            Token::EqualEqual => Some((BinaryOp::Equal, 3, false)),
2015            Token::BangEqual => Some((BinaryOp::NotEqual, 3, false)),
2016            Token::Less => Some((BinaryOp::Less, 4, false)),
2017            Token::LessEqual => Some((BinaryOp::LessEqual, 4, false)),
2018            Token::Greater => Some((BinaryOp::Greater, 4, false)),
2019            Token::GreaterEqual => Some((BinaryOp::GreaterEqual, 4, false)),
2020            Token::Plus => Some((BinaryOp::Add, 5, false)),
2021            Token::Minus => Some((BinaryOp::Sub, 5, false)),
2022            Token::Star => Some((BinaryOp::Mul, 6, false)),
2023            Token::Slash => Some((BinaryOp::Div, 6, false)),
2024            Token::DoubleStar => Some((BinaryOp::Pow, 7, true)),
2025            _ => None,
2026        }
2027    }
2028
2029    fn parse_pattern(&mut self) -> Result<(Pattern, Span), ParserError> {
2030        match self.next() {
2031            Some(TokenSpan {
2032                token: Token::LParen,
2033                span,
2034            }) => {
2035                if matches!(self.peek(), Some(Token::RParen)) {
2036                    let end = self.expect_span(Token::RParen)?;
2037                    return Err(ParserError::Unexpected(Token::RParen, end));
2038                }
2039                let (first, _) = self.parse_pattern()?;
2040                if matches!(self.peek(), Some(Token::Comma)) {
2041                    self.next();
2042                    let mut items = vec![first];
2043                    while !matches!(self.peek(), Some(Token::RParen)) {
2044                        let (item, _) = self.parse_pattern()?;
2045                        items.push(item);
2046                        if !matches!(self.peek(), Some(Token::Comma)) {
2047                            break;
2048                        }
2049                        self.next();
2050                        if matches!(self.peek(), Some(Token::RParen)) {
2051                            break;
2052                        }
2053                    }
2054                    let end = self.expect_span(Token::RParen)?;
2055                    Ok((Pattern::Tuple(items), merge_span(span, end)))
2056                } else {
2057                    let end = self.expect_span(Token::RParen)?;
2058                    Ok((first, merge_span(span, end)))
2059                }
2060            }
2061            Some(TokenSpan {
2062                token: Token::Ident(name),
2063                span,
2064            }) => {
2065                if name == "_" {
2066                    Ok((Pattern::Wildcard, span))
2067                } else if matches!(self.peek(), Some(Token::LParen)) {
2068                    self.next();
2069                    let mut args = Vec::new();
2070                    if !matches!(self.peek(), Some(Token::RParen)) {
2071                        loop {
2072                            let (arg, _) = self.parse_pattern()?;
2073                            args.push(arg);
2074                            if !matches!(self.peek(), Some(Token::Comma)) {
2075                                break;
2076                            }
2077                            self.next();
2078                            if matches!(self.peek(), Some(Token::RParen)) {
2079                                break;
2080                            }
2081                        }
2082                    }
2083                    let end = self.expect_span(Token::RParen)?;
2084                    Ok((Pattern::Constructor { name, args }, merge_span(span, end)))
2085                } else {
2086                    Ok((Pattern::Ident(name), span))
2087                }
2088            }
2089            Some(TokenSpan {
2090                token: Token::Int(value),
2091                span,
2092            }) => value
2093                .parse::<i64>()
2094                .map(|value| (Pattern::Int(value), span))
2095                .map_err(|_| ParserError::Unexpected(Token::Int(value), span)),
2096            Some(TokenSpan {
2097                token: Token::Str(value),
2098                span,
2099            }) => Ok((Pattern::Str(value), span)),
2100            Some(TokenSpan {
2101                token: Token::True,
2102                span,
2103            }) => Ok((Pattern::Bool(true), span)),
2104            Some(TokenSpan {
2105                token: Token::False,
2106                span,
2107            }) => Ok((Pattern::Bool(false), span)),
2108            Some(TokenSpan { token, span }) => Err(ParserError::Unexpected(token, span)),
2109            None => Err(ParserError::Eof(self.eof_pos)),
2110        }
2111    }
2112}
2113
2114#[cfg(test)]
2115mod tests {
2116    use super::*;
2117
2118    #[test]
2119    fn parse_empty_function() {
2120        let program = parse_program("fn main() {}\n").unwrap();
2121        assert_eq!(program.items.len(), 1);
2122    }
2123
2124    #[test]
2125    fn parse_let_and_return() {
2126        let src = "fn main() { let x = 1; return x; }";
2127        let program = parse_program(src).unwrap();
2128        let func = match &program.items[0] {
2129            Item::Trait(_) => panic!("expected function"),
2130            Item::Enum(_) => panic!("expected function"),
2131            Item::Pipeline(_) => panic!("expected function"),
2132            Item::Function(func) => func,
2133        };
2134        assert_eq!(func.body.len(), 2);
2135    }
2136
2137    #[test]
2138    fn parse_if_expression() {
2139        let src = "fn main() { if 1 { return 2; } else { return 3; } }";
2140        let program = parse_program(src).unwrap();
2141        let func = match &program.items[0] {
2142            Item::Trait(_) => panic!("expected function"),
2143            Item::Enum(_) => panic!("expected function"),
2144            Item::Pipeline(_) => panic!("expected function"),
2145            Item::Function(func) => func,
2146        };
2147        assert_eq!(func.body.len(), 1);
2148    }
2149
2150    #[test]
2151    fn parse_pipeline_suffix_attributes() {
2152        let src = r#"
2153        pipeline MyPipeline @suffix(val=1) {
2154            input: i64,
2155            steps: [],
2156        }
2157        "#;
2158        let program = parse_program(src).unwrap();
2159        if let Item::Pipeline(pipe) = &program.items[0] {
2160            assert_eq!(pipe.attrs.len(), 1);
2161            assert_eq!(pipe.attrs[0].name, "suffix");
2162        } else {
2163            panic!("expected pipeline");
2164        }
2165    }
2166
2167    #[test]
2168    fn parse_complex_attributes() {
2169        let src = r#"
2170        @complex(nested=[1, 2], call=ExternalCall("DB"))
2171        fn main() {}
2172        "#;
2173        let program = parse_program(src).unwrap();
2174        if let Item::Function(func) = &program.items[0] {
2175            assert_eq!(func.attrs.len(), 1);
2176            let attr = &func.attrs[0];
2177            assert_eq!(attr.name, "complex");
2178            // args: nested, =, [, 1, ,, 2, ], ,, call, =, ExternalCall, (, DB, )
2179            // My implementation preserves structure by tokens.
2180            // nested -> Ident
2181            // = -> Equal
2182            // [ -> LBracket
2183            // 1 -> Int
2184            // , -> Comma (inside brackets)
2185            // 2 -> Int
2186            // ] -> RBracket
2187            // , -> Comma (top level) -> SKIPPED
2188            // call -> Ident
2189            // = -> Equal
2190            // ExternalCall -> Ident
2191            // ( -> LParen
2192            // DB -> Str
2193            // ) -> RParen
2194
2195            let expected = vec![
2196                "nested",
2197                "=",
2198                "[",
2199                "1",
2200                ",",
2201                "2",
2202                "]",
2203                "call",
2204                "=",
2205                "ExternalCall",
2206                "(",
2207                "DB",
2208                ")",
2209            ];
2210            assert_eq!(attr.args, expected);
2211        } else {
2212            panic!("expected function");
2213        }
2214    }
2215
2216    #[test]
2217    fn parse_external_attribute_spec() {
2218        let src = r#"
2219        @external(python="torch.nn.Linear", effects=[IO, Time, ExternalCall("Bybit")])
2220        fn predict(input: i64): i64 { return input; }
2221        "#;
2222        let program = parse_program(src).unwrap();
2223        let func = match &program.items[0] {
2224            Item::Function(func) => func,
2225            _ => panic!("expected function"),
2226        };
2227
2228        let spec = func.external_spec.as_ref().expect("expected external spec");
2229        assert_eq!(spec.python.as_deref(), Some("torch.nn.Linear"));
2230        assert_eq!(
2231            spec.effects,
2232            vec![
2233                "IO".to_string(),
2234                "Time".to_string(),
2235                "ExternalCall(Bybit)".to_string(),
2236            ]
2237        );
2238    }
2239
2240    #[test]
2241    fn parse_external_attribute_legacy_python_only() {
2242        let src = r#"
2243        @external("math.sqrt")
2244        fn predict(input: i64): i64 { return input; }
2245        "#;
2246        let program = parse_program(src).unwrap();
2247        let func = match &program.items[0] {
2248            Item::Function(func) => func,
2249            _ => panic!("expected function"),
2250        };
2251
2252        let spec = func.external_spec.as_ref().expect("expected external spec");
2253        assert_eq!(spec.python.as_deref(), Some("math.sqrt"));
2254        assert!(spec.effects.is_empty());
2255    }
2256    #[test]
2257    fn parse_match_expression() {
2258        let src = "fn main() { match x { 1 => foo(), _ => bar(), }; }";
2259        let program = parse_program(src).unwrap();
2260        let func = match &program.items[0] {
2261            Item::Trait(_) => panic!("expected function"),
2262            Item::Enum(_) => panic!("expected function"),
2263            Item::Pipeline(_) => panic!("expected function"),
2264            Item::Function(func) => func,
2265        };
2266        assert_eq!(func.body.len(), 1);
2267    }
2268
2269    #[test]
2270    fn parse_tuple_expr_and_pattern() {
2271        let src = "fn main() { let pair = (1, 2); match pair { (1, x) => x, _ => 0 }; }";
2272        let program = parse_program(src).unwrap();
2273        let func = match &program.items[0] {
2274            Item::Trait(_) => panic!("expected function"),
2275            Item::Enum(_) => panic!("expected function"),
2276            Item::Pipeline(_) => panic!("expected function"),
2277            Item::Function(func) => func,
2278        };
2279        assert_eq!(func.body.len(), 2);
2280    }
2281
2282    #[test]
2283    fn parse_binary_precedence() {
2284        let src = "fn main() { let x = 1 + 2 * 3; }";
2285        let program = parse_program(src).unwrap();
2286        let func = match &program.items[0] {
2287            Item::Trait(_) => panic!("expected function"),
2288            Item::Enum(_) => panic!("expected function"),
2289            Item::Pipeline(_) => panic!("expected function"),
2290            Item::Function(func) => func,
2291        };
2292        let Stmt::Let { expr, .. } = &func.body[0] else {
2293            panic!("expected let");
2294        };
2295        match &expr.kind {
2296            ExprKind::Binary { op, left, right } => {
2297                assert_eq!(*op, BinaryOp::Add);
2298                assert!(matches!(left.kind, ExprKind::Int(1)));
2299                assert!(matches!(
2300                    right.kind,
2301                    ExprKind::Binary {
2302                        op: BinaryOp::Mul,
2303                        ..
2304                    }
2305                ));
2306            }
2307            _ => panic!("expected binary expression"),
2308        }
2309    }
2310
2311    #[test]
2312    fn parse_unary_expression() {
2313        let src = "fn main() { let x = -1; let y = !false; }";
2314        let program = parse_program(src).unwrap();
2315        let func = match &program.items[0] {
2316            Item::Trait(_) => panic!("expected function"),
2317            Item::Enum(_) => panic!("expected function"),
2318            Item::Pipeline(_) => panic!("expected function"),
2319            Item::Function(func) => func,
2320        };
2321        let Stmt::Let { expr, .. } = &func.body[0] else {
2322            panic!("expected let");
2323        };
2324        assert!(matches!(
2325            expr.kind,
2326            ExprKind::Unary {
2327                op: UnaryOp::Neg,
2328                ..
2329            }
2330        ));
2331        let Stmt::Let { expr, .. } = &func.body[1] else {
2332            panic!("expected let");
2333        };
2334        assert!(matches!(
2335            expr.kind,
2336            ExprKind::Unary {
2337                op: UnaryOp::Not,
2338                ..
2339            }
2340        ));
2341    }
2342
2343    #[test]
2344    fn parse_enum_generics() {
2345        let src = "enum Result<T, E> { Ok, Err }";
2346        let program = parse_program(src).unwrap();
2347        let Item::Enum(def) = &program.items[0] else {
2348            panic!("expected enum");
2349        };
2350        assert_eq!(def.name, "Result");
2351        assert_eq!(def.generics, vec!["T", "E"]);
2352        assert_eq!(def.variants.len(), 2);
2353        assert_eq!(def.variants[0].name, "Ok");
2354        assert!(def.variants[0].args.is_empty());
2355        assert_eq!(def.variants[1].name, "Err");
2356        assert!(def.variants[1].args.is_empty());
2357    }
2358
2359    #[test]
2360    fn parse_enum_variant_types() {
2361        let src = "enum Result<T> { Ok(Safe<T, !nan>), Err(string) }";
2362        let program = parse_program(src).unwrap();
2363        let Item::Enum(def) = &program.items[0] else {
2364            panic!("expected enum");
2365        };
2366        assert_eq!(def.variants.len(), 2);
2367        assert_eq!(def.variants[0].name, "Ok");
2368        assert_eq!(
2369            def.variants[0].args,
2370            vec![Type::Safe {
2371                base: Box::new(Type::Ident("T".into())),
2372                constraints: vec!["nan".into()]
2373            }]
2374        );
2375        assert_eq!(def.variants[1].name, "Err");
2376        assert_eq!(def.variants[1].args, vec![Type::Ident("string".into())]);
2377    }
2378
2379    #[test]
2380    fn parse_float_literal() {
2381        let src = "fn main() { let x = 3.14; }";
2382        let program = parse_program(src).unwrap();
2383        let func = match &program.items[0] {
2384            Item::Trait(_) => panic!("expected function"),
2385            Item::Enum(_) => panic!("expected function"),
2386            Item::Pipeline(_) => panic!("expected function"),
2387            Item::Function(func) => func,
2388        };
2389        let Stmt::Let { expr, .. } = &func.body[0] else {
2390            panic!("expected let");
2391        };
2392        assert!(
2393            matches!(expr.kind, ExprKind::Float(f) if (f - 314.0_f64 / 100.0_f64).abs() < 1e-9)
2394        );
2395    }
2396
2397    #[test]
2398    fn parse_typed_let_and_params() {
2399        let src = "fn add(x: i64, y: i64): i64 { let z: i64 = x + y; return z; }";
2400        let program = parse_program(src).unwrap();
2401        let func = match &program.items[0] {
2402            Item::Trait(_) => panic!("expected function"),
2403            Item::Enum(_) => panic!("expected function"),
2404            Item::Pipeline(_) => panic!("expected function"),
2405            Item::Function(func) => func,
2406        };
2407        assert_eq!(func.params.len(), 2);
2408        assert_eq!(func.return_type, Some(Type::Ident("i64".into())));
2409        let Stmt::Let { ty, .. } = &func.body[0] else {
2410            panic!("expected let");
2411        };
2412        assert_eq!(ty, &Some(Type::Ident("i64".into())));
2413    }
2414
2415    #[test]
2416    fn parse_array_and_slice_types() {
2417        let src = "fn main() { let a: [i64; 3] = 0; let b: [i64] = 0; }";
2418        let program = parse_program(src).unwrap();
2419        let func = match &program.items[0] {
2420            Item::Trait(_) => panic!("expected function"),
2421            Item::Enum(_) => panic!("expected function"),
2422            Item::Pipeline(_) => panic!("expected function"),
2423            Item::Function(func) => func,
2424        };
2425        let Stmt::Let { ty, .. } = &func.body[0] else {
2426            panic!("expected let");
2427        };
2428        assert!(matches!(ty, Some(Type::Array { len: 3, .. })));
2429        let Stmt::Let { ty, .. } = &func.body[1] else {
2430            panic!("expected let");
2431        };
2432        assert!(matches!(ty, Some(Type::Slice { .. })));
2433    }
2434
2435    #[test]
2436    fn parse_record_type() {
2437        let src = "fn main() { let entry: { reason: string, score: f64, eligible: bool } = 0; }";
2438        let program = parse_program(src).unwrap();
2439        let func = match &program.items[0] {
2440            Item::Trait(_) => panic!("expected function"),
2441            Item::Enum(_) => panic!("expected function"),
2442            Item::Pipeline(_) => panic!("expected function"),
2443            Item::Function(func) => func,
2444        };
2445        let Stmt::Let { ty, .. } = &func.body[0] else {
2446            panic!("expected let");
2447        };
2448        assert_eq!(
2449            ty,
2450            &Some(Type::Record(vec![
2451                ("reason".into(), Type::Ident("string".into())),
2452                ("score".into(), Type::Ident("f64".into())),
2453                ("eligible".into(), Type::Ident("bool".into())),
2454            ]))
2455        );
2456    }
2457
2458    #[test]
2459    fn parse_record_literal() {
2460        let src = "fn main() { let entry = { reason: \"ok\", score: 1.0, eligible: true }; }";
2461        let program = parse_program(src).unwrap();
2462        let func = match &program.items[0] {
2463            Item::Trait(_) => panic!("expected function"),
2464            Item::Enum(_) => panic!("expected function"),
2465            Item::Pipeline(_) => panic!("expected function"),
2466            Item::Function(func) => func,
2467        };
2468        let Stmt::Let { expr, .. } = &func.body[0] else {
2469            panic!("expected let");
2470        };
2471        let ExprKind::RecordLiteral(fields) = &expr.kind else {
2472            panic!("expected record literal");
2473        };
2474        assert_eq!(fields.len(), 3);
2475        assert_eq!(fields[0].0, "reason");
2476        assert!(matches!(fields[0].1.kind, ExprKind::Str(_)));
2477        assert_eq!(fields[1].0, "score");
2478        assert!(matches!(fields[1].1.kind, ExprKind::Float(_)));
2479        assert_eq!(fields[2].0, "eligible");
2480        assert!(matches!(fields[2].1.kind, ExprKind::Bool(true)));
2481    }
2482
2483    #[test]
2484    fn parse_safe_type() {
2485        let src = "fn main() { let x: Safe<i64, !nan, !inf> = 1; }";
2486        let program = parse_program(src).unwrap();
2487        let func = match &program.items[0] {
2488            Item::Trait(_) => panic!("expected function"),
2489            Item::Enum(_) => panic!("expected function"),
2490            Item::Pipeline(_) => panic!("expected function"),
2491            Item::Function(func) => func,
2492        };
2493        let Stmt::Let { ty, .. } = &func.body[0] else {
2494            panic!("expected let");
2495        };
2496        assert_eq!(
2497            ty,
2498            &Some(Type::Safe {
2499                base: Box::new(Type::Ident("i64".into())),
2500                constraints: vec!["nan".into(), "inf".into()],
2501            })
2502        );
2503    }
2504
2505    #[test]
2506    fn parse_function_type() {
2507        let src = "fn main() { let f: fn(i64, i64) -> i64 = add; }";
2508        let program = parse_program(src).unwrap();
2509        let func = match &program.items[0] {
2510            Item::Trait(_) => panic!("expected function"),
2511            Item::Enum(_) => panic!("expected function"),
2512            Item::Pipeline(_) => panic!("expected function"),
2513            Item::Function(func) => func,
2514        };
2515        let Stmt::Let { ty, .. } = &func.body[0] else {
2516            panic!("expected let");
2517        };
2518        assert_eq!(
2519            ty,
2520            &Some(Type::Func {
2521                params: vec![Type::Ident("i64".into()), Type::Ident("i64".into())],
2522                ret: Box::new(Type::Ident("i64".into())),
2523            })
2524        );
2525    }
2526
2527    #[test]
2528    fn parse_while_and_for() {
2529        let src = "fn main() { while x < 10 { x = x + 1; } for i in xs { return i; } }";
2530        let program = parse_program(src).unwrap();
2531        let func = match &program.items[0] {
2532            Item::Trait(_) => panic!("expected function"),
2533            Item::Enum(_) => panic!("expected function"),
2534            Item::Pipeline(_) => panic!("expected function"),
2535            Item::Function(func) => func,
2536        };
2537        assert!(matches!(func.body[0], Stmt::While { .. }));
2538        assert!(matches!(func.body[1], Stmt::For { .. }));
2539    }
2540
2541    #[test]
2542    fn parse_array_literal() {
2543        let src = "fn main() { let xs = [1, 2, 3]; }";
2544        let program = parse_program(src).unwrap();
2545        let func = match &program.items[0] {
2546            Item::Trait(_) => panic!("expected function"),
2547            Item::Enum(_) => panic!("expected function"),
2548            Item::Pipeline(_) => panic!("expected function"),
2549            Item::Function(func) => func,
2550        };
2551        let Stmt::Let { expr, .. } = &func.body[0] else {
2552            panic!("expected let");
2553        };
2554        assert!(matches!(expr.kind, ExprKind::ArrayLiteral(ref items) if items.len() == 3));
2555    }
2556
2557    #[test]
2558    fn parse_range_expression() {
2559        let src = "fn main() { let xs = 1..10; }";
2560        let program = parse_program(src).unwrap();
2561        let func = match &program.items[0] {
2562            Item::Trait(_) => panic!("expected function"),
2563            Item::Enum(_) => panic!("expected function"),
2564            Item::Pipeline(_) => panic!("expected function"),
2565            Item::Function(func) => func,
2566        };
2567        let Stmt::Let { expr, .. } = &func.body[0] else {
2568            panic!("expected let");
2569        };
2570        assert!(matches!(
2571            expr.kind,
2572            ExprKind::Binary {
2573                op: BinaryOp::Range,
2574                ..
2575            }
2576        ));
2577    }
2578
2579    #[test]
2580    fn parse_match_with_guard() {
2581        let src = "fn main() { match x { y if y > 0 => y, _ => 0, } }";
2582        let program = parse_program(src).unwrap();
2583        let func = match &program.items[0] {
2584            Item::Trait(_) => panic!("expected function"),
2585            Item::Enum(_) => panic!("expected function"),
2586            Item::Pipeline(_) => panic!("expected function"),
2587            Item::Function(func) => func,
2588        };
2589        assert_eq!(func.body.len(), 1);
2590    }
2591
2592    #[test]
2593    fn parse_await_and_block_expr() {
2594        let src = "fn main() { let x = await foo(); { return x; } }";
2595        let program = parse_program(src).unwrap();
2596        let func = match &program.items[0] {
2597            Item::Trait(_) => panic!("expected function"),
2598            Item::Enum(_) => panic!("expected function"),
2599            Item::Pipeline(_) => panic!("expected function"),
2600            Item::Function(func) => func,
2601        };
2602        assert_eq!(func.body.len(), 2);
2603        let Stmt::Let { expr, .. } = &func.body[0] else {
2604            panic!("expected let");
2605        };
2606        assert!(matches!(expr.kind, ExprKind::Await(_)));
2607        assert!(
2608            matches!(func.body[1], Stmt::Expr(ref expr) if matches!(expr.kind, ExprKind::Block(_)))
2609        );
2610    }
2611
2612    #[test]
2613    fn parse_postfix_chain() {
2614        let src = "fn main() { let x = foo(1).bar[0]; }";
2615        let program = parse_program(src).unwrap();
2616        let func = match &program.items[0] {
2617            Item::Trait(_) => panic!("expected function"),
2618            Item::Enum(_) => panic!("expected function"),
2619            Item::Pipeline(_) => panic!("expected function"),
2620            Item::Function(func) => func,
2621        };
2622        let Stmt::Let { expr, .. } = &func.body[0] else {
2623            panic!("expected let");
2624        };
2625        assert!(matches!(expr.kind, ExprKind::Index { .. }));
2626    }
2627    #[test]
2628    fn parse_program_with_type_declarations() {
2629        let src = r#"
2630            type MarketSignal {
2631                symbol: string,
2632                current_price: f64,
2633            }
2634
2635            pipeline ViperSmartCopy {
2636                input: i64,
2637                steps: [],
2638                output: i64
2639            }
2640        "#;
2641
2642        let program = parse_program(src).unwrap();
2643        assert_eq!(program.items.len(), 1);
2644        assert!(matches!(program.items[0], Item::Pipeline(_)));
2645    }
2646}