Skip to main content

pine_parser/
lib.rs

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