Skip to main content

pine_parser/
lib.rs

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