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
39trait TokenTypeExt {
41 fn to_binop(&self) -> Option<BinOp>;
42}
43
44impl TokenTypeExt for TokenType {
45 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 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 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 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 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 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 fn skip_newlines(&mut self) {
205 while self.match_token(&[TokenType::Newline]) {}
206 }
207
208 fn skip_whitespace(&mut self) {
210 while self.match_token(&[TokenType::Newline, TokenType::Indent, TokenType::Dedent]) {}
211 }
212
213 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 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 fn parse_indented_expression(&mut self) -> Result<Expr, ParserError> {
262 self.skip_newlines();
263
264 let has_indent = self.match_token(&[TokenType::Indent]);
266
267 let expr = self.expression()?;
268
269 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 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 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 self.skip_newlines();
310
311 if self.check(&TokenType::Dedent) {
313 self.advance();
314 break;
315 }
316
317 if self.is_at_end() {
319 break;
320 }
321
322 fields.push(parse_field(self)?);
324 }
325
326 Ok(fields)
327 }
328
329 fn skip_newlines_and_indent(&mut self) {
331 self.skip_newlines();
332 self.match_token(&[TokenType::Indent]);
333 }
334
335 fn skip_newlines_and_dedent(&mut self) {
337 self.skip_newlines();
338 self.match_token(&[TokenType::Dedent]);
339 }
340
341 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(); 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 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(); 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 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 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 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(); let final_type = p.parse_type_suffix(type_name.clone())?;
417
418 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 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 self.skip_newlines();
448
449 if !self.match_token(&[TokenType::Comma]) {
450 break;
451 }
452
453 self.skip_newlines_and_indent();
455 }
456 }
457
458 Ok(items)
459 }
460
461 pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
463 let mut statements = vec![];
464
465 while !self.is_at_end() {
466 self.skip_whitespace();
468
469 if self.is_at_end() {
471 break;
472 }
473
474 statements.push(self.declaration()?);
475 }
476
477 Ok(statements)
478 }
479
480 fn cur_loc(&self) -> Loc {
483 let token = self.peek();
484 Loc::new(token.line as u32, token.column as u32)
485 }
486
487 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 let type_qualifier = self.parse_optional_type_qualifier();
496
497 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 VarKind::Plain
506 } else {
507 return self.check_type_annotated_declaration();
509 };
510
511 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 if self.match_token(&[TokenType::Type]) {
519 return self.type_declaration(false);
520 }
521
522 if self.match_token(&[TokenType::Enum]) {
524 return self.enum_declaration(false);
525 }
526
527 if self.match_token(&[TokenType::Method]) {
529 return self.method_declaration(false);
530 }
531
532 if self.match_token(&[TokenType::Int, TokenType::Float]) {
534 let type_name = self.tokens[self.current - 1].lexeme.clone();
535 let type_name = self.parse_type_suffix(type_name)?;
537 return self.typed_var_declaration(Some(type_name), VarKind::Plain);
538 }
539
540 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 let loc = self.cur_loc();
551 let type_name = self.expect_identifier()?;
552
553 self.consume(TokenType::Newline, "Expected newline after type name")?;
555
556 self.consume(TokenType::Indent, "Expected indent for type fields")?;
558
559 let fields = self.parse_indented_fields(|p| {
561 let type_qualifier = p.parse_optional_type_qualifier();
563
564 let field_type = p.parse_type()?;
568
569 let field_loc = p.cur_loc();
571 let field_name = p.expect_identifier()?;
572
573 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 let loc = self.cur_loc();
600 let enum_name = self.expect_identifier()?;
601
602 self.consume(TokenType::Newline, "Expected newline after enum name")?;
604
605 self.consume(TokenType::Indent, "Expected indent for enum fields")?;
607
608 let fields = self.parse_indented_fields(|p| {
610 let field_loc = p.cur_loc();
612 let field_name = p.expect_identifier()?;
613
614 let title = if p.match_token(&[TokenType::Assign]) {
616 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 if self.match_token(&[TokenType::Type]) {
649 return self.type_declaration(true);
650 }
651
652 if self.match_token(&[TokenType::Enum]) {
654 return self.enum_declaration(true);
655 }
656
657 let is_method = self.match_token(&[TokenType::Method]);
660
661 if is_method {
662 return self.method_declaration(true);
663 }
664
665 let loc = self.cur_loc();
667 let func_name = self.expect_identifier()?;
668
669 if self.check(&TokenType::LParen) {
671 self.advance(); let params = self.function_params()?;
675 self.consume(TokenType::RParen, "Expected ')' after function parameters")?;
676 self.consume(TokenType::Arrow, "Expected '=>'")?;
677
678 self.match_token(&[TokenType::Newline]);
680
681 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 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 let path = if let TokenType::Ident(p) = &self.peek().typ {
702 let mut path_parts = vec![p.clone()];
703 self.advance();
704
705 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 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 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 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 let loc = self.cur_loc();
753 let method_name = self.expect_identifier()?;
754
755 self.consume(TokenType::LParen, "Expected '(' after method name")?;
757
758 let mut params = Vec::new();
760
761 if !self.check(&TokenType::RParen) {
762 loop {
763 let type_qualifier = self.parse_optional_type_qualifier();
765
766 let type_annotation = self.parse_optional_type_annotation();
768
769 let param_loc = self.cur_loc();
771 let param_name = self.expect_identifier()?;
772
773 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 self.consume(TokenType::Arrow, "Expected '=>' after method parameters")?;
798
799 self.match_token(&[TokenType::Newline]);
801
802 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 if self.match_token(&[TokenType::Export]) {
850 return self.export_statement();
851 }
852
853 if self.match_token(&[TokenType::Import]) {
855 return self.import_statement();
856 }
857
858 if self.match_token(&[TokenType::If]) {
860 return self.if_statement();
861 }
862
863 if self.match_token(&[TokenType::For]) {
865 return self.for_statement();
866 }
867
868 if self.match_token(&[TokenType::While]) {
870 return self.while_statement();
871 }
872
873 if self.match_token(&[TokenType::Break]) {
875 return Ok(Stmt::Break {
876 loc: self.prev_loc(),
877 });
878 }
879
880 if self.match_token(&[TokenType::Continue]) {
882 return Ok(Stmt::Continue {
883 loc: self.prev_loc(),
884 });
885 }
886
887 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(); let mut names = vec![];
895
896 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 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 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 if let TokenType::Ident(name) = &self.peek().typ {
936 let name = name.clone();
937 let name_loc = self.cur_loc();
938
939 if let Some((param_structs, body)) = self.try_parse(|p| {
941 p.advance(); 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 p.match_token(&[TokenType::Newline]);
950
951 let body = p.parse_block()?;
953
954 Ok((params, body))
955 }) {
956 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 if let Some(stmt) = self.try_parse(|p| {
973 p.advance(); if p.match_token(&[TokenType::Assign]) {
976 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 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 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 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 let type_qualifier = p.parse_optional_type_qualifier();
1049
1050 let type_annotation = p.parse_optional_type_annotation();
1052
1053 let param_loc = p.cur_loc();
1054 let name = p.expect_identifier()?;
1055
1056 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 if self.check(&TokenType::LBracket) {
1076 self.advance(); 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 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 let loc = self.cur_loc();
1106 let var_name = self.expect_identifier()?;
1107
1108 if self.check(&TokenType::In) {
1110 self.advance(); let collection = self.expression()?;
1113
1114 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 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 let step = if matches!(&self.peek().typ, TokenType::Ident(name) if name == "by") {
1136 self.advance(); Some(self.expression()?)
1138 } else {
1139 None
1140 };
1141
1142 self.match_token(&[TokenType::Newline]);
1144
1145 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 let condition = self.expression()?;
1162
1163 self.match_token(&[TokenType::Newline]);
1165
1166 let body = self.parse_block()?;
1168
1169 Ok(Stmt::While { condition, body })
1170 }
1171
1172 fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1173 let condition = self.expression()?;
1175
1176 self.match_token(&[TokenType::Newline]);
1178
1179 let then_branch = self.parse_block()?;
1181
1182 let mut else_if_branches = Vec::new();
1184
1185 loop {
1186 self.skip_newlines();
1188
1189 if self.check(&TokenType::Else) {
1191 if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1192 p.advance(); if p.match_token(&[TokenType::If]) {
1195 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 self.skip_newlines();
1218
1219 let else_branch = if self.match_token(&[TokenType::Else]) {
1220 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 self.consume(TokenType::If, "Expected 'if'")?;
1239
1240 let condition = self.expression()?;
1242
1243 self.match_token(&[TokenType::Newline]);
1245
1246 self.match_token(&[TokenType::Indent]);
1248
1249 let then_expr = self.expression()?;
1251
1252 self.skip_newlines();
1254 self.match_token(&[TokenType::Dedent]);
1255
1256 let mut else_if_branches = Vec::new();
1258
1259 loop {
1260 self.skip_newlines();
1262
1263 if self.check(&TokenType::Else) {
1265 if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1266 p.advance(); if p.match_token(&[TokenType::If]) {
1269 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 self.skip_newlines();
1295 let else_expr = if self.match_token(&[TokenType::Else]) {
1296 self.match_token(&[TokenType::Newline]);
1298
1299 self.match_token(&[TokenType::Indent]);
1301
1302 let expr = self.expression()?;
1304
1305 self.skip_newlines();
1307 self.match_token(&[TokenType::Dedent]);
1308
1309 Some(Box::new(expr))
1310 } else {
1311 None };
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 if !self.match_token(&[TokenType::Indent]) {
1327 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 loop {
1340 self.skip_newlines();
1342
1343 if self.check(&TokenType::Else) {
1345 break;
1346 }
1347
1348 if self.check(&TokenType::Dedent) {
1350 self.advance(); self.skip_newlines();
1354 if self.check(&TokenType::Else) {
1355 break;
1356 }
1357
1358 break;
1360 }
1361
1362 if self.is_at_end() {
1364 break;
1365 }
1366
1367 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 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 fn expression(&mut self) -> Result<Expr, ParserError> {
1391 self.ternary()
1392 }
1393
1394 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 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 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 if self.check(&TokenType::If) {
1435 return self.if_expression();
1436 }
1437
1438 let mut expr = self.logical_or()?;
1439
1440 self.skip_newlines();
1442
1443 self.try_consume_indent_if_followed_by(&TokenType::Question);
1445
1446 if self.match_token(&[TokenType::Question]) {
1447 self.skip_newlines_and_indent();
1449
1450 let then_expr = self.expression()?;
1451
1452 self.skip_newlines();
1454
1455 self.try_consume_indent_if_followed_by(&TokenType::Colon);
1457
1458 self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1459
1460 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 self.skip_newlines();
1504
1505 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 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 if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1567 return Ok(expr);
1568 }
1569
1570 loop {
1571 if self.match_token(&[TokenType::Dot]) {
1572 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 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 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 let type_args = self.try_parse_type_args().unwrap_or_default();
1619
1620 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 break;
1637 }
1638 } else if self.match_token(&[TokenType::LParen]) {
1639 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 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(); if p.check(&TokenType::Assign) {
1678 p.advance(); 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 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 if self.match_token(&[TokenType::Na]) {
1739 return Ok(Expr::Literal(Literal::Na));
1740 }
1741
1742 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 self.skip_newlines();
1762 let had_indent = self.match_token(&[TokenType::Indent]);
1763
1764 let expr = self.expression()?;
1765
1766 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 if self.match_token(&[TokenType::Switch]) {
1778 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 self.match_token(&[TokenType::Newline]);
1789
1790 let has_indent = self.match_token(&[TokenType::Indent]);
1792
1793 let mut cases = vec![];
1794
1795 loop {
1797 self.skip_newlines();
1799
1800 if self.check(&TokenType::Dedent) {
1802 if has_indent {
1803 self.advance(); }
1805 break;
1806 }
1807
1808 if self.is_at_end() {
1810 break;
1811 }
1812
1813 if self.match_token(&[TokenType::Arrow]) {
1815 self.skip_newlines();
1817
1818 let result = self.expression()?;
1820
1821 let default_pattern = Expr::Literal(Literal::Bool(true));
1823 cases.push((default_pattern, result));
1824 continue;
1825 }
1826
1827 if let Some((pattern, result)) = self.try_parse(|p| {
1829 let pattern = p.expression()?;
1831
1832 if !p.match_token(&[TokenType::Arrow]) {
1834 return Err(ParserError::UnexpectedToken(
1835 p.peek().typ.clone(),
1836 p.peek().line,
1837 ));
1838 }
1839
1840 p.skip_newlines();
1842
1843 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 if self.match_token(&[TokenType::LBracket]) {
1858 self.skip_newlines_and_indent();
1860
1861 let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1862
1863 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 let expr = parse_expr("42").unwrap();
1899 assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1900
1901 let expr = parse_expr(r#""hello""#).unwrap();
1903 assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1904
1905 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 let json = serde_json::to_string_pretty(&ast)?;
2229 fs::write(&json_path, &json)?;
2230 } else if json_path.exists() {
2231 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 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 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}