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        }
871
872        // Check for continue
873        if self.match_token(&[TokenType::Continue]) {
874            return Ok(Stmt::Continue);
875        }
876
877        // Check for tuple destructuring: [a, b, c] = func()
878        // But only if followed by = (otherwise it's an array literal)
879        if self.check(&TokenType::LBracket) {
880            let tuple_loc = self.cur_loc();
881            if let Some((names, value)) = self.try_parse(|p| {
882                p.advance(); // consume [
883
884                let mut names = vec![];
885
886                // Parse identifiers separated by commas
887                if !p.check(&TokenType::RBracket) {
888                    loop {
889                        if let TokenType::Ident(name) = &p.peek().typ {
890                            names.push(name.clone());
891                            p.advance();
892                        } else {
893                            // Not all identifiers, not tuple destructuring
894                            return Err(ParserError::ExpectedVariableName(p.peek().line));
895                        }
896
897                        if !p.match_token(&[TokenType::Comma]) {
898                            break;
899                        }
900                    }
901                }
902
903                p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
904                p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
905
906                // Skip newlines after =
907                p.skip_newlines();
908
909                let value = p.expression()?;
910
911                Ok((names, value))
912            }) {
913                return Ok(Stmt::TupleAssignment {
914                    names,
915                    value,
916                    loc: tuple_loc,
917                });
918            }
919        }
920
921        // Check for implicit variable declaration, reassignment, or function definition
922        // name = expr (declaration)
923        // name := expr (reassignment)
924        // name(params) => body (function definition)
925        if let TokenType::Ident(name) = &self.peek().typ {
926            let name = name.clone();
927            let name_loc = self.cur_loc();
928
929            // Check for function definition: name(params) =>
930            if let Some((param_structs, body)) = self.try_parse(|p| {
931                p.advance(); // consume identifier
932                p.consume(TokenType::LParen, "Expected '('")?;
933
934                let params = p.function_params()?;
935                p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
936                p.consume(TokenType::Arrow, "Expected '=>'")?;
937
938                // Skip optional newline after =>
939                p.match_token(&[TokenType::Newline]);
940
941                // Parse function body (can be a block or single expression)
942                let body = p.parse_block()?;
943
944                Ok((params, body))
945            }) {
946                // Use the full FunctionParam structs for Expr::Function
947                let initializer = Some(Expr::Function {
948                    params: param_structs,
949                    body,
950                });
951                return Ok(Stmt::VarDecl {
952                    name,
953                    type_qualifier: None,
954                    type_annotation: None,
955                    initializer,
956                    var_kind: VarKind::Plain,
957                    loc: name_loc,
958                });
959            }
960
961            // Try to parse as assignment/declaration
962            if let Some(stmt) = self.try_parse(|p| {
963                p.advance(); // consume identifier
964
965                if p.match_token(&[TokenType::Assign]) {
966                    // This is an assignment with =, treat it as a var declaration
967                    let initializer = Some(p.parse_indented_expression()?);
968
969                    Ok(Stmt::VarDecl {
970                        name: name.clone(),
971                        type_qualifier: None,
972                        type_annotation: None,
973                        initializer,
974                        var_kind: VarKind::Plain,
975                        loc: name_loc,
976                    })
977                } else if p.match_token(&[TokenType::ColonAssign]) {
978                    // This is a reassignment with :=
979                    let value = p.parse_indented_expression()?;
980
981                    Ok(Stmt::Assignment {
982                        target: Expr::Variable {
983                            name: name.clone(),
984                            loc: name_loc,
985                        },
986                        value,
987                    })
988                } else if p.match_token(&[
989                    TokenType::PlusAssign,
990                    TokenType::MinusAssign,
991                    TokenType::StarAssign,
992                    TokenType::SlashAssign,
993                ]) {
994                    // Compound assignment: x += 5 is equivalent to x := x + 5
995                    let op_tok = &p.tokens[p.current - 1];
996                    let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
997                    let op = op_tok
998                        .typ
999                        .to_binop()
1000                        .expect("compound assign token should convert to binop");
1001
1002                    let right = p.parse_indented_expression()?;
1003
1004                    let value = Expr::Binary {
1005                        left: Box::new(Expr::Variable {
1006                            name: name.clone(),
1007                            loc: name_loc,
1008                        }),
1009                        op,
1010                        right: Box::new(right),
1011                        loc: op_loc,
1012                    };
1013                    Ok(Stmt::Assignment {
1014                        target: Expr::Variable {
1015                            name: name.clone(),
1016                            loc: name_loc,
1017                        },
1018                        value,
1019                    })
1020                } else {
1021                    // Not an assignment operator, fail
1022                    Err(ParserError::UnexpectedToken(
1023                        p.peek().typ.clone(),
1024                        p.peek().line,
1025                    ))
1026                }
1027            }) {
1028                return Ok(stmt);
1029            }
1030        }
1031
1032        self.expression_statement()
1033    }
1034
1035    fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
1036        self.parse_comma_separated(&TokenType::RParen, |p| {
1037            // Parse optional type qualifier (const, input, simple, series)
1038            let type_qualifier = p.parse_optional_type_qualifier();
1039
1040            // Parse optional type annotation
1041            let type_annotation = p.parse_optional_type_annotation();
1042
1043            let param_loc = p.cur_loc();
1044            let name = p.expect_identifier()?;
1045
1046            // Check for default value: param = value
1047            let default_value = if p.match_token(&[TokenType::Assign]) {
1048                Some(p.expression()?)
1049            } else {
1050                None
1051            };
1052
1053            Ok(pine_ast::FunctionParam {
1054                type_qualifier,
1055                type_annotation,
1056                name,
1057                default_value,
1058                loc: param_loc,
1059            })
1060        })
1061    }
1062
1063    fn for_statement(&mut self) -> Result<Stmt, ParserError> {
1064        // Check if it's a tuple form: for [index, item] in collection
1065        if self.check(&TokenType::LBracket) {
1066            self.advance(); // consume [
1067
1068            let loc = self.cur_loc();
1069            let index_var = self.expect_identifier()?;
1070
1071            self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
1072
1073            let item_var = self.expect_identifier()?;
1074
1075            self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
1076            self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
1077
1078            let collection = self.expression()?;
1079
1080            // Skip optional newline
1081            self.match_token(&[TokenType::Newline]);
1082
1083            let body = self.parse_block()?;
1084
1085            return Ok(Stmt::ForIn {
1086                index_var: Some(index_var),
1087                item_var,
1088                collection,
1089                body,
1090                loc,
1091            });
1092        }
1093
1094        // Parse variable name
1095        let loc = self.cur_loc();
1096        let var_name = self.expect_identifier()?;
1097
1098        // Check if it's for...in (simple form) or for...to
1099        if self.check(&TokenType::In) {
1100            self.advance(); // consume 'in'
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            Ok(Stmt::ForIn {
1110                index_var: None,
1111                item_var: var_name,
1112                collection,
1113                body,
1114                loc,
1115            })
1116        } else {
1117            // Traditional for...to loop
1118            self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1119            let from = self.expression()?;
1120            self.consume(TokenType::To, "Expected 'to' in for loop")?;
1121            let to = self.expression()?;
1122
1123            // Skip optional newline after to
1124            self.match_token(&[TokenType::Newline]);
1125
1126            // Parse the body - multiple statements
1127            let body = self.parse_block()?;
1128
1129            Ok(Stmt::For {
1130                var_name,
1131                from,
1132                to,
1133                body,
1134                loc,
1135            })
1136        }
1137    }
1138
1139    fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1140        // Parse: while condition
1141        let condition = self.expression()?;
1142
1143        // Skip optional newline after condition
1144        self.match_token(&[TokenType::Newline]);
1145
1146        // Parse the body - multiple statements
1147        let body = self.parse_block()?;
1148
1149        Ok(Stmt::While { condition, body })
1150    }
1151
1152    fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1153        // Parse the condition (no parentheses required in PineScript)
1154        let condition = self.expression()?;
1155
1156        // Skip optional newline after condition
1157        self.match_token(&[TokenType::Newline]);
1158
1159        // Parse the then branch - multiple statements until we hit 'else', dedent, or certain keywords
1160        let then_branch = self.parse_block()?;
1161
1162        // Parse else if branches
1163        let mut else_if_branches = Vec::new();
1164
1165        loop {
1166            // Skip any newlines before else
1167            self.skip_newlines();
1168
1169            // Check if we have "else if"
1170            if self.check(&TokenType::Else) {
1171                if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1172                    p.advance(); // consume 'else'
1173                                 // Check if next token is 'if'
1174                    if p.match_token(&[TokenType::If]) {
1175                        // This is an else if
1176                        let else_if_condition = p.expression()?;
1177                        p.match_token(&[TokenType::Newline]);
1178                        let else_if_body = p.parse_block()?;
1179                        Ok((else_if_condition, else_if_body))
1180                    } else {
1181                        Err(ParserError::UnexpectedToken(
1182                            p.peek().typ.clone(),
1183                            p.peek().line,
1184                        ))
1185                    }
1186                }) {
1187                    else_if_branches.push((else_if_condition, else_if_body));
1188                } else {
1189                    break;
1190                }
1191            } else {
1192                break;
1193            }
1194        }
1195
1196        // Check for final else branch
1197        self.skip_newlines();
1198
1199        let else_branch = if self.match_token(&[TokenType::Else]) {
1200            // Skip optional newline after else
1201            self.match_token(&[TokenType::Newline]);
1202
1203            Some(self.parse_block()?)
1204        } else {
1205            None
1206        };
1207
1208        Ok(Stmt::If {
1209            condition,
1210            then_branch,
1211            else_if_branches,
1212            else_branch,
1213        })
1214    }
1215
1216    fn if_expression(&mut self) -> Result<Expr, ParserError> {
1217        // Consume 'if' token
1218        self.consume(TokenType::If, "Expected 'if'")?;
1219
1220        // Parse the condition
1221        let condition = self.expression()?;
1222
1223        // Skip optional newline after condition
1224        self.match_token(&[TokenType::Newline]);
1225
1226        // Skip optional indent
1227        self.match_token(&[TokenType::Indent]);
1228
1229        // Parse the then expression (single expression, not a block of statements)
1230        let then_expr = self.expression()?;
1231
1232        // Skip newlines and dedent
1233        self.skip_newlines();
1234        self.match_token(&[TokenType::Dedent]);
1235
1236        // Parse else if branches
1237        let mut else_if_branches = Vec::new();
1238
1239        loop {
1240            // Skip any newlines before else
1241            self.skip_newlines();
1242
1243            // Check if we have "else if"
1244            if self.check(&TokenType::Else) {
1245                if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1246                    p.advance(); // consume 'else'
1247                                 // Check if next token is 'if'
1248                    if p.match_token(&[TokenType::If]) {
1249                        // This is an else if
1250                        let else_if_condition = p.expression()?;
1251                        p.match_token(&[TokenType::Newline]);
1252                        p.match_token(&[TokenType::Indent]);
1253                        let else_if_expr = p.expression()?;
1254                        p.skip_newlines();
1255                        p.match_token(&[TokenType::Dedent]);
1256                        Ok((else_if_condition, else_if_expr))
1257                    } else {
1258                        Err(ParserError::UnexpectedToken(
1259                            p.peek().typ.clone(),
1260                            p.peek().line,
1261                        ))
1262                    }
1263                }) {
1264                    else_if_branches.push((else_if_condition, else_if_expr));
1265                } else {
1266                    break;
1267                }
1268            } else {
1269                break;
1270            }
1271        }
1272
1273        // Parse final else branch (optional - if not present, returns na)
1274        self.skip_newlines();
1275        let else_expr = if self.match_token(&[TokenType::Else]) {
1276            // Skip optional newline after else
1277            self.match_token(&[TokenType::Newline]);
1278
1279            // Skip optional indent
1280            self.match_token(&[TokenType::Indent]);
1281
1282            // Parse else expression
1283            let expr = self.expression()?;
1284
1285            // Skip newlines and optional dedent
1286            self.skip_newlines();
1287            self.match_token(&[TokenType::Dedent]);
1288
1289            Some(Box::new(expr))
1290        } else {
1291            None // Will return na if no branch matches
1292        };
1293
1294        Ok(Expr::IfExpr {
1295            condition: Box::new(condition),
1296            then_expr: Box::new(then_expr),
1297            else_if_branches,
1298            else_expr,
1299        })
1300    }
1301
1302    fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1303        let mut stmts = vec![];
1304
1305        // Expect an indent token to start the block
1306        if !self.match_token(&[TokenType::Indent]) {
1307            // No indent means single-line block or empty block
1308            // Try to parse a single statement on the same line
1309            if !self.check(&TokenType::Newline)
1310                && !self.check(&TokenType::Else)
1311                && !self.is_at_end()
1312            {
1313                stmts.push(self.declaration()?);
1314            }
1315            return Ok(stmts);
1316        }
1317
1318        // Parse statements until we hit a dedent
1319        loop {
1320            // Skip leading newlines
1321            self.skip_newlines();
1322
1323            // Check for else (which ends the then branch)
1324            if self.check(&TokenType::Else) {
1325                break;
1326            }
1327
1328            // Check for end of block
1329            if self.check(&TokenType::Dedent) {
1330                self.advance(); // consume the dedent
1331
1332                // Check if else follows the dedent
1333                self.skip_newlines();
1334                if self.check(&TokenType::Else) {
1335                    break;
1336                }
1337
1338                // If not else, we're truly done
1339                break;
1340            }
1341
1342            // Stop at EOF
1343            if self.is_at_end() {
1344                break;
1345            }
1346
1347            // Parse a statement
1348            stmts.push(self.declaration()?);
1349        }
1350
1351        Ok(stmts)
1352    }
1353
1354    fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1355        let expr = self.expression()?;
1356
1357        // Check if this is an assignment statement (e.g., obj.field := value)
1358        if self.match_token(&[TokenType::ColonAssign]) {
1359            let value = self.parse_indented_expression()?;
1360            return Ok(Stmt::Assignment {
1361                target: expr,
1362                value,
1363            });
1364        }
1365
1366        Ok(Stmt::Expression(expr))
1367    }
1368
1369    // Expression parsing with precedence
1370    fn expression(&mut self) -> Result<Expr, ParserError> {
1371        self.ternary()
1372    }
1373
1374    /// Generic binary operator parser using left-associativity
1375    fn binary_left_assoc(
1376        &mut self,
1377        operators: &[TokenType],
1378        next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1379    ) -> Result<Expr, ParserError> {
1380        let mut expr = next_precedence(self)?;
1381
1382        loop {
1383            // Skip newlines before operators (for leading operators on continuation lines)
1384            self.skip_newlines();
1385
1386            if !self.match_token(operators) {
1387                break;
1388            }
1389
1390            let op_tok = &self.tokens[self.current - 1];
1391            let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1392            let op = op_tok
1393                .typ
1394                .to_binop()
1395                .expect("matched operator token should convert to binop");
1396
1397            // Skip newlines after binary operators (for multi-line expressions)
1398            self.skip_newlines_and_indent();
1399
1400            let right = next_precedence(self)?;
1401            expr = Expr::Binary {
1402                left: Box::new(expr),
1403                op,
1404                right: Box::new(right),
1405                loc: op_loc,
1406            };
1407        }
1408
1409        Ok(expr)
1410    }
1411
1412    fn ternary(&mut self) -> Result<Expr, ParserError> {
1413        // Check for if expression first
1414        if self.check(&TokenType::If) {
1415            return self.if_expression();
1416        }
1417
1418        let mut expr = self.logical_or()?;
1419
1420        // Skip newlines before '?' for multi-line ternaries
1421        self.skip_newlines();
1422
1423        // Skip indent if followed by '?' (for multiline ternaries)
1424        self.try_consume_indent_if_followed_by(&TokenType::Question);
1425
1426        if self.match_token(&[TokenType::Question]) {
1427            // Skip newlines after '?'
1428            self.skip_newlines_and_indent();
1429
1430            let then_expr = self.expression()?;
1431
1432            // Skip newlines before ':'
1433            self.skip_newlines();
1434
1435            // Skip indent if followed by ':' (for multiline ternaries)
1436            self.try_consume_indent_if_followed_by(&TokenType::Colon);
1437
1438            self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1439
1440            // Skip newlines after ':'
1441            self.skip_newlines_and_indent();
1442
1443            let else_expr = self.expression()?;
1444            expr = Expr::Ternary {
1445                condition: Box::new(expr),
1446                then_expr: Box::new(then_expr),
1447                else_expr: Box::new(else_expr),
1448            };
1449        }
1450
1451        Ok(expr)
1452    }
1453
1454    fn logical_or(&mut self) -> Result<Expr, ParserError> {
1455        self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1456    }
1457
1458    fn logical_and(&mut self) -> Result<Expr, ParserError> {
1459        self.binary_left_assoc(&[TokenType::And], Self::equality)
1460    }
1461
1462    fn equality(&mut self) -> Result<Expr, ParserError> {
1463        self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1464    }
1465
1466    fn comparison(&mut self) -> Result<Expr, ParserError> {
1467        self.binary_left_assoc(
1468            &[
1469                TokenType::Greater,
1470                TokenType::Less,
1471                TokenType::GreaterEqual,
1472                TokenType::LessEqual,
1473            ],
1474            Self::term,
1475        )
1476    }
1477
1478    fn term(&mut self) -> Result<Expr, ParserError> {
1479        let mut expr = self.factor()?;
1480
1481        loop {
1482            // Skip newlines before operators (for leading operators on continuation lines)
1483            self.skip_newlines();
1484
1485            // Skip indent/dedent if followed by an operator (for leading operators on continuation lines)
1486            self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1487
1488            if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
1489                break;
1490            }
1491
1492            let op_tok = &self.tokens[self.current - 1];
1493            let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1494            let op = op_tok
1495                .typ
1496                .to_binop()
1497                .expect("term token should convert to binop");
1498            // Skip newlines after binary operators (for multi-line expressions)
1499            self.skip_newlines_and_indent();
1500            let right = self.factor()?;
1501            expr = Expr::Binary {
1502                left: Box::new(expr),
1503                op,
1504                right: Box::new(right),
1505                loc: op_loc,
1506            };
1507        }
1508
1509        Ok(expr)
1510    }
1511
1512    fn factor(&mut self) -> Result<Expr, ParserError> {
1513        self.binary_left_assoc(
1514            &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1515            Self::unary,
1516        )
1517    }
1518
1519    fn unary(&mut self) -> Result<Expr, ParserError> {
1520        if self.match_token(&[TokenType::Minus]) {
1521            let expr = self.unary()?;
1522            return Ok(Expr::Unary {
1523                op: UnOp::Neg,
1524                expr: Box::new(expr),
1525            });
1526        }
1527
1528        if self.match_token(&[TokenType::Not]) {
1529            let expr = self.unary()?;
1530            return Ok(Expr::Unary {
1531                op: UnOp::Not,
1532                expr: Box::new(expr),
1533            });
1534        }
1535
1536        self.postfix()
1537    }
1538
1539    fn postfix(&mut self) -> Result<Expr, ParserError> {
1540        let mut expr = self.primary()?;
1541
1542        // A `switch`/`if` block expression spans lines and terminates the
1543        // expression. A following `[`/`.`/`(` begins a new statement, not a
1544        // postfix operator on the block's result (its trailing NEWLINE/DEDENT
1545        // has already been consumed, so the loop below can't see the boundary).
1546        if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1547            return Ok(expr);
1548        }
1549
1550        loop {
1551            if self.match_token(&[TokenType::Dot]) {
1552                // Member access: expr.member
1553                // Allow keywords as member names (e.g., input.int, color.new)
1554                let member = match &self.peek().typ {
1555                    TokenType::Ident(name) => {
1556                        let name = name.clone();
1557                        self.advance();
1558                        name
1559                    }
1560                    TokenType::Int => {
1561                        self.advance();
1562                        "int".to_string()
1563                    }
1564                    TokenType::Float => {
1565                        self.advance();
1566                        "float".to_string()
1567                    }
1568                    _ => {
1569                        // Try to use the lexeme if it's a keyword
1570                        let lexeme = self.peek().lexeme.clone();
1571                        if !lexeme.is_empty() {
1572                            self.advance();
1573                            lexeme
1574                        } else {
1575                            return Err(ParserError::ExpectedIdentifierAfterDot(self.peek().line));
1576                        }
1577                    }
1578                };
1579                let member_loc = self.prev_loc();
1580                expr = Expr::MemberAccess {
1581                    object: Box::new(expr),
1582                    member,
1583                    member_loc,
1584                };
1585            } else if self.match_token(&[TokenType::LBracket]) {
1586                // Historical reference: expr[index]
1587                let index = self.expression()?;
1588                self.consume(TokenType::RBracket, "Expected ']'")?;
1589                expr = Expr::Index {
1590                    expr: Box::new(expr),
1591                    index: Box::new(index),
1592                };
1593            } else if self.check(&TokenType::Less) {
1594                // Try to parse type arguments: <type>
1595                // This is tricky because < can also be a comparison operator
1596                // We use try_parse to backtrack if it's not actually type args
1597                let type_args = self.try_parse_type_args().unwrap_or_default();
1598
1599                // After type args, we must have a function call
1600                if self.match_token(&[TokenType::LParen]) {
1601                    let lparen = &self.tokens[self.current - 1];
1602                    let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1603                    let id = self.next_call_id();
1604                    let args = self.arguments()?;
1605                    self.consume(TokenType::RParen, "Expected ')'")?;
1606                    expr = Expr::Call {
1607                        callee: Box::new(expr),
1608                        type_args,
1609                        args,
1610                        id,
1611                        loc: call_loc,
1612                    };
1613                } else {
1614                    // Not a function call, just break
1615                    break;
1616                }
1617            } else if self.match_token(&[TokenType::LParen]) {
1618                // Function call without type arguments
1619                let lparen = &self.tokens[self.current - 1];
1620                let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1621                let id = self.next_call_id();
1622                let args = self.arguments()?;
1623                self.consume(TokenType::RParen, "Expected ')'")?;
1624                expr = Expr::Call {
1625                    callee: Box::new(expr),
1626                    type_args: vec![],
1627                    args,
1628                    id,
1629                    loc: call_loc,
1630                };
1631            } else {
1632                break;
1633            }
1634        }
1635
1636        Ok(expr)
1637    }
1638
1639    fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1640        let mut args = vec![];
1641
1642        if !self.check(&TokenType::RParen) {
1643            loop {
1644                // Check for named argument: name=value
1645                // In PineScript, function calls can have named arguments like plot(x, title="foo", color=red)
1646                // `type` is a keyword (v5 UDTs) but is also v3/v4's `input(..., type=...)`
1647                // parameter name, so accept it as a key too.
1648                let key_name = match &self.peek().typ {
1649                    TokenType::Ident(name) => Some(name.clone()),
1650                    TokenType::Type => Some("type".to_string()),
1651                    _ => None,
1652                };
1653                if let Some(name) = key_name {
1654                    if let Some((name, value)) = self.try_parse(|p| {
1655                        p.advance(); // consume identifier
1656                        if p.check(&TokenType::Assign) {
1657                            // This is a named argument
1658                            p.advance(); // consume =
1659                            let value = p.expression()?;
1660                            Ok((name.clone(), value))
1661                        } else {
1662                            Err(ParserError::UnexpectedToken(
1663                                p.peek().typ.clone(),
1664                                p.peek().line,
1665                            ))
1666                        }
1667                    }) {
1668                        args.push(Argument::Named { name, value });
1669                    } else {
1670                        // Not a named argument, parse as expression
1671                        let expr = self.expression()?;
1672                        args.push(Argument::Positional(expr));
1673                    }
1674                } else {
1675                    let expr = self.expression()?;
1676                    args.push(Argument::Positional(expr));
1677                }
1678
1679                if !self.match_token(&[TokenType::Comma]) {
1680                    break;
1681                }
1682            }
1683        }
1684
1685        Ok(args)
1686    }
1687
1688    fn primary(&mut self) -> Result<Expr, ParserError> {
1689        if let TokenType::IntLiteral(n) = self.peek().typ {
1690            self.advance();
1691            return Ok(Expr::Literal(Literal::Int(n)));
1692        }
1693
1694        if let TokenType::Number(n) = self.peek().typ {
1695            self.advance();
1696            return Ok(Expr::Literal(Literal::Number(n)));
1697        }
1698
1699        if let TokenType::String(ref s) = self.peek().typ {
1700            let s = s.clone();
1701            self.advance();
1702            return Ok(Expr::Literal(Literal::String(s)));
1703        }
1704
1705        if let TokenType::Bool(b) = self.peek().typ {
1706            self.advance();
1707            return Ok(Expr::Literal(Literal::Bool(b)));
1708        }
1709
1710        if let TokenType::HexColor(ref hex) = self.peek().typ {
1711            let hex = hex.clone();
1712            self.advance();
1713            return Ok(Expr::Literal(Literal::HexColor(hex)));
1714        }
1715
1716        // Handle na as a literal
1717        if self.match_token(&[TokenType::Na]) {
1718            return Ok(Expr::Literal(Literal::Na));
1719        }
1720
1721        // Handle keywords that can be used as identifiers (int, float)
1722        // These can be function names (e.g., int(), float())
1723        if self.match_token(&[TokenType::Int, TokenType::Float]) {
1724            let name = self.tokens[self.current - 1].lexeme.clone();
1725            return Ok(Expr::Variable {
1726                name,
1727                loc: self.prev_loc(),
1728            });
1729        }
1730
1731        if let TokenType::Ident(ref name) = self.peek().typ {
1732            let name = name.clone();
1733            let loc = self.cur_loc();
1734            self.advance();
1735            return Ok(Expr::Variable { name, loc });
1736        }
1737
1738        if self.match_token(&[TokenType::LParen]) {
1739            // Skip newlines and indents after opening parenthesis for multiline expressions
1740            self.skip_newlines();
1741            let had_indent = self.match_token(&[TokenType::Indent]);
1742
1743            let expr = self.expression()?;
1744
1745            // Skip newlines and consume dedent if we had indent
1746            self.skip_newlines();
1747            if had_indent {
1748                self.match_token(&[TokenType::Dedent]);
1749            }
1750
1751            self.consume(TokenType::RParen, "Expected ')'")?;
1752            return Ok(expr);
1753        }
1754
1755        // Switch expression: switch value \n case => result
1756        if self.match_token(&[TokenType::Switch]) {
1757            let value = Box::new(self.expression()?);
1758
1759            // Skip newline after switch value
1760            self.match_token(&[TokenType::Newline]);
1761
1762            // Skip indent for switch block
1763            let has_indent = self.match_token(&[TokenType::Indent]);
1764
1765            let mut cases = vec![];
1766
1767            // Parse cases until we can't parse any more
1768            loop {
1769                // Skip leading newlines
1770                self.skip_newlines();
1771
1772                // Check for dedent (end of switch block)
1773                if self.check(&TokenType::Dedent) {
1774                    if has_indent {
1775                        self.advance(); // consume dedent
1776                    }
1777                    break;
1778                }
1779
1780                // Check if we're done (end of block or EOF)
1781                if self.is_at_end() {
1782                    break;
1783                }
1784
1785                // Check for default case: => result (no pattern)
1786                if self.match_token(&[TokenType::Arrow]) {
1787                    // Skip newlines after =>
1788                    self.skip_newlines();
1789
1790                    // Parse the result expression
1791                    let result = self.expression()?;
1792
1793                    // Use a special "default" literal as the pattern
1794                    let default_pattern = Expr::Literal(Literal::Bool(true));
1795                    cases.push((default_pattern, result));
1796                    continue;
1797                }
1798
1799                // Try to parse a case
1800                if let Some((pattern, result)) = self.try_parse(|p| {
1801                    // Parse the pattern (could be a string, number, identifier, etc.)
1802                    let pattern = p.expression()?;
1803
1804                    // Expect =>
1805                    if !p.match_token(&[TokenType::Arrow]) {
1806                        return Err(ParserError::UnexpectedToken(
1807                            p.peek().typ.clone(),
1808                            p.peek().line,
1809                        ));
1810                    }
1811
1812                    // Skip newlines after =>
1813                    p.skip_newlines();
1814
1815                    // Parse the result expression
1816                    let result = p.expression()?;
1817                    Ok((pattern, result))
1818                }) {
1819                    cases.push((pattern, result));
1820                } else {
1821                    break;
1822                }
1823            }
1824
1825            return Ok(Expr::Switch { value, cases });
1826        }
1827
1828        // Array literal: [1, 2, 3]
1829        if self.match_token(&[TokenType::LBracket]) {
1830            // Skip leading newlines
1831            self.skip_newlines_and_indent();
1832
1833            let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1834
1835            // Skip trailing newlines and dedent
1836            self.skip_newlines_and_dedent();
1837
1838            self.consume(TokenType::RBracket, "Expected ']'")?;
1839            return Ok(Expr::Array(elements));
1840        }
1841
1842        Err(ParserError::UnexpectedToken(
1843            self.peek().typ.clone(),
1844            self.peek().line,
1845        ))
1846    }
1847}
1848
1849#[cfg(test)]
1850mod tests {
1851    use super::*;
1852    use pine_lexer::Lexer;
1853
1854    fn parse_expr(input: &str) -> eyre::Result<Expr> {
1855        let mut lexer = Lexer::new(input);
1856        let tokens = lexer.tokenize()?;
1857        let mut parser = Parser::new(tokens);
1858        let stmts = parser.parse()?;
1859
1860        if let Some(Stmt::Expression(expr)) = stmts.first() {
1861            Ok(expr.clone())
1862        } else {
1863            Err(eyre::eyre!("Expected expression statement".to_string()))
1864        }
1865    }
1866
1867    #[test]
1868    fn test_literals() {
1869        // Numbers
1870        let expr = parse_expr("42").unwrap();
1871        assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1872
1873        // Strings
1874        let expr = parse_expr(r#""hello""#).unwrap();
1875        assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1876
1877        // Booleans
1878        let expr = parse_expr("true").unwrap();
1879        assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1880    }
1881
1882    #[test]
1883    fn test_variables() {
1884        let expr = parse_expr("close").unwrap();
1885        assert_eq!(expr, Expr::var("close"));
1886
1887        let expr = parse_expr("my_var").unwrap();
1888        assert_eq!(expr, Expr::var("my_var"));
1889    }
1890
1891    #[test]
1892    fn test_historical_references() {
1893        // close[1] - previous close
1894        let expr = parse_expr("close[1]").unwrap();
1895        assert!(matches!(expr, Expr::Index { .. }));
1896        if let Expr::Index { expr: base, index } = expr {
1897            assert_eq!(*base, Expr::var("close"));
1898            assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1899        }
1900
1901        // high[5] - 5 bars ago
1902        let expr = parse_expr("high[5]").unwrap();
1903        if let Expr::Index { expr: base, index } = expr {
1904            assert_eq!(*base, Expr::var("high"));
1905            assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1906        }
1907    }
1908
1909    #[test]
1910    fn test_function_calls() {
1911        // Simple function call
1912        let expr = parse_expr("sma(close, 14)").unwrap();
1913        if let Expr::Call {
1914            callee,
1915            type_args,
1916            args,
1917            ..
1918        } = expr
1919        {
1920            assert_eq!(*callee, Expr::var("sma"));
1921            assert_eq!(type_args.len(), 0);
1922            assert_eq!(args.len(), 2);
1923            assert_eq!(args[0], Argument::Positional(Expr::var("close")));
1924            assert_eq!(
1925                args[1],
1926                Argument::Positional(Expr::Literal(Literal::Int(14)))
1927            );
1928        } else {
1929            panic!("Expected function call");
1930        }
1931
1932        // No arguments
1933        let expr = parse_expr("foo()").unwrap();
1934        if let Expr::Call {
1935            callee,
1936            type_args,
1937            args,
1938            ..
1939        } = expr
1940        {
1941            assert_eq!(*callee, Expr::var("foo"));
1942            assert_eq!(type_args.len(), 0);
1943            assert_eq!(args.len(), 0);
1944        }
1945    }
1946
1947    #[test]
1948    fn test_arithmetic_expressions() {
1949        // Addition
1950        let expr = parse_expr("2 + 3").unwrap();
1951        if let Expr::Binary {
1952            left, op, right, ..
1953        } = expr
1954        {
1955            assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1956            assert_eq!(op, BinOp::Add);
1957            assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1958        }
1959
1960        // Multiplication has higher precedence: 2 + 3 * 4 = 2 + (3 * 4)
1961        let expr = parse_expr("2 + 3 * 4").unwrap();
1962        if let Expr::Binary {
1963            left,
1964            op: op1,
1965            right,
1966            ..
1967        } = expr
1968        {
1969            assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1970            assert_eq!(op1, BinOp::Add);
1971            if let Expr::Binary {
1972                left: l2,
1973                op: op2,
1974                right: r2,
1975                ..
1976            } = *right
1977            {
1978                assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
1979                assert_eq!(op2, BinOp::Mul);
1980                assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
1981            }
1982        }
1983
1984        // Division
1985        let expr = parse_expr("10 / 2").unwrap();
1986        if let Expr::Binary {
1987            left, op, right, ..
1988        } = expr
1989        {
1990            assert_eq!(*left, Expr::Literal(Literal::Int(10)));
1991            assert_eq!(op, BinOp::Div);
1992            assert_eq!(*right, Expr::Literal(Literal::Int(2)));
1993        }
1994
1995        // Subtraction
1996        let expr = parse_expr("5 - 3").unwrap();
1997        if let Expr::Binary {
1998            left, op, right, ..
1999        } = expr
2000        {
2001            assert_eq!(*left, Expr::Literal(Literal::Int(5)));
2002            assert_eq!(op, BinOp::Sub);
2003            assert_eq!(*right, Expr::Literal(Literal::Int(3)));
2004        }
2005    }
2006
2007    #[test]
2008    fn test_comparison_expressions() {
2009        // Greater than
2010        let expr = parse_expr("close > open").unwrap();
2011        if let Expr::Binary {
2012            left, op, right, ..
2013        } = expr
2014        {
2015            assert_eq!(*left, Expr::var("close"));
2016            assert_eq!(op, BinOp::Greater);
2017            assert_eq!(*right, Expr::var("open"));
2018        }
2019
2020        // Less than
2021        let expr = parse_expr("rsi < 30").unwrap();
2022        if let Expr::Binary {
2023            left, op, right, ..
2024        } = expr
2025        {
2026            assert_eq!(*left, Expr::var("rsi"));
2027            assert_eq!(op, BinOp::Less);
2028            assert_eq!(*right, Expr::Literal(Literal::Int(30)));
2029        }
2030
2031        // Equality
2032        let expr = parse_expr("x == 5").unwrap();
2033        if let Expr::Binary {
2034            left, op, right, ..
2035        } = expr
2036        {
2037            assert_eq!(*left, Expr::var("x"));
2038            assert_eq!(op, BinOp::Eq);
2039            assert_eq!(*right, Expr::Literal(Literal::Int(5)));
2040        }
2041    }
2042
2043    #[test]
2044    fn test_unary_expressions() {
2045        // Negation
2046        let expr = parse_expr("-5").unwrap();
2047        if let Expr::Unary { op, expr } = expr {
2048            assert_eq!(op, UnOp::Neg);
2049            assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
2050        }
2051
2052        // Double negation
2053        let expr = parse_expr("--10").unwrap();
2054        if let Expr::Unary { op: op1, expr: e1 } = expr {
2055            assert_eq!(op1, UnOp::Neg);
2056            if let Expr::Unary { op: op2, expr: e2 } = *e1 {
2057                assert_eq!(op2, UnOp::Neg);
2058                assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
2059            }
2060        }
2061    }
2062
2063    #[test]
2064    fn test_var_declarations() {
2065        let mut lexer = Lexer::new("var x = 10");
2066        let tokens = lexer.tokenize().unwrap();
2067        let mut parser = Parser::new(tokens);
2068        let stmts = parser.parse().unwrap();
2069
2070        assert_eq!(stmts.len(), 1);
2071        if let Stmt::VarDecl {
2072            name,
2073            type_qualifier,
2074            type_annotation,
2075            initializer,
2076            var_kind,
2077            ..
2078        } = &stmts[0]
2079        {
2080            assert_eq!(name, "x");
2081            assert_eq!(*type_qualifier, None);
2082            assert_eq!(*type_annotation, None);
2083            assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
2084            assert_eq!(
2085                initializer.as_ref().unwrap(),
2086                &Expr::Literal(Literal::Int(10))
2087            );
2088        } else {
2089            panic!("Expected VarDecl");
2090        }
2091
2092        // Var without initializer
2093        let mut lexer = Lexer::new("var y");
2094        let tokens = lexer.tokenize().unwrap();
2095        let mut parser = Parser::new(tokens);
2096        let stmts = parser.parse().unwrap();
2097
2098        if let Stmt::VarDecl {
2099            name, initializer, ..
2100        } = &stmts[0]
2101        {
2102            assert_eq!(name, "y");
2103            assert!(initializer.is_none());
2104        }
2105    }
2106
2107    #[test]
2108    fn test_pinescript_examples() {
2109        // PineScript: close[1] > close[2]
2110        let expr = parse_expr("close[1] > close[2]").unwrap();
2111        assert!(matches!(
2112            expr,
2113            Expr::Binary {
2114                op: BinOp::Greater,
2115                ..
2116            }
2117        ));
2118
2119        // PineScript: sma(close, 14) > sma(close, 28)
2120        let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2121        if let Expr::Binary {
2122            left, op, right, ..
2123        } = expr
2124        {
2125            assert_eq!(op, BinOp::Greater);
2126            assert!(matches!(*left, Expr::Call { .. }));
2127            assert!(matches!(*right, Expr::Call { .. }));
2128        }
2129
2130        // PineScript: (high + low) / 2
2131        let expr = parse_expr("(high + low) / 2").unwrap();
2132        if let Expr::Binary {
2133            left,
2134            op: div_op,
2135            right,
2136            ..
2137        } = expr
2138        {
2139            assert_eq!(div_op, BinOp::Div);
2140            assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2141            assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2142        }
2143    }
2144
2145    /// Helper function to recursively collect all .pine files in a directory
2146    fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2147        walkdir::WalkDir::new(dir)
2148            .into_iter()
2149            .filter_map(|e| e.ok())
2150            .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2151            .map(|e| e.path().to_path_buf())
2152            .collect()
2153    }
2154
2155    #[test]
2156    fn test_parse_testdata_files() -> eyre::Result<()> {
2157        use std::fs;
2158        use std::path::Path;
2159
2160        let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2161
2162        let filter = std::env::var("TEST_FILE").ok();
2163        let debug = std::env::var("DEBUG").is_ok();
2164        let generate_ast = std::env::var("GENERATE_AST").is_ok();
2165
2166        let pine_files = collect_pine_files_recursive(&testdata_dir);
2167
2168        let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2169            let content = fs::read_to_string(path)?;
2170
2171            let mut lexer = Lexer::new(&content);
2172            let tokens = lexer.tokenize()?;
2173
2174            if debug {
2175                println!("Tokens: {:#?}", tokens);
2176            }
2177
2178            let mut parser = Parser::new(tokens);
2179            let ast = parser.parse()?;
2180
2181            if debug {
2182                let ast_json = serde_json::to_string(&ast)?;
2183                println!("AST JSON: {:?}", ast_json);
2184            }
2185
2186            // Check for corresponding _ast.json file
2187            let json_path = path.with_file_name(format!(
2188                "{}_ast.json",
2189                path.file_stem().unwrap().to_str().unwrap()
2190            ));
2191
2192            if generate_ast {
2193                // Generate/overwrite AST JSON file
2194                let json = serde_json::to_string_pretty(&ast)?;
2195                fs::write(&json_path, &json)?;
2196            } else if json_path.exists() {
2197                // Compare with expected AST
2198                let expected_json = fs::read_to_string(&json_path)?;
2199                let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2200
2201                if ast != expected_ast {
2202                    return Err(eyre::eyre!(
2203                        "AST mismatch, expected AST from {:?}",
2204                        json_path
2205                    ));
2206                }
2207            }
2208
2209            Ok(())
2210        };
2211
2212        for path in pine_files {
2213            let filename = path.file_name().unwrap().to_str().unwrap();
2214
2215            // Skip if filter is set and doesn't match
2216            if let Some(ref filter_name) = filter {
2217                if filename != filter_name {
2218                    continue;
2219                }
2220            }
2221
2222            if let Err(e) = process_file(&path) {
2223                return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2224            }
2225        }
2226
2227        Ok(())
2228    }
2229
2230    #[test]
2231    #[ignore]
2232    fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2233        use std::fs;
2234        use std::path::Path;
2235
2236        let testdata_dir =
2237            Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2238
2239        let filter = std::env::var("TEST_FILE").ok();
2240        let debug = std::env::var("DEBUG").is_ok();
2241
2242        let pine_files = collect_pine_files_recursive(&testdata_dir);
2243
2244        let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2245            let content = fs::read_to_string(path)?;
2246
2247            let mut lexer = Lexer::new(&content);
2248            let tokens = lexer.tokenize()?;
2249
2250            if debug {
2251                println!("Tokens: {:#?}", tokens);
2252            }
2253
2254            let mut parser = Parser::new(tokens);
2255            let ast = parser.parse()?;
2256
2257            let ast_json = serde_json::to_string(&ast)?;
2258
2259            if debug {
2260                println!("AST JSON: {:?}", ast_json);
2261            }
2262
2263            Ok(())
2264        };
2265
2266        for path in pine_files {
2267            let filename = path.file_name().unwrap().to_str().unwrap();
2268
2269            // Skip if filter is set and doesn't match
2270            if let Some(ref filter_name) = filter {
2271                if filename != filter_name {
2272                    continue;
2273                }
2274            }
2275
2276            if let Err(e) = process_file(&path) {
2277                return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2278            }
2279        }
2280
2281        Ok(())
2282    }
2283}