Skip to main content

pine_parser/
lib.rs

1pub use pine_ast::{Argument, BinOp, Comment, Expr, Literal, Loc, Program, Stmt, UnOp, VarKind};
2use pine_lexer::{Token, TokenType};
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum ParserErrorKind {
7    #[error("Unexpected token: {0:?}")]
8    UnexpectedToken(TokenType),
9
10    #[error("{expected} but found {found:?}")]
11    ExpectedToken { expected: String, found: TokenType },
12
13    #[error("Expected variable name")]
14    ExpectedVariableName,
15
16    #[error("Expected parameter name")]
17    ExpectedParameterName,
18
19    #[error("Can only call identifiers or member access")]
20    InvalidCallTarget,
21
22    #[error("Expected identifier after '.'")]
23    ExpectedIdentifierAfterDot,
24
25    #[error(transparent)]
26    Lexer(pine_lexer::LexerError),
27}
28
29/// A parse error and the 1-based source position it points at.
30#[derive(Debug)]
31pub struct ParserError {
32    pub loc: Loc,
33    pub kind: ParserErrorKind,
34}
35
36impl ParserError {
37    /// The 1-based `(line, column)` the error points at.
38    pub fn location(&self) -> (u32, u32) {
39        (self.loc.line, self.loc.column)
40    }
41}
42
43impl std::fmt::Display for ParserError {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match &self.kind {
46            // Lexer errors already spell out their own line and column.
47            ParserErrorKind::Lexer(e) => write!(f, "{e}"),
48            kind => write!(f, "{kind} at line {}", self.loc.line),
49        }
50    }
51}
52
53impl std::error::Error for ParserError {
54    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
55        Some(&self.kind)
56    }
57}
58
59impl From<pine_lexer::LexerError> for ParserError {
60    fn from(e: pine_lexer::LexerError) -> Self {
61        let (line, column) = e.location();
62        ParserError {
63            loc: Loc::new(line, column),
64            kind: ParserErrorKind::Lexer(e),
65        }
66    }
67}
68
69impl From<ParserError> for String {
70    fn from(err: ParserError) -> String {
71        err.to_string()
72    }
73}
74
75/// Helper trait to convert TokenType to operators
76trait TokenTypeExt {
77    fn to_binop(&self) -> Option<BinOp>;
78}
79
80impl TokenTypeExt for TokenType {
81    /// Convert token type to binary operator, if applicable
82    fn to_binop(&self) -> Option<BinOp> {
83        match self {
84            TokenType::Plus => Some(BinOp::Add),
85            TokenType::Minus => Some(BinOp::Sub),
86            TokenType::Star => Some(BinOp::Mul),
87            TokenType::Slash => Some(BinOp::Div),
88            TokenType::Percent => Some(BinOp::Mod),
89            TokenType::Equal => Some(BinOp::Eq),
90            TokenType::NotEqual => Some(BinOp::NotEq),
91            TokenType::Less => Some(BinOp::Less),
92            TokenType::Greater => Some(BinOp::Greater),
93            TokenType::LessEqual => Some(BinOp::LessEq),
94            TokenType::GreaterEqual => Some(BinOp::GreaterEq),
95            TokenType::And => Some(BinOp::And),
96            TokenType::Or => Some(BinOp::Or),
97            TokenType::PlusAssign => Some(BinOp::Add),
98            TokenType::MinusAssign => Some(BinOp::Sub),
99            TokenType::StarAssign => Some(BinOp::Mul),
100            TokenType::SlashAssign => Some(BinOp::Div),
101            _ => None,
102        }
103    }
104}
105
106pub struct Parser {
107    tokens: Vec<Token>,
108    comments: Vec<Comment>,
109    current: usize,
110    next_call_id: u32,
111}
112
113impl Parser {
114    pub fn new(tokens: Vec<Token>) -> Self {
115        let mut comments = Vec::new();
116        let tokens = tokens
117            .into_iter()
118            .filter(|t| {
119                if let TokenType::Comment(text) = &t.typ {
120                    comments.push(Comment {
121                        line: t.line as u32,
122                        text: text.clone(),
123                    });
124                }
125                !matches!(t.typ, TokenType::Comment(_) | TokenType::BlankLine)
126            })
127            .collect();
128        Self {
129            tokens,
130            comments,
131            current: 0,
132            next_call_id: 1,
133        }
134    }
135
136    pub fn parse_program(mut self) -> Result<Program, ParserError> {
137        let statements = self.parse()?;
138        Ok(Program::new(statements).with_comments(self.comments))
139    }
140
141    /// Lex and parse `source` into a program in one step.
142    pub fn parse_source(source: &str) -> Result<Program, ParserError> {
143        let tokens = pine_lexer::Lexer::new(source).tokenize()?;
144        Self::new(tokens).parse_program()
145    }
146
147    fn next_call_id(&mut self) -> u32 {
148        let id = self.next_call_id;
149        self.next_call_id += 1;
150        id
151    }
152
153    fn peek(&self) -> &Token {
154        &self.tokens[self.current]
155    }
156
157    fn is_at_end(&self) -> bool {
158        matches!(self.peek().typ, TokenType::Eof)
159    }
160
161    fn advance(&mut self) -> &Token {
162        if !self.is_at_end() {
163            self.current += 1;
164        }
165        &self.tokens[self.current - 1]
166    }
167
168    fn check(&self, typ: &TokenType) -> bool {
169        !self.is_at_end() && &self.peek().typ == typ
170    }
171
172    fn match_token(&mut self, types: &[TokenType]) -> bool {
173        for typ in types {
174            if self.check(typ) {
175                self.advance();
176                return true;
177            }
178        }
179        false
180    }
181
182    /// Try to parse something speculatively. If parsing fails, restore position and return None.
183    /// This is useful for lookahead/backtracking scenarios.
184    fn try_parse<T, F>(&mut self, f: F) -> Option<T>
185    where
186        F: FnOnce(&mut Self) -> Result<T, ParserError>,
187    {
188        let saved_pos = self.current;
189        match f(self) {
190            Ok(val) => Some(val),
191            Err(_) => {
192                self.current = saved_pos;
193                None
194            }
195        }
196    }
197
198    /// Try to parse type arguments: <type1, type2, ...>
199    /// Returns None if this isn't actually type arguments (e.g., it's a comparison)
200    fn try_parse_type_args(&mut self) -> Option<Vec<String>> {
201        self.try_parse(|p| {
202            p.consume(TokenType::Less, "Expected '<'")?;
203
204            let mut type_args = vec![];
205
206            loop {
207                // Parse type name (identifier or type keyword like int/float)
208                let type_name = match &p.peek().typ {
209                    TokenType::Ident(name) => name.clone(),
210                    TokenType::Int => "int".to_string(),
211                    TokenType::Float => "float".to_string(),
212                    _ => return Err(p.unexpected()),
213                };
214                p.advance();
215                type_args.push(type_name);
216
217                // Check for comma (more types) or end
218                if p.match_token(&[TokenType::Comma]) {
219                    continue;
220                } else if p.match_token(&[TokenType::Greater]) {
221                    break;
222                } else {
223                    return Err(p.unexpected());
224                }
225            }
226
227            Ok(type_args)
228        })
229    }
230
231    /// Skip any newline tokens
232    fn skip_newlines(&mut self) {
233        while self.match_token(&[TokenType::Newline]) {}
234    }
235
236    /// Skip newlines, indents, and dedents (whitespace tokens)
237    fn skip_whitespace(&mut self) {
238        while self.match_token(&[TokenType::Newline, TokenType::Indent, TokenType::Dedent]) {}
239    }
240
241    /// Parse an optional type suffix: an array `[]`, or — for a collection type
242    /// (`array`/`matrix`/`map`) — a generic `<...>` argument list. Returns the
243    /// type's textual form (e.g. `float[]`, `array<Point>`, `map<string, int>`).
244    /// The generic form is limited to collection bases so a bare `x < y > z`
245    /// comparison is never mistaken for a type.
246    fn parse_type_suffix(&mut self, type_name: String) -> Result<String, ParserError> {
247        if self.match_token(&[TokenType::LBracket]) {
248            self.consume(TokenType::RBracket, "Expected ']' after '[' in array type")?;
249            Ok(format!("{type_name}[]"))
250        } else if matches!(type_name.as_str(), "array" | "matrix" | "map")
251            && self.match_token(&[TokenType::Less])
252        {
253            let mut args = Vec::new();
254            loop {
255                args.push(self.parse_type()?);
256                if !self.match_token(&[TokenType::Comma]) {
257                    break;
258                }
259            }
260            self.consume(
261                TokenType::Greater,
262                "Expected '>' after generic type arguments",
263            )?;
264            Ok(format!("{type_name}<{}>", args.join(", ")))
265        } else {
266            Ok(type_name)
267        }
268    }
269
270    /// Parse a type name (`int`/`float`/identifier) with its optional suffix.
271    fn parse_type(&mut self) -> Result<String, ParserError> {
272        let base = match &self.peek().typ {
273            TokenType::Int => "int".to_string(),
274            TokenType::Float => "float".to_string(),
275            TokenType::Ident(name) => name.clone(),
276            _ => return Err(self.unexpected()),
277        };
278        self.advance();
279        self.parse_type_suffix(base)
280    }
281
282    /// Parse an expression that may be on an indented continuation line.
283    /// Handles: newlines + optional indent + expression + optional dedent
284    fn parse_indented_expression(&mut self) -> Result<Expr, ParserError> {
285        self.skip_newlines();
286
287        // Check if expression is on an indented line
288        let has_indent = self.match_token(&[TokenType::Indent]);
289
290        let expr = self.expression()?;
291
292        // Consume dedent if we had indent
293        if has_indent {
294            self.match_token(&[TokenType::Dedent]);
295        }
296
297        Ok(expr)
298    }
299
300    fn consume(&mut self, typ: TokenType, message: &str) -> Result<&Token, ParserError> {
301        if self.check(&typ) {
302            Ok(self.advance())
303        } else {
304            Err(self.error(ParserErrorKind::ExpectedToken {
305                expected: message.to_string(),
306                found: self.peek().typ.clone(),
307            }))
308        }
309    }
310
311    /// Helper to extract an identifier from the current token and advance
312    fn expect_identifier(&mut self) -> Result<String, ParserError> {
313        if let TokenType::Ident(name) = &self.peek().typ {
314            let name = name.clone();
315            self.advance();
316            Ok(name)
317        } else {
318            Err(self.error(ParserErrorKind::ExpectedVariableName))
319        }
320    }
321
322    /// Generic helper to parse indented field blocks
323    fn parse_indented_fields<T, F>(&mut self, parse_field: F) -> Result<Vec<T>, ParserError>
324    where
325        F: Fn(&mut Self) -> Result<T, ParserError>,
326    {
327        let mut fields = Vec::new();
328
329        loop {
330            // Skip newlines between fields
331            self.skip_newlines();
332
333            // Check for dedent (end of field block)
334            if self.check(&TokenType::Dedent) {
335                self.advance();
336                break;
337            }
338
339            // Check for end of file
340            if self.is_at_end() {
341                break;
342            }
343
344            // Parse a field using the provided parser
345            fields.push(parse_field(self)?);
346        }
347
348        Ok(fields)
349    }
350
351    /// Helper to skip newlines and optionally match indent
352    fn skip_newlines_and_indent(&mut self) {
353        self.skip_newlines();
354        self.match_token(&[TokenType::Indent]);
355    }
356
357    /// Helper to skip newlines and optionally match dedent
358    fn skip_newlines_and_dedent(&mut self) {
359        self.skip_newlines();
360        self.match_token(&[TokenType::Dedent]);
361    }
362
363    /// Helper to speculatively consume indent only if followed by expected token
364    fn try_consume_indent_if_followed_by(&mut self, expected: &TokenType) {
365        if self.check(&TokenType::Indent) {
366            self.try_parse(|p| {
367                p.advance(); // consume indent
368                if p.check(expected) {
369                    Ok(())
370                } else {
371                    Err(p.unexpected())
372                }
373            });
374        }
375    }
376
377    /// Helper to speculatively consume indent/dedent only if followed by one of the expected operators
378    fn try_consume_layout_token_if_followed_by(&mut self, expected: &[TokenType]) {
379        if self.check(&TokenType::Indent) || self.check(&TokenType::Dedent) {
380            self.try_parse(|p| {
381                p.advance(); // consume indent or dedent
382                for typ in expected {
383                    if p.check(typ) {
384                        return Ok(());
385                    }
386                }
387                Err(p.unexpected())
388            });
389        }
390    }
391
392    /// Helper to parse optional type qualifier (const, input, simple, series)
393    fn parse_optional_type_qualifier(&mut self) -> Option<pine_ast::TypeQualifier> {
394        use pine_ast::TypeQualifier;
395        if self.match_token(&[TokenType::Const]) {
396            Some(TypeQualifier::Const)
397        } else if let TokenType::Ident(name) = &self.peek().typ {
398            match name.as_str() {
399                "input" => {
400                    self.advance();
401                    Some(TypeQualifier::Input)
402                }
403                "simple" => {
404                    self.advance();
405                    Some(TypeQualifier::Simple)
406                }
407                "series" => {
408                    self.advance();
409                    Some(TypeQualifier::Series)
410                }
411                _ => None,
412            }
413        } else {
414            None
415        }
416    }
417
418    /// Helper to parse optional type annotation with array suffix
419    /// Returns None if no type annotation is found
420    /// Supports: int, float, or custom identifier types with optional [] suffix
421    fn parse_optional_type_annotation(&mut self) -> Option<String> {
422        if self.match_token(&[TokenType::Int, TokenType::Float]) {
423            let type_name = self.tokens[self.current - 1].lexeme.clone();
424            // Check for array type: int[] or float[]
425            self.parse_type_suffix(type_name).ok()
426        } else if let TokenType::Ident(type_name) = &self.peek().typ {
427            let type_name = type_name.clone();
428            self.try_parse(|p| {
429                p.advance(); // consume potential type name
430
431                // Check for an array `[]` or generic `<...>` suffix.
432                let final_type = p.parse_type_suffix(type_name.clone())?;
433
434                // Must be followed by identifier to be a type annotation
435                if !matches!(p.peek().typ, TokenType::Ident(_)) {
436                    return Err(p.error(ParserErrorKind::ExpectedVariableName));
437                }
438
439                Ok(final_type)
440            })
441        } else {
442            None
443        }
444    }
445
446    /// Generic helper to parse comma-separated lists
447    /// Handles newlines and optional indentation around commas
448    fn parse_comma_separated<T, F>(
449        &mut self,
450        closing_delimiter: &TokenType,
451        parse_item: F,
452    ) -> Result<Vec<T>, ParserError>
453    where
454        F: Fn(&mut Self) -> Result<T, ParserError>,
455    {
456        let mut items = vec![];
457
458        if !self.check(closing_delimiter) {
459            loop {
460                items.push(parse_item(self)?);
461
462                // Skip newlines after each item
463                self.skip_newlines();
464
465                if !self.match_token(&[TokenType::Comma]) {
466                    break;
467                }
468
469                // Skip newlines after comma
470                self.skip_newlines_and_indent();
471            }
472        }
473
474        Ok(items)
475    }
476
477    // Parse a program (top-level)
478    pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
479        let mut statements = vec![];
480
481        while !self.is_at_end() {
482            // Skip any leading newlines and dedents (dedents at top level are from EOF)
483            self.skip_whitespace();
484
485            // Check if we reached EOF after skipping
486            if self.is_at_end() {
487                break;
488            }
489
490            statements.push(self.declaration()?);
491        }
492
493        Ok(statements)
494    }
495
496    // Declarations (var declarations, assignments, etc.)
497    /// The position of the current token, for attaching to a declaration node.
498    fn cur_loc(&self) -> Loc {
499        let token = self.peek();
500        Loc::new(token.line as u32, token.column as u32)
501    }
502
503    /// The position of the most recently consumed token.
504    fn prev_loc(&self) -> Loc {
505        let token = &self.tokens[self.current.saturating_sub(1)];
506        Loc::new(token.line as u32, token.column as u32)
507    }
508
509    /// Build an error at the current token's position.
510    fn error(&self, kind: ParserErrorKind) -> ParserError {
511        ParserError {
512            loc: self.cur_loc(),
513            kind,
514        }
515    }
516
517    /// The current token is not valid here.
518    fn unexpected(&self) -> ParserError {
519        self.error(ParserErrorKind::UnexpectedToken(self.peek().typ.clone()))
520    }
521
522    fn declaration(&mut self) -> Result<Stmt, ParserError> {
523        // Check for type qualifier first (const, input, simple, series)
524        let type_qualifier = self.parse_optional_type_qualifier();
525
526        // Check for var or varip keyword (can be followed by type annotation)
527        let var_kind = if self.match_token(&[TokenType::Varip]) {
528            VarKind::Varip
529        } else if self.match_token(&[TokenType::Var]) {
530            VarKind::Var
531        } else if type_qualifier.is_some() {
532            // If we have a type qualifier but no var/varip, it's still a variable declaration
533            // e.g., const int x = 5
534            VarKind::Plain
535        } else {
536            // Not a var/varip declaration, continue to other statement types
537            return self.check_type_annotated_declaration();
538        };
539
540        // Check if followed by type annotation: var int x = ..., var float y = ..., var label l = ...
541        let type_annotation = self.parse_optional_type_annotation();
542        self.typed_var_declaration_with_qualifier(type_qualifier, type_annotation, var_kind)
543    }
544
545    fn check_type_annotated_declaration(&mut self) -> Result<Stmt, ParserError> {
546        // Check for type declaration: type TypeName
547        if self.match_token(&[TokenType::Type]) {
548            return self.type_declaration(false);
549        }
550
551        // Check for enum declaration: enum EnumName
552        if self.match_token(&[TokenType::Enum]) {
553            return self.enum_declaration(false);
554        }
555
556        // Check for method declaration: method methodName(params) =>
557        if self.match_token(&[TokenType::Method]) {
558            return self.method_declaration(false);
559        }
560
561        // Check for type-annotated declaration without var: int x = ..., float y = ..., int[] x = ...
562        if self.match_token(&[TokenType::Int, TokenType::Float]) {
563            let type_name = self.tokens[self.current - 1].lexeme.clone();
564            // Check for array type: int[] or float[]
565            let type_name = self.parse_type_suffix(type_name)?;
566            return self.typed_var_declaration(Some(type_name), VarKind::Plain);
567        }
568
569        // Check for identifier type with optional []: string x = ..., string[] x = ...
570        if let Some(type_annotation) = self.parse_optional_type_annotation() {
571            return self.typed_var_declaration(Some(type_annotation), VarKind::Plain);
572        }
573
574        self.statement()
575    }
576
577    fn type_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
578        // Parse type name
579        let loc = self.cur_loc();
580        let type_name = self.expect_identifier()?;
581
582        // Expect newline before fields
583        self.consume(TokenType::Newline, "Expected newline after type name")?;
584
585        // Expect indent to start field block
586        self.consume(TokenType::Indent, "Expected indent for type fields")?;
587
588        // Parse fields using generic helper
589        let fields = self.parse_indented_fields(|p| {
590            // Parse optional type qualifier (const, input, simple, series)
591            let type_qualifier = p.parse_optional_type_qualifier();
592
593            // Parse field: type_annotation field_name [= default_value]. The
594            // type may be generic (`array<float>`, `map<string, int>`), so it
595            // goes through the same parser as variable and parameter types.
596            let field_type = p.parse_type()?;
597
598            // Parse field name
599            let field_loc = p.cur_loc();
600            let field_name = p.expect_identifier()?;
601
602            // Parse optional default value
603            let default_value = if p.match_token(&[TokenType::Assign]) {
604                Some(p.expression()?)
605            } else {
606                None
607            };
608
609            Ok(pine_ast::TypeField {
610                name: field_name,
611                type_qualifier,
612                type_annotation: field_type,
613                default_value,
614                loc: field_loc,
615            })
616        })?;
617
618        Ok(Stmt::TypeDecl {
619            name: type_name,
620            fields,
621            export,
622            loc,
623        })
624    }
625
626    fn enum_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
627        // Parse enum name
628        let loc = self.cur_loc();
629        let enum_name = self.expect_identifier()?;
630
631        // Expect newline before fields
632        self.consume(TokenType::Newline, "Expected newline after enum name")?;
633
634        // Expect indent to start field block
635        self.consume(TokenType::Indent, "Expected indent for enum fields")?;
636
637        // Parse fields using generic helper
638        let fields = self.parse_indented_fields(|p| {
639            // Parse field: field_name [= "title"]
640            let field_loc = p.cur_loc();
641            let field_name = p.expect_identifier()?;
642
643            // Parse optional title
644            let title = if p.match_token(&[TokenType::Assign]) {
645                // Expect a string literal for the title
646                if let TokenType::String(s) = &p.peek().typ {
647                    let s = s.clone();
648                    p.advance();
649                    Some(s)
650                } else {
651                    return Err(p.unexpected());
652                }
653            } else {
654                None
655            };
656
657            Ok(pine_ast::EnumField {
658                name: field_name,
659                title,
660                loc: field_loc,
661            })
662        })?;
663
664        Ok(Stmt::EnumDecl {
665            name: enum_name,
666            fields,
667            export,
668            loc,
669        })
670    }
671
672    fn export_statement(&mut self) -> Result<Stmt, ParserError> {
673        // export type typename - delegate to type_declaration
674        if self.match_token(&[TokenType::Type]) {
675            return self.type_declaration(true);
676        }
677
678        // export enum enumname - delegate to enum_declaration
679        if self.match_token(&[TokenType::Enum]) {
680            return self.enum_declaration(true);
681        }
682
683        // export [method] functionname(params) => body
684        // Check if it's a method
685        let is_method = self.match_token(&[TokenType::Method]);
686
687        if is_method {
688            return self.method_declaration(true);
689        }
690
691        // Parse function name
692        let loc = self.cur_loc();
693        let func_name = self.expect_identifier()?;
694
695        // Check if this is a function declaration (followed by '(')
696        if self.check(&TokenType::LParen) {
697            // export functionname(params) => body
698            self.advance(); // consume '('
699
700            let params = self.function_params()?;
701            self.consume(TokenType::RParen, "Expected ')' after function parameters")?;
702            self.consume(TokenType::Arrow, "Expected '=>'")?;
703
704            // Skip optional newline after =>
705            self.match_token(&[TokenType::Newline]);
706
707            // Parse function body (can be a block or single expression)
708            let body = self.parse_block()?;
709
710            Ok(Stmt::FunctionDecl {
711                name: func_name,
712                params,
713                body,
714                export: true,
715                loc,
716            })
717        } else {
718            // Just export functionname (old style - keeping for backward compatibility)
719            Ok(Stmt::Export {
720                item: pine_ast::ExportItem::Type(func_name),
721            })
722        }
723    }
724
725    fn import_statement(&mut self) -> Result<Stmt, ParserError> {
726        // import userName/libraryName/version as alias
727        let path = if let TokenType::Ident(p) = &self.peek().typ {
728            let mut path_parts = vec![p.clone()];
729            self.advance();
730
731            // Parse path segments separated by /
732            while self.match_token(&[TokenType::Slash]) {
733                if let TokenType::Ident(part) = &self.peek().typ {
734                    path_parts.push(part.clone());
735                    self.advance();
736                } else if let TokenType::IntLiteral(n) = &self.peek().typ {
737                    // Version number (an integer path segment)
738                    path_parts.push(n.to_string());
739                    self.advance();
740                } else {
741                    return Err(self.unexpected());
742                }
743            }
744
745            path_parts.join("/")
746        } else {
747            return Err(self.error(ParserErrorKind::ExpectedVariableName));
748        };
749
750        // Expect 'as' keyword - for now we'll check for an identifier "as"
751        if let TokenType::Ident(kw) = &self.peek().typ {
752            if kw != "as" {
753                return Err(self.unexpected());
754            }
755            self.advance();
756        } else {
757            return Err(self.unexpected());
758        }
759
760        // Parse alias
761        let loc = self.cur_loc();
762        let alias = self.expect_identifier()?;
763
764        Ok(Stmt::Import { path, alias, loc })
765    }
766
767    fn method_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
768        // Parse method name
769        let loc = self.cur_loc();
770        let method_name = self.expect_identifier()?;
771
772        // Expect '('
773        self.consume(TokenType::LParen, "Expected '(' after method name")?;
774
775        // Parse parameters
776        let mut params = Vec::new();
777
778        if !self.check(&TokenType::RParen) {
779            loop {
780                // Parse optional type qualifier (const, input, simple, series)
781                let type_qualifier = self.parse_optional_type_qualifier();
782
783                // Parse optional type annotation
784                let type_annotation = self.parse_optional_type_annotation();
785
786                // Parse parameter name
787                let param_loc = self.cur_loc();
788                let param_name = self.expect_identifier()?;
789
790                // Parse optional default value
791                let default_value = if self.match_token(&[TokenType::Assign]) {
792                    Some(self.expression()?)
793                } else {
794                    None
795                };
796
797                params.push(pine_ast::MethodParam {
798                    type_qualifier,
799                    type_annotation,
800                    name: param_name,
801                    default_value,
802                    loc: param_loc,
803                });
804
805                if !self.match_token(&[TokenType::Comma]) {
806                    break;
807                }
808            }
809        }
810
811        self.consume(TokenType::RParen, "Expected ')' after parameters")?;
812
813        // Expect '=>'
814        self.consume(TokenType::Arrow, "Expected '=>' after method parameters")?;
815
816        // Skip optional newline after =>
817        self.match_token(&[TokenType::Newline]);
818
819        // Parse method body (can be a block or single expression)
820        let body = self.parse_block()?;
821
822        Ok(Stmt::MethodDecl {
823            name: method_name,
824            params,
825            body,
826            export,
827            loc,
828        })
829    }
830
831    fn typed_var_declaration(
832        &mut self,
833        type_annotation: Option<String>,
834        var_kind: VarKind,
835    ) -> Result<Stmt, ParserError> {
836        self.typed_var_declaration_with_qualifier(None, type_annotation, var_kind)
837    }
838
839    fn typed_var_declaration_with_qualifier(
840        &mut self,
841        type_qualifier: Option<pine_ast::TypeQualifier>,
842        type_annotation: Option<String>,
843        var_kind: VarKind,
844    ) -> Result<Stmt, ParserError> {
845        let loc = self.cur_loc();
846        let name = self.expect_identifier()?;
847
848        let initializer = if self.match_token(&[TokenType::Assign]) {
849            Some(self.parse_indented_expression()?)
850        } else {
851            None
852        };
853
854        Ok(Stmt::VarDecl {
855            name,
856            type_qualifier,
857            type_annotation,
858            initializer,
859            var_kind,
860            loc,
861        })
862    }
863
864    fn statement(&mut self) -> Result<Stmt, ParserError> {
865        // Check for export statement
866        if self.match_token(&[TokenType::Export]) {
867            return self.export_statement();
868        }
869
870        // Check for import statement
871        if self.match_token(&[TokenType::Import]) {
872            return self.import_statement();
873        }
874
875        // Check for if statement
876        if self.match_token(&[TokenType::If]) {
877            return self.if_statement();
878        }
879
880        // Check for for loop
881        if self.match_token(&[TokenType::For]) {
882            return self.for_statement();
883        }
884
885        // Check for while loop
886        if self.match_token(&[TokenType::While]) {
887            return self.while_statement();
888        }
889
890        // Check for break
891        if self.match_token(&[TokenType::Break]) {
892            return Ok(Stmt::Break {
893                loc: self.prev_loc(),
894            });
895        }
896
897        // Check for continue
898        if self.match_token(&[TokenType::Continue]) {
899            return Ok(Stmt::Continue {
900                loc: self.prev_loc(),
901            });
902        }
903
904        // Check for tuple destructuring: [a, b, c] = func()
905        // But only if followed by = (otherwise it's an array literal)
906        if self.check(&TokenType::LBracket) {
907            let tuple_loc = self.cur_loc();
908            if let Some((names, value)) = self.try_parse(|p| {
909                p.advance(); // consume [
910
911                let mut names = vec![];
912
913                // Parse identifiers separated by commas
914                if !p.check(&TokenType::RBracket) {
915                    loop {
916                        if let TokenType::Ident(name) = &p.peek().typ {
917                            names.push(name.clone());
918                            p.advance();
919                        } else {
920                            // Not all identifiers, not tuple destructuring
921                            return Err(p.error(ParserErrorKind::ExpectedVariableName));
922                        }
923
924                        if !p.match_token(&[TokenType::Comma]) {
925                            break;
926                        }
927                    }
928                }
929
930                p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
931                p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
932
933                // Skip newlines after =
934                p.skip_newlines();
935
936                let value = p.expression()?;
937
938                Ok((names, value))
939            }) {
940                return Ok(Stmt::TupleAssignment {
941                    names,
942                    value,
943                    loc: tuple_loc,
944                });
945            }
946        }
947
948        // Check for implicit variable declaration, reassignment, or function definition
949        // name = expr (declaration)
950        // name := expr (reassignment)
951        // name(params) => body (function definition)
952        if let TokenType::Ident(name) = &self.peek().typ {
953            let name = name.clone();
954            let name_loc = self.cur_loc();
955
956            // Check for function definition: name(params) =>
957            if let Some((param_structs, body)) = self.try_parse(|p| {
958                p.advance(); // consume identifier
959                p.consume(TokenType::LParen, "Expected '('")?;
960
961                let params = p.function_params()?;
962                p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
963                p.consume(TokenType::Arrow, "Expected '=>'")?;
964
965                // Skip optional newline after =>
966                p.match_token(&[TokenType::Newline]);
967
968                // Parse function body (can be a block or single expression)
969                let body = p.parse_block()?;
970
971                Ok((params, body))
972            }) {
973                // Use the full FunctionParam structs for Expr::Function
974                let initializer = Some(Expr::Function {
975                    params: param_structs,
976                    body,
977                });
978                return Ok(Stmt::VarDecl {
979                    name,
980                    type_qualifier: None,
981                    type_annotation: None,
982                    initializer,
983                    var_kind: VarKind::Plain,
984                    loc: name_loc,
985                });
986            }
987
988            // Try to parse as assignment/declaration
989            if let Some(stmt) = self.try_parse(|p| {
990                p.advance(); // consume identifier
991
992                if p.match_token(&[TokenType::Assign]) {
993                    // This is an assignment with =, treat it as a var declaration
994                    let initializer = Some(p.parse_indented_expression()?);
995
996                    Ok(Stmt::VarDecl {
997                        name: name.clone(),
998                        type_qualifier: None,
999                        type_annotation: None,
1000                        initializer,
1001                        var_kind: VarKind::Plain,
1002                        loc: name_loc,
1003                    })
1004                } else if p.match_token(&[TokenType::ColonAssign]) {
1005                    // This is a reassignment with :=
1006                    let value = p.parse_indented_expression()?;
1007
1008                    Ok(Stmt::Assignment {
1009                        target: Expr::Variable {
1010                            name: name.clone(),
1011                            loc: name_loc,
1012                        },
1013                        value,
1014                    })
1015                } else if p.match_token(&[
1016                    TokenType::PlusAssign,
1017                    TokenType::MinusAssign,
1018                    TokenType::StarAssign,
1019                    TokenType::SlashAssign,
1020                ]) {
1021                    // Compound assignment: x += 5 is equivalent to x := x + 5
1022                    let op_tok = &p.tokens[p.current - 1];
1023                    let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1024                    let op = op_tok
1025                        .typ
1026                        .to_binop()
1027                        .expect("compound assign token should convert to binop");
1028
1029                    let right = p.parse_indented_expression()?;
1030
1031                    let value = Expr::Binary {
1032                        left: Box::new(Expr::Variable {
1033                            name: name.clone(),
1034                            loc: name_loc,
1035                        }),
1036                        op,
1037                        right: Box::new(right),
1038                        loc: op_loc,
1039                    };
1040                    Ok(Stmt::Assignment {
1041                        target: Expr::Variable {
1042                            name: name.clone(),
1043                            loc: name_loc,
1044                        },
1045                        value,
1046                    })
1047                } else {
1048                    // Not an assignment operator, fail
1049                    Err(p.unexpected())
1050                }
1051            }) {
1052                return Ok(stmt);
1053            }
1054        }
1055
1056        self.expression_statement()
1057    }
1058
1059    fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
1060        self.parse_comma_separated(&TokenType::RParen, |p| {
1061            // Parse optional type qualifier (const, input, simple, series)
1062            let type_qualifier = p.parse_optional_type_qualifier();
1063
1064            // Parse optional type annotation
1065            let type_annotation = p.parse_optional_type_annotation();
1066
1067            let param_loc = p.cur_loc();
1068            let name = p.expect_identifier()?;
1069
1070            // Check for default value: param = value
1071            let default_value = if p.match_token(&[TokenType::Assign]) {
1072                Some(p.expression()?)
1073            } else {
1074                None
1075            };
1076
1077            Ok(pine_ast::FunctionParam {
1078                type_qualifier,
1079                type_annotation,
1080                name,
1081                default_value,
1082                loc: param_loc,
1083            })
1084        })
1085    }
1086
1087    fn for_statement(&mut self) -> Result<Stmt, ParserError> {
1088        // Check if it's a tuple form: for [index, item] in collection
1089        if self.check(&TokenType::LBracket) {
1090            self.advance(); // consume [
1091
1092            let loc = self.cur_loc();
1093            let index_var = self.expect_identifier()?;
1094
1095            self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
1096
1097            let item_var = self.expect_identifier()?;
1098
1099            self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
1100            self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
1101
1102            let collection = self.expression()?;
1103
1104            // Skip optional newline
1105            self.match_token(&[TokenType::Newline]);
1106
1107            let body = self.parse_block()?;
1108
1109            return Ok(Stmt::ForIn {
1110                index_var: Some(index_var),
1111                item_var,
1112                collection,
1113                body,
1114                loc,
1115            });
1116        }
1117
1118        // Parse variable name
1119        let loc = self.cur_loc();
1120        let var_name = self.expect_identifier()?;
1121
1122        // Check if it's for...in (simple form) or for...to
1123        if self.check(&TokenType::In) {
1124            self.advance(); // consume 'in'
1125
1126            let collection = self.expression()?;
1127
1128            // Skip optional newline
1129            self.match_token(&[TokenType::Newline]);
1130
1131            let body = self.parse_block()?;
1132
1133            Ok(Stmt::ForIn {
1134                index_var: None,
1135                item_var: var_name,
1136                collection,
1137                body,
1138                loc,
1139            })
1140        } else {
1141            // Traditional for...to loop
1142            self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1143            let from = self.expression()?;
1144            self.consume(TokenType::To, "Expected 'to' in for loop")?;
1145            let to = self.expression()?;
1146
1147            // Optional `by <step>`. `by` is a contextual keyword (not lexed as
1148            // one), so match it as an identifier here.
1149            let step = if matches!(&self.peek().typ, TokenType::Ident(name) if name == "by") {
1150                self.advance(); // consume 'by'
1151                Some(self.expression()?)
1152            } else {
1153                None
1154            };
1155
1156            // Skip optional newline after to/step
1157            self.match_token(&[TokenType::Newline]);
1158
1159            // Parse the body - multiple statements
1160            let body = self.parse_block()?;
1161
1162            Ok(Stmt::For {
1163                var_name,
1164                from,
1165                to,
1166                step,
1167                body,
1168                loc,
1169            })
1170        }
1171    }
1172
1173    fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1174        // Parse: while condition
1175        let condition = self.expression()?;
1176
1177        // Skip optional newline after condition
1178        self.match_token(&[TokenType::Newline]);
1179
1180        // Parse the body - multiple statements
1181        let body = self.parse_block()?;
1182
1183        Ok(Stmt::While { condition, body })
1184    }
1185
1186    fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1187        // Parse the condition (no parentheses required in PineScript)
1188        let condition = self.expression()?;
1189
1190        // Skip optional newline after condition
1191        self.match_token(&[TokenType::Newline]);
1192
1193        // Parse the then branch - multiple statements until we hit 'else', dedent, or certain keywords
1194        let then_branch = self.parse_block()?;
1195
1196        // Parse else if branches
1197        let mut else_if_branches = Vec::new();
1198
1199        loop {
1200            // Skip any newlines before else
1201            self.skip_newlines();
1202
1203            // Check if we have "else if"
1204            if self.check(&TokenType::Else) {
1205                if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1206                    p.advance(); // consume 'else'
1207                                 // Check if next token is 'if'
1208                    if p.match_token(&[TokenType::If]) {
1209                        // This is an else if
1210                        let else_if_condition = p.expression()?;
1211                        p.match_token(&[TokenType::Newline]);
1212                        let else_if_body = p.parse_block()?;
1213                        Ok((else_if_condition, else_if_body))
1214                    } else {
1215                        Err(p.unexpected())
1216                    }
1217                }) {
1218                    else_if_branches.push((else_if_condition, else_if_body));
1219                } else {
1220                    break;
1221                }
1222            } else {
1223                break;
1224            }
1225        }
1226
1227        // Check for final else branch
1228        self.skip_newlines();
1229
1230        let else_branch = if self.match_token(&[TokenType::Else]) {
1231            // Skip optional newline after else
1232            self.match_token(&[TokenType::Newline]);
1233
1234            Some(self.parse_block()?)
1235        } else {
1236            None
1237        };
1238
1239        Ok(Stmt::If {
1240            condition,
1241            then_branch,
1242            else_if_branches,
1243            else_branch,
1244        })
1245    }
1246
1247    fn if_expression(&mut self) -> Result<Expr, ParserError> {
1248        // Consume 'if' token
1249        self.consume(TokenType::If, "Expected 'if'")?;
1250
1251        // Parse the condition
1252        let condition = self.expression()?;
1253
1254        // Skip optional newline after condition
1255        self.match_token(&[TokenType::Newline]);
1256
1257        // Skip optional indent
1258        self.match_token(&[TokenType::Indent]);
1259
1260        // Parse the then expression (single expression, not a block of statements)
1261        let then_expr = self.expression()?;
1262
1263        // Skip newlines and dedent
1264        self.skip_newlines();
1265        self.match_token(&[TokenType::Dedent]);
1266
1267        // Parse else if branches
1268        let mut else_if_branches = Vec::new();
1269
1270        loop {
1271            // Skip any newlines before else
1272            self.skip_newlines();
1273
1274            // Check if we have "else if"
1275            if self.check(&TokenType::Else) {
1276                if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1277                    p.advance(); // consume 'else'
1278                                 // Check if next token is 'if'
1279                    if p.match_token(&[TokenType::If]) {
1280                        // This is an else if
1281                        let else_if_condition = p.expression()?;
1282                        p.match_token(&[TokenType::Newline]);
1283                        p.match_token(&[TokenType::Indent]);
1284                        let else_if_expr = p.expression()?;
1285                        p.skip_newlines();
1286                        p.match_token(&[TokenType::Dedent]);
1287                        Ok((else_if_condition, else_if_expr))
1288                    } else {
1289                        Err(p.unexpected())
1290                    }
1291                }) {
1292                    else_if_branches.push((else_if_condition, else_if_expr));
1293                } else {
1294                    break;
1295                }
1296            } else {
1297                break;
1298            }
1299        }
1300
1301        // Parse final else branch (optional - if not present, returns na)
1302        self.skip_newlines();
1303        let else_expr = if self.match_token(&[TokenType::Else]) {
1304            // Skip optional newline after else
1305            self.match_token(&[TokenType::Newline]);
1306
1307            // Skip optional indent
1308            self.match_token(&[TokenType::Indent]);
1309
1310            // Parse else expression
1311            let expr = self.expression()?;
1312
1313            // Skip newlines and optional dedent
1314            self.skip_newlines();
1315            self.match_token(&[TokenType::Dedent]);
1316
1317            Some(Box::new(expr))
1318        } else {
1319            None // Will return na if no branch matches
1320        };
1321
1322        Ok(Expr::IfExpr {
1323            condition: Box::new(condition),
1324            then_expr: Box::new(then_expr),
1325            else_if_branches,
1326            else_expr,
1327        })
1328    }
1329
1330    fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1331        let mut stmts = vec![];
1332
1333        // Expect an indent token to start the block
1334        if !self.match_token(&[TokenType::Indent]) {
1335            // No indent means single-line block or empty block
1336            // Try to parse a single statement on the same line
1337            if !self.check(&TokenType::Newline)
1338                && !self.check(&TokenType::Else)
1339                && !self.is_at_end()
1340            {
1341                stmts.push(self.declaration()?);
1342            }
1343            return Ok(stmts);
1344        }
1345
1346        // Parse statements until we hit a dedent
1347        loop {
1348            // Skip leading newlines
1349            self.skip_newlines();
1350
1351            // Check for else (which ends the then branch)
1352            if self.check(&TokenType::Else) {
1353                break;
1354            }
1355
1356            // Check for end of block
1357            if self.check(&TokenType::Dedent) {
1358                self.advance(); // consume the dedent
1359
1360                // Check if else follows the dedent
1361                self.skip_newlines();
1362                if self.check(&TokenType::Else) {
1363                    break;
1364                }
1365
1366                // If not else, we're truly done
1367                break;
1368            }
1369
1370            // Stop at EOF
1371            if self.is_at_end() {
1372                break;
1373            }
1374
1375            // Parse a statement
1376            stmts.push(self.declaration()?);
1377        }
1378
1379        Ok(stmts)
1380    }
1381
1382    fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1383        let expr = self.expression()?;
1384
1385        // Check if this is an assignment statement (e.g., obj.field := value)
1386        if self.match_token(&[TokenType::ColonAssign]) {
1387            let value = self.parse_indented_expression()?;
1388            return Ok(Stmt::Assignment {
1389                target: expr,
1390                value,
1391            });
1392        }
1393
1394        Ok(Stmt::Expression(expr))
1395    }
1396
1397    // Expression parsing with precedence
1398    fn expression(&mut self) -> Result<Expr, ParserError> {
1399        self.ternary()
1400    }
1401
1402    /// Generic binary operator parser using left-associativity
1403    fn binary_left_assoc(
1404        &mut self,
1405        operators: &[TokenType],
1406        next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1407    ) -> Result<Expr, ParserError> {
1408        let mut expr = next_precedence(self)?;
1409
1410        loop {
1411            // Skip newlines before operators (for leading operators on continuation lines)
1412            self.skip_newlines();
1413
1414            if !self.match_token(operators) {
1415                break;
1416            }
1417
1418            let op_tok = &self.tokens[self.current - 1];
1419            let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1420            let op = op_tok
1421                .typ
1422                .to_binop()
1423                .expect("matched operator token should convert to binop");
1424
1425            // Skip newlines after binary operators (for multi-line expressions)
1426            self.skip_newlines_and_indent();
1427
1428            let right = next_precedence(self)?;
1429            expr = Expr::Binary {
1430                left: Box::new(expr),
1431                op,
1432                right: Box::new(right),
1433                loc: op_loc,
1434            };
1435        }
1436
1437        Ok(expr)
1438    }
1439
1440    fn ternary(&mut self) -> Result<Expr, ParserError> {
1441        // Check for if expression first
1442        if self.check(&TokenType::If) {
1443            return self.if_expression();
1444        }
1445
1446        let mut expr = self.logical_or()?;
1447
1448        // Skip newlines before '?' for multi-line ternaries
1449        self.skip_newlines();
1450
1451        // Skip indent if followed by '?' (for multiline ternaries)
1452        self.try_consume_indent_if_followed_by(&TokenType::Question);
1453
1454        if self.match_token(&[TokenType::Question]) {
1455            // Skip newlines after '?'
1456            self.skip_newlines_and_indent();
1457
1458            let then_expr = self.expression()?;
1459
1460            // Skip newlines before ':'
1461            self.skip_newlines();
1462
1463            // Skip indent if followed by ':' (for multiline ternaries)
1464            self.try_consume_indent_if_followed_by(&TokenType::Colon);
1465
1466            self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1467
1468            // Skip newlines after ':'
1469            self.skip_newlines_and_indent();
1470
1471            let else_expr = self.expression()?;
1472            expr = Expr::Ternary {
1473                condition: Box::new(expr),
1474                then_expr: Box::new(then_expr),
1475                else_expr: Box::new(else_expr),
1476            };
1477        }
1478
1479        Ok(expr)
1480    }
1481
1482    fn logical_or(&mut self) -> Result<Expr, ParserError> {
1483        self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1484    }
1485
1486    fn logical_and(&mut self) -> Result<Expr, ParserError> {
1487        self.binary_left_assoc(&[TokenType::And], Self::equality)
1488    }
1489
1490    fn equality(&mut self) -> Result<Expr, ParserError> {
1491        self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1492    }
1493
1494    fn comparison(&mut self) -> Result<Expr, ParserError> {
1495        self.binary_left_assoc(
1496            &[
1497                TokenType::Greater,
1498                TokenType::Less,
1499                TokenType::GreaterEqual,
1500                TokenType::LessEqual,
1501            ],
1502            Self::term,
1503        )
1504    }
1505
1506    fn term(&mut self) -> Result<Expr, ParserError> {
1507        let mut expr = self.factor()?;
1508
1509        loop {
1510            // Skip newlines before operators (for leading operators on continuation lines)
1511            self.skip_newlines();
1512
1513            // Skip indent/dedent if followed by an operator (for leading operators on continuation lines)
1514            self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1515
1516            if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
1517                break;
1518            }
1519
1520            let op_tok = &self.tokens[self.current - 1];
1521            let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1522            let op = op_tok
1523                .typ
1524                .to_binop()
1525                .expect("term token should convert to binop");
1526            // Skip newlines after binary operators (for multi-line expressions)
1527            self.skip_newlines_and_indent();
1528            let right = self.factor()?;
1529            expr = Expr::Binary {
1530                left: Box::new(expr),
1531                op,
1532                right: Box::new(right),
1533                loc: op_loc,
1534            };
1535        }
1536
1537        Ok(expr)
1538    }
1539
1540    fn factor(&mut self) -> Result<Expr, ParserError> {
1541        self.binary_left_assoc(
1542            &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1543            Self::unary,
1544        )
1545    }
1546
1547    fn unary(&mut self) -> Result<Expr, ParserError> {
1548        if self.match_token(&[TokenType::Minus]) {
1549            let expr = self.unary()?;
1550            return Ok(Expr::Unary {
1551                op: UnOp::Neg,
1552                expr: Box::new(expr),
1553            });
1554        }
1555
1556        if self.match_token(&[TokenType::Not]) {
1557            let expr = self.unary()?;
1558            return Ok(Expr::Unary {
1559                op: UnOp::Not,
1560                expr: Box::new(expr),
1561            });
1562        }
1563
1564        self.postfix()
1565    }
1566
1567    fn postfix(&mut self) -> Result<Expr, ParserError> {
1568        let mut expr = self.primary()?;
1569
1570        // A `switch`/`if` block expression spans lines and terminates the
1571        // expression. A following `[`/`.`/`(` begins a new statement, not a
1572        // postfix operator on the block's result (its trailing NEWLINE/DEDENT
1573        // has already been consumed, so the loop below can't see the boundary).
1574        if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1575            return Ok(expr);
1576        }
1577
1578        loop {
1579            if self.match_token(&[TokenType::Dot]) {
1580                // Member access: expr.member
1581                // Allow keywords as member names (e.g., input.int, color.new)
1582                let member = match &self.peek().typ {
1583                    TokenType::Ident(name) => {
1584                        let name = name.clone();
1585                        self.advance();
1586                        name
1587                    }
1588                    TokenType::Int => {
1589                        self.advance();
1590                        "int".to_string()
1591                    }
1592                    TokenType::Float => {
1593                        self.advance();
1594                        "float".to_string()
1595                    }
1596                    _ => {
1597                        // Try to use the lexeme if it's a keyword
1598                        let lexeme = self.peek().lexeme.clone();
1599                        if !lexeme.is_empty() {
1600                            self.advance();
1601                            lexeme
1602                        } else {
1603                            return Err(self.error(ParserErrorKind::ExpectedIdentifierAfterDot));
1604                        }
1605                    }
1606                };
1607                let member_loc = self.prev_loc();
1608                expr = Expr::MemberAccess {
1609                    object: Box::new(expr),
1610                    member,
1611                    member_loc,
1612                };
1613            } else if self.match_token(&[TokenType::LBracket]) {
1614                // Historical reference: expr[index]
1615                let index = self.expression()?;
1616                self.consume(TokenType::RBracket, "Expected ']'")?;
1617                expr = Expr::Index {
1618                    expr: Box::new(expr),
1619                    index: Box::new(index),
1620                    id: self.next_call_id(),
1621                };
1622            } else if self.check(&TokenType::Less) {
1623                // Try to parse type arguments: <type>
1624                // This is tricky because < can also be a comparison operator
1625                // We use try_parse to backtrack if it's not actually type args
1626                let type_args = self.try_parse_type_args().unwrap_or_default();
1627
1628                // After type args, we must have a function call
1629                if self.match_token(&[TokenType::LParen]) {
1630                    let lparen = &self.tokens[self.current - 1];
1631                    let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1632                    let id = self.next_call_id();
1633                    let args = self.arguments()?;
1634                    self.consume(TokenType::RParen, "Expected ')'")?;
1635                    expr = Expr::Call {
1636                        callee: Box::new(expr),
1637                        type_args,
1638                        args,
1639                        id,
1640                        loc: call_loc,
1641                    };
1642                } else {
1643                    // Not a function call, just break
1644                    break;
1645                }
1646            } else if self.match_token(&[TokenType::LParen]) {
1647                // Function call without type arguments
1648                let lparen = &self.tokens[self.current - 1];
1649                let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1650                let id = self.next_call_id();
1651                let args = self.arguments()?;
1652                self.consume(TokenType::RParen, "Expected ')'")?;
1653                expr = Expr::Call {
1654                    callee: Box::new(expr),
1655                    type_args: vec![],
1656                    args,
1657                    id,
1658                    loc: call_loc,
1659                };
1660            } else {
1661                break;
1662            }
1663        }
1664
1665        Ok(expr)
1666    }
1667
1668    fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1669        let mut args = vec![];
1670
1671        if !self.check(&TokenType::RParen) {
1672            loop {
1673                // Check for named argument: name=value
1674                // In PineScript, function calls can have named arguments like plot(x, title="foo", color=red)
1675                // `type` is a keyword (v5 UDTs) but is also v3/v4's `input(..., type=...)`
1676                // parameter name, so accept it as a key too.
1677                let key_name = match &self.peek().typ {
1678                    TokenType::Ident(name) => Some(name.clone()),
1679                    TokenType::Type => Some("type".to_string()),
1680                    _ => None,
1681                };
1682                if let Some(name) = key_name {
1683                    if let Some((name, value)) = self.try_parse(|p| {
1684                        p.advance(); // consume identifier
1685                        if p.check(&TokenType::Assign) {
1686                            // This is a named argument
1687                            p.advance(); // consume =
1688                            let value = p.expression()?;
1689                            Ok((name.clone(), value))
1690                        } else {
1691                            Err(p.unexpected())
1692                        }
1693                    }) {
1694                        args.push(Argument::Named { name, value });
1695                    } else {
1696                        // Not a named argument, parse as expression
1697                        let expr = self.expression()?;
1698                        args.push(Argument::Positional(expr));
1699                    }
1700                } else {
1701                    let expr = self.expression()?;
1702                    args.push(Argument::Positional(expr));
1703                }
1704
1705                if !self.match_token(&[TokenType::Comma]) {
1706                    break;
1707                }
1708            }
1709        }
1710
1711        Ok(args)
1712    }
1713
1714    fn primary(&mut self) -> Result<Expr, ParserError> {
1715        if let TokenType::IntLiteral(n) = self.peek().typ {
1716            self.advance();
1717            return Ok(Expr::Literal(Literal::Int(n)));
1718        }
1719
1720        if let TokenType::Number(n) = self.peek().typ {
1721            self.advance();
1722            return Ok(Expr::Literal(Literal::Number(n)));
1723        }
1724
1725        if let TokenType::String(ref s) = self.peek().typ {
1726            let s = s.clone();
1727            self.advance();
1728            return Ok(Expr::Literal(Literal::String(s)));
1729        }
1730
1731        if let TokenType::Bool(b) = self.peek().typ {
1732            self.advance();
1733            return Ok(Expr::Literal(Literal::Bool(b)));
1734        }
1735
1736        if let TokenType::HexColor(ref hex) = self.peek().typ {
1737            let hex = hex.clone();
1738            self.advance();
1739            return Ok(Expr::Literal(Literal::HexColor(hex)));
1740        }
1741
1742        // Handle na as a literal
1743        if self.match_token(&[TokenType::Na]) {
1744            return Ok(Expr::Literal(Literal::Na));
1745        }
1746
1747        // Handle keywords that can be used as identifiers (int, float)
1748        // These can be function names (e.g., int(), float())
1749        if self.match_token(&[TokenType::Int, TokenType::Float]) {
1750            let name = self.tokens[self.current - 1].lexeme.clone();
1751            return Ok(Expr::Variable {
1752                name,
1753                loc: self.prev_loc(),
1754            });
1755        }
1756
1757        if let TokenType::Ident(ref name) = self.peek().typ {
1758            let name = name.clone();
1759            let loc = self.cur_loc();
1760            self.advance();
1761            return Ok(Expr::Variable { name, loc });
1762        }
1763
1764        if self.match_token(&[TokenType::LParen]) {
1765            // Skip newlines and indents after opening parenthesis for multiline expressions
1766            self.skip_newlines();
1767            let had_indent = self.match_token(&[TokenType::Indent]);
1768
1769            let expr = self.expression()?;
1770
1771            // Skip newlines and consume dedent if we had indent
1772            self.skip_newlines();
1773            if had_indent {
1774                self.match_token(&[TokenType::Dedent]);
1775            }
1776
1777            self.consume(TokenType::RParen, "Expected ')'")?;
1778            return Ok(expr);
1779        }
1780
1781        // Switch expression: switch value \n case => result
1782        if self.match_token(&[TokenType::Switch]) {
1783            // The subjectless form (`switch` with boolean arms) has no expression
1784            // after the keyword — just a newline. Treat it as `switch true`, so an
1785            // arm matches when its condition evaluates to `true`.
1786            let value = if self.check(&TokenType::Newline) {
1787                Box::new(Expr::Literal(Literal::Bool(true)))
1788            } else {
1789                Box::new(self.expression()?)
1790            };
1791
1792            // Skip newline after switch value
1793            self.match_token(&[TokenType::Newline]);
1794
1795            // Skip indent for switch block
1796            let has_indent = self.match_token(&[TokenType::Indent]);
1797
1798            let mut cases = vec![];
1799
1800            // Parse cases until we can't parse any more
1801            loop {
1802                // Skip leading newlines
1803                self.skip_newlines();
1804
1805                // Check for dedent (end of switch block)
1806                if self.check(&TokenType::Dedent) {
1807                    if has_indent {
1808                        self.advance(); // consume dedent
1809                    }
1810                    break;
1811                }
1812
1813                // Check if we're done (end of block or EOF)
1814                if self.is_at_end() {
1815                    break;
1816                }
1817
1818                // Check for default case: => result (no pattern)
1819                if self.match_token(&[TokenType::Arrow]) {
1820                    // Skip newlines after =>
1821                    self.skip_newlines();
1822
1823                    // Parse the result expression
1824                    let result = self.expression()?;
1825
1826                    // Use a special "default" literal as the pattern
1827                    let default_pattern = Expr::Literal(Literal::Bool(true));
1828                    cases.push((default_pattern, result));
1829                    continue;
1830                }
1831
1832                // Try to parse a case
1833                if let Some((pattern, result)) = self.try_parse(|p| {
1834                    // Parse the pattern (could be a string, number, identifier, etc.)
1835                    let pattern = p.expression()?;
1836
1837                    // Expect =>
1838                    if !p.match_token(&[TokenType::Arrow]) {
1839                        return Err(p.unexpected());
1840                    }
1841
1842                    // Skip newlines after =>
1843                    p.skip_newlines();
1844
1845                    // Parse the result expression
1846                    let result = p.expression()?;
1847                    Ok((pattern, result))
1848                }) {
1849                    cases.push((pattern, result));
1850                } else {
1851                    break;
1852                }
1853            }
1854
1855            return Ok(Expr::Switch { value, cases });
1856        }
1857
1858        // Array literal: [1, 2, 3]
1859        if self.match_token(&[TokenType::LBracket]) {
1860            // Skip leading newlines
1861            self.skip_newlines_and_indent();
1862
1863            let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1864
1865            // Skip trailing newlines and dedent
1866            self.skip_newlines_and_dedent();
1867
1868            self.consume(TokenType::RBracket, "Expected ']'")?;
1869            return Ok(Expr::Array(elements));
1870        }
1871
1872        Err(self.unexpected())
1873    }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878    use super::*;
1879    use pine_lexer::Lexer;
1880
1881    fn parse_expr(input: &str) -> eyre::Result<Expr> {
1882        let mut lexer = Lexer::new(input);
1883        let tokens = lexer.tokenize()?;
1884        let mut parser = Parser::new(tokens);
1885        let stmts = parser.parse()?;
1886
1887        if let Some(Stmt::Expression(expr)) = stmts.first() {
1888            Ok(expr.clone())
1889        } else {
1890            Err(eyre::eyre!("Expected expression statement".to_string()))
1891        }
1892    }
1893
1894    #[test]
1895    fn test_literals() {
1896        // Numbers
1897        let expr = parse_expr("42").unwrap();
1898        assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1899
1900        // Strings
1901        let expr = parse_expr(r#""hello""#).unwrap();
1902        assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1903
1904        // Booleans
1905        let expr = parse_expr("true").unwrap();
1906        assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1907    }
1908
1909    #[test]
1910    fn test_variables() {
1911        let expr = parse_expr("close").unwrap();
1912        assert_eq!(expr, Expr::var("close"));
1913
1914        let expr = parse_expr("my_var").unwrap();
1915        assert_eq!(expr, Expr::var("my_var"));
1916    }
1917
1918    #[test]
1919    fn test_historical_references() {
1920        // close[1] - previous close
1921        let expr = parse_expr("close[1]").unwrap();
1922        assert!(matches!(expr, Expr::Index { .. }));
1923        if let Expr::Index {
1924            expr: base, index, ..
1925        } = expr
1926        {
1927            assert_eq!(*base, Expr::var("close"));
1928            assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1929        }
1930
1931        // high[5] - 5 bars ago
1932        let expr = parse_expr("high[5]").unwrap();
1933        if let Expr::Index {
1934            expr: base, index, ..
1935        } = expr
1936        {
1937            assert_eq!(*base, Expr::var("high"));
1938            assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1939        }
1940    }
1941
1942    #[test]
1943    fn test_function_calls() {
1944        // Simple function call
1945        let expr = parse_expr("sma(close, 14)").unwrap();
1946        if let Expr::Call {
1947            callee,
1948            type_args,
1949            args,
1950            ..
1951        } = expr
1952        {
1953            assert_eq!(*callee, Expr::var("sma"));
1954            assert_eq!(type_args.len(), 0);
1955            assert_eq!(args.len(), 2);
1956            assert_eq!(args[0], Argument::Positional(Expr::var("close")));
1957            assert_eq!(
1958                args[1],
1959                Argument::Positional(Expr::Literal(Literal::Int(14)))
1960            );
1961        } else {
1962            panic!("Expected function call");
1963        }
1964
1965        // No arguments
1966        let expr = parse_expr("foo()").unwrap();
1967        if let Expr::Call {
1968            callee,
1969            type_args,
1970            args,
1971            ..
1972        } = expr
1973        {
1974            assert_eq!(*callee, Expr::var("foo"));
1975            assert_eq!(type_args.len(), 0);
1976            assert_eq!(args.len(), 0);
1977        }
1978    }
1979
1980    #[test]
1981    fn test_arithmetic_expressions() {
1982        // Addition
1983        let expr = parse_expr("2 + 3").unwrap();
1984        if let Expr::Binary {
1985            left, op, right, ..
1986        } = expr
1987        {
1988            assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1989            assert_eq!(op, BinOp::Add);
1990            assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1991        }
1992
1993        // Multiplication has higher precedence: 2 + 3 * 4 = 2 + (3 * 4)
1994        let expr = parse_expr("2 + 3 * 4").unwrap();
1995        if let Expr::Binary {
1996            left,
1997            op: op1,
1998            right,
1999            ..
2000        } = expr
2001        {
2002            assert_eq!(*left, Expr::Literal(Literal::Int(2)));
2003            assert_eq!(op1, BinOp::Add);
2004            if let Expr::Binary {
2005                left: l2,
2006                op: op2,
2007                right: r2,
2008                ..
2009            } = *right
2010            {
2011                assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
2012                assert_eq!(op2, BinOp::Mul);
2013                assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
2014            }
2015        }
2016
2017        // Division
2018        let expr = parse_expr("10 / 2").unwrap();
2019        if let Expr::Binary {
2020            left, op, right, ..
2021        } = expr
2022        {
2023            assert_eq!(*left, Expr::Literal(Literal::Int(10)));
2024            assert_eq!(op, BinOp::Div);
2025            assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2026        }
2027
2028        // Subtraction
2029        let expr = parse_expr("5 - 3").unwrap();
2030        if let Expr::Binary {
2031            left, op, right, ..
2032        } = expr
2033        {
2034            assert_eq!(*left, Expr::Literal(Literal::Int(5)));
2035            assert_eq!(op, BinOp::Sub);
2036            assert_eq!(*right, Expr::Literal(Literal::Int(3)));
2037        }
2038    }
2039
2040    #[test]
2041    fn test_comparison_expressions() {
2042        // Greater than
2043        let expr = parse_expr("close > open").unwrap();
2044        if let Expr::Binary {
2045            left, op, right, ..
2046        } = expr
2047        {
2048            assert_eq!(*left, Expr::var("close"));
2049            assert_eq!(op, BinOp::Greater);
2050            assert_eq!(*right, Expr::var("open"));
2051        }
2052
2053        // Less than
2054        let expr = parse_expr("rsi < 30").unwrap();
2055        if let Expr::Binary {
2056            left, op, right, ..
2057        } = expr
2058        {
2059            assert_eq!(*left, Expr::var("rsi"));
2060            assert_eq!(op, BinOp::Less);
2061            assert_eq!(*right, Expr::Literal(Literal::Int(30)));
2062        }
2063
2064        // Equality
2065        let expr = parse_expr("x == 5").unwrap();
2066        if let Expr::Binary {
2067            left, op, right, ..
2068        } = expr
2069        {
2070            assert_eq!(*left, Expr::var("x"));
2071            assert_eq!(op, BinOp::Eq);
2072            assert_eq!(*right, Expr::Literal(Literal::Int(5)));
2073        }
2074    }
2075
2076    #[test]
2077    fn test_unary_expressions() {
2078        // Negation
2079        let expr = parse_expr("-5").unwrap();
2080        if let Expr::Unary { op, expr } = expr {
2081            assert_eq!(op, UnOp::Neg);
2082            assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
2083        }
2084
2085        // Double negation
2086        let expr = parse_expr("--10").unwrap();
2087        if let Expr::Unary { op: op1, expr: e1 } = expr {
2088            assert_eq!(op1, UnOp::Neg);
2089            if let Expr::Unary { op: op2, expr: e2 } = *e1 {
2090                assert_eq!(op2, UnOp::Neg);
2091                assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
2092            }
2093        }
2094    }
2095
2096    #[test]
2097    fn test_var_declarations() {
2098        let mut lexer = Lexer::new("var x = 10");
2099        let tokens = lexer.tokenize().unwrap();
2100        let mut parser = Parser::new(tokens);
2101        let stmts = parser.parse().unwrap();
2102
2103        assert_eq!(stmts.len(), 1);
2104        if let Stmt::VarDecl {
2105            name,
2106            type_qualifier,
2107            type_annotation,
2108            initializer,
2109            var_kind,
2110            ..
2111        } = &stmts[0]
2112        {
2113            assert_eq!(name, "x");
2114            assert_eq!(*type_qualifier, None);
2115            assert_eq!(*type_annotation, None);
2116            assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
2117            assert_eq!(
2118                initializer.as_ref().unwrap(),
2119                &Expr::Literal(Literal::Int(10))
2120            );
2121        } else {
2122            panic!("Expected VarDecl");
2123        }
2124
2125        // Var without initializer
2126        let mut lexer = Lexer::new("var y");
2127        let tokens = lexer.tokenize().unwrap();
2128        let mut parser = Parser::new(tokens);
2129        let stmts = parser.parse().unwrap();
2130
2131        if let Stmt::VarDecl {
2132            name, initializer, ..
2133        } = &stmts[0]
2134        {
2135            assert_eq!(name, "y");
2136            assert!(initializer.is_none());
2137        }
2138    }
2139
2140    #[test]
2141    fn test_pinescript_examples() {
2142        // PineScript: close[1] > close[2]
2143        let expr = parse_expr("close[1] > close[2]").unwrap();
2144        assert!(matches!(
2145            expr,
2146            Expr::Binary {
2147                op: BinOp::Greater,
2148                ..
2149            }
2150        ));
2151
2152        // PineScript: sma(close, 14) > sma(close, 28)
2153        let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2154        if let Expr::Binary {
2155            left, op, right, ..
2156        } = expr
2157        {
2158            assert_eq!(op, BinOp::Greater);
2159            assert!(matches!(*left, Expr::Call { .. }));
2160            assert!(matches!(*right, Expr::Call { .. }));
2161        }
2162
2163        // PineScript: (high + low) / 2
2164        let expr = parse_expr("(high + low) / 2").unwrap();
2165        if let Expr::Binary {
2166            left,
2167            op: div_op,
2168            right,
2169            ..
2170        } = expr
2171        {
2172            assert_eq!(div_op, BinOp::Div);
2173            assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2174            assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2175        }
2176    }
2177
2178    /// Helper function to recursively collect all .pine files in a directory
2179    fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2180        walkdir::WalkDir::new(dir)
2181            .into_iter()
2182            .filter_map(|e| e.ok())
2183            .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2184            .map(|e| e.path().to_path_buf())
2185            .collect()
2186    }
2187
2188    #[test]
2189    fn test_parse_testdata_files() -> eyre::Result<()> {
2190        use std::fs;
2191        use std::path::Path;
2192
2193        let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2194
2195        let filter = std::env::var("TEST_FILE").ok();
2196        let debug = std::env::var("DEBUG").is_ok();
2197        let generate_ast = std::env::var("GENERATE_AST").is_ok();
2198
2199        let pine_files = collect_pine_files_recursive(&testdata_dir);
2200
2201        let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2202            let content = fs::read_to_string(path)?;
2203
2204            let mut lexer = Lexer::new(&content);
2205            let tokens = lexer.tokenize()?;
2206
2207            if debug {
2208                println!("Tokens: {:#?}", tokens);
2209            }
2210
2211            let mut parser = Parser::new(tokens);
2212            let ast = parser.parse()?;
2213
2214            if debug {
2215                let ast_json = serde_json::to_string(&ast)?;
2216                println!("AST JSON: {:?}", ast_json);
2217            }
2218
2219            // Check for corresponding _ast.json file
2220            let json_path = path.with_file_name(format!(
2221                "{}_ast.json",
2222                path.file_stem().unwrap().to_str().unwrap()
2223            ));
2224
2225            if generate_ast {
2226                // Generate/overwrite AST JSON file
2227                let json = serde_json::to_string_pretty(&ast)?;
2228                fs::write(&json_path, &json)?;
2229            } else if json_path.exists() {
2230                // Compare with expected AST
2231                let expected_json = fs::read_to_string(&json_path)?;
2232                let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2233
2234                if ast != expected_ast {
2235                    return Err(eyre::eyre!(
2236                        "AST mismatch, expected AST from {:?}",
2237                        json_path
2238                    ));
2239                }
2240            }
2241
2242            Ok(())
2243        };
2244
2245        for path in pine_files {
2246            let filename = path.file_name().unwrap().to_str().unwrap();
2247
2248            // Skip if filter is set and doesn't match
2249            if let Some(ref filter_name) = filter {
2250                if filename != filter_name {
2251                    continue;
2252                }
2253            }
2254
2255            if let Err(e) = process_file(&path) {
2256                return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2257            }
2258        }
2259
2260        Ok(())
2261    }
2262
2263    #[test]
2264    #[ignore]
2265    fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2266        use std::fs;
2267        use std::path::Path;
2268
2269        let testdata_dir =
2270            Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2271
2272        let filter = std::env::var("TEST_FILE").ok();
2273        let debug = std::env::var("DEBUG").is_ok();
2274
2275        let pine_files = collect_pine_files_recursive(&testdata_dir);
2276
2277        let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2278            let content = fs::read_to_string(path)?;
2279
2280            let mut lexer = Lexer::new(&content);
2281            let tokens = lexer.tokenize()?;
2282
2283            if debug {
2284                println!("Tokens: {:#?}", tokens);
2285            }
2286
2287            let mut parser = Parser::new(tokens);
2288            let ast = parser.parse()?;
2289
2290            let ast_json = serde_json::to_string(&ast)?;
2291
2292            if debug {
2293                println!("AST JSON: {:?}", ast_json);
2294            }
2295
2296            Ok(())
2297        };
2298
2299        for path in pine_files {
2300            let filename = path.file_name().unwrap().to_str().unwrap();
2301
2302            // Skip if filter is set and doesn't match
2303            if let Some(ref filter_name) = filter {
2304                if filename != filter_name {
2305                    continue;
2306                }
2307            }
2308
2309            if let Err(e) = process_file(&path) {
2310                return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2311            }
2312        }
2313
2314        Ok(())
2315    }
2316}