1pub use pine_ast::{Argument, BinOp, Expr, Literal, Loc, Program, Stmt, UnOp, VarKind};
2use pine_lexer::{Token, TokenType};
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum ParserError {
7 #[error("Unexpected token: {0:?} at line {1}")]
8 UnexpectedToken(TokenType, usize),
9
10 #[error("Expected {expected} but found {found:?} at line {line}")]
11 ExpectedToken {
12 expected: String,
13 found: TokenType,
14 line: usize,
15 },
16
17 #[error("Expected variable name at line {0}")]
18 ExpectedVariableName(usize),
19
20 #[error("Expected parameter name at line {0}")]
21 ExpectedParameterName(usize),
22
23 #[error("Can only call identifiers or member access at line {0}")]
24 InvalidCallTarget(usize),
25
26 #[error("Expected identifier after '.' at line {0}")]
27 ExpectedIdentifierAfterDot(usize),
28
29 #[error(transparent)]
30 Lexer(#[from] pine_lexer::LexerError),
31}
32
33impl From<ParserError> for String {
34 fn from(err: ParserError) -> String {
35 err.to_string()
36 }
37}
38
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 current: usize,
73 next_call_id: u32,
74}
75
76impl Parser {
77 pub fn new(tokens: Vec<Token>) -> Self {
78 let tokens = tokens
79 .into_iter()
80 .filter(|t| !matches!(t.typ, TokenType::Comment(_) | TokenType::BlankLine))
81 .collect();
82 Self {
83 tokens,
84 current: 0,
85 next_call_id: 1,
86 }
87 }
88
89 pub fn parse_source(source: &str) -> Result<Program, ParserError> {
91 let tokens = pine_lexer::Lexer::new(source).tokenize()?;
92 Ok(Program::new(Self::new(tokens).parse()?))
93 }
94
95 fn next_call_id(&mut self) -> u32 {
96 let id = self.next_call_id;
97 self.next_call_id += 1;
98 id
99 }
100
101 fn peek(&self) -> &Token {
102 &self.tokens[self.current]
103 }
104
105 fn is_at_end(&self) -> bool {
106 matches!(self.peek().typ, TokenType::Eof)
107 }
108
109 fn advance(&mut self) -> &Token {
110 if !self.is_at_end() {
111 self.current += 1;
112 }
113 &self.tokens[self.current - 1]
114 }
115
116 fn check(&self, typ: &TokenType) -> bool {
117 !self.is_at_end() && &self.peek().typ == typ
118 }
119
120 fn match_token(&mut self, types: &[TokenType]) -> bool {
121 for typ in types {
122 if self.check(typ) {
123 self.advance();
124 return true;
125 }
126 }
127 false
128 }
129
130 fn try_parse<T, F>(&mut self, f: F) -> Option<T>
133 where
134 F: FnOnce(&mut Self) -> Result<T, ParserError>,
135 {
136 let saved_pos = self.current;
137 match f(self) {
138 Ok(val) => Some(val),
139 Err(_) => {
140 self.current = saved_pos;
141 None
142 }
143 }
144 }
145
146 fn try_parse_type_args(&mut self) -> Option<Vec<String>> {
149 self.try_parse(|p| {
150 p.consume(TokenType::Less, "Expected '<'")?;
151
152 let mut type_args = vec![];
153
154 loop {
155 let type_name = match &p.peek().typ {
157 TokenType::Ident(name) => name.clone(),
158 TokenType::Int => "int".to_string(),
159 TokenType::Float => "float".to_string(),
160 _ => {
161 return Err(ParserError::UnexpectedToken(
162 p.peek().typ.clone(),
163 p.peek().line,
164 ))
165 }
166 };
167 p.advance();
168 type_args.push(type_name);
169
170 if p.match_token(&[TokenType::Comma]) {
172 continue;
173 } else if p.match_token(&[TokenType::Greater]) {
174 break;
175 } else {
176 return Err(ParserError::UnexpectedToken(
177 p.peek().typ.clone(),
178 p.peek().line,
179 ));
180 }
181 }
182
183 Ok(type_args)
184 })
185 }
186
187 fn skip_newlines(&mut self) {
189 while self.match_token(&[TokenType::Newline]) {}
190 }
191
192 fn skip_whitespace(&mut self) {
194 while self.match_token(&[TokenType::Newline, TokenType::Indent, TokenType::Dedent]) {}
195 }
196
197 fn parse_type_suffix(&mut self, type_name: String) -> Result<String, ParserError> {
203 if self.match_token(&[TokenType::LBracket]) {
204 self.consume(TokenType::RBracket, "Expected ']' after '[' in array type")?;
205 Ok(format!("{type_name}[]"))
206 } else if matches!(type_name.as_str(), "array" | "matrix" | "map")
207 && self.match_token(&[TokenType::Less])
208 {
209 let mut args = Vec::new();
210 loop {
211 args.push(self.parse_type()?);
212 if !self.match_token(&[TokenType::Comma]) {
213 break;
214 }
215 }
216 self.consume(
217 TokenType::Greater,
218 "Expected '>' after generic type arguments",
219 )?;
220 Ok(format!("{type_name}<{}>", args.join(", ")))
221 } else {
222 Ok(type_name)
223 }
224 }
225
226 fn parse_type(&mut self) -> Result<String, ParserError> {
228 let base = match &self.peek().typ {
229 TokenType::Int => "int".to_string(),
230 TokenType::Float => "float".to_string(),
231 TokenType::Ident(name) => name.clone(),
232 other => {
233 return Err(ParserError::UnexpectedToken(
234 other.clone(),
235 self.peek().line,
236 ))
237 }
238 };
239 self.advance();
240 self.parse_type_suffix(base)
241 }
242
243 fn parse_indented_expression(&mut self) -> Result<Expr, ParserError> {
246 self.skip_newlines();
247
248 let has_indent = self.match_token(&[TokenType::Indent]);
250
251 let expr = self.expression()?;
252
253 if has_indent {
255 self.match_token(&[TokenType::Dedent]);
256 }
257
258 Ok(expr)
259 }
260
261 fn consume(&mut self, typ: TokenType, message: &str) -> Result<&Token, ParserError> {
262 if self.check(&typ) {
263 Ok(self.advance())
264 } else {
265 Err(ParserError::ExpectedToken {
266 expected: message.to_string(),
267 found: self.peek().typ.clone(),
268 line: self.peek().line,
269 })
270 }
271 }
272
273 fn expect_identifier(&mut self) -> Result<String, ParserError> {
275 if let TokenType::Ident(name) = &self.peek().typ {
276 let name = name.clone();
277 self.advance();
278 Ok(name)
279 } else {
280 Err(ParserError::ExpectedVariableName(self.peek().line))
281 }
282 }
283
284 fn parse_indented_fields<T, F>(&mut self, parse_field: F) -> Result<Vec<T>, ParserError>
286 where
287 F: Fn(&mut Self) -> Result<T, ParserError>,
288 {
289 let mut fields = Vec::new();
290
291 loop {
292 self.skip_newlines();
294
295 if self.check(&TokenType::Dedent) {
297 self.advance();
298 break;
299 }
300
301 if self.is_at_end() {
303 break;
304 }
305
306 fields.push(parse_field(self)?);
308 }
309
310 Ok(fields)
311 }
312
313 fn skip_newlines_and_indent(&mut self) {
315 self.skip_newlines();
316 self.match_token(&[TokenType::Indent]);
317 }
318
319 fn skip_newlines_and_dedent(&mut self) {
321 self.skip_newlines();
322 self.match_token(&[TokenType::Dedent]);
323 }
324
325 fn try_consume_indent_if_followed_by(&mut self, expected: &TokenType) {
327 if self.check(&TokenType::Indent) {
328 self.try_parse(|p| {
329 p.advance(); if p.check(expected) {
331 Ok(())
332 } else {
333 Err(ParserError::UnexpectedToken(
334 p.peek().typ.clone(),
335 p.peek().line,
336 ))
337 }
338 });
339 }
340 }
341
342 fn try_consume_layout_token_if_followed_by(&mut self, expected: &[TokenType]) {
344 if self.check(&TokenType::Indent) || self.check(&TokenType::Dedent) {
345 self.try_parse(|p| {
346 p.advance(); for typ in expected {
348 if p.check(typ) {
349 return Ok(());
350 }
351 }
352 Err(ParserError::UnexpectedToken(
353 p.peek().typ.clone(),
354 p.peek().line,
355 ))
356 });
357 }
358 }
359
360 fn parse_optional_type_qualifier(&mut self) -> Option<pine_ast::TypeQualifier> {
362 use pine_ast::TypeQualifier;
363 if self.match_token(&[TokenType::Const]) {
364 Some(TypeQualifier::Const)
365 } else if let TokenType::Ident(name) = &self.peek().typ {
366 match name.as_str() {
367 "input" => {
368 self.advance();
369 Some(TypeQualifier::Input)
370 }
371 "simple" => {
372 self.advance();
373 Some(TypeQualifier::Simple)
374 }
375 "series" => {
376 self.advance();
377 Some(TypeQualifier::Series)
378 }
379 _ => None,
380 }
381 } else {
382 None
383 }
384 }
385
386 fn parse_optional_type_annotation(&mut self) -> Option<String> {
390 if self.match_token(&[TokenType::Int, TokenType::Float]) {
391 let type_name = self.tokens[self.current - 1].lexeme.clone();
392 self.parse_type_suffix(type_name).ok()
394 } else if let TokenType::Ident(type_name) = &self.peek().typ {
395 let type_name = type_name.clone();
396 self.try_parse(|p| {
397 p.advance(); let final_type = p.parse_type_suffix(type_name.clone())?;
401
402 if !matches!(p.peek().typ, TokenType::Ident(_)) {
404 return Err(ParserError::ExpectedVariableName(p.peek().line));
405 }
406
407 Ok(final_type)
408 })
409 } else {
410 None
411 }
412 }
413
414 fn parse_comma_separated<T, F>(
417 &mut self,
418 closing_delimiter: &TokenType,
419 parse_item: F,
420 ) -> Result<Vec<T>, ParserError>
421 where
422 F: Fn(&mut Self) -> Result<T, ParserError>,
423 {
424 let mut items = vec![];
425
426 if !self.check(closing_delimiter) {
427 loop {
428 items.push(parse_item(self)?);
429
430 self.skip_newlines();
432
433 if !self.match_token(&[TokenType::Comma]) {
434 break;
435 }
436
437 self.skip_newlines_and_indent();
439 }
440 }
441
442 Ok(items)
443 }
444
445 pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
447 let mut statements = vec![];
448
449 while !self.is_at_end() {
450 self.skip_whitespace();
452
453 if self.is_at_end() {
455 break;
456 }
457
458 statements.push(self.declaration()?);
459 }
460
461 Ok(statements)
462 }
463
464 fn cur_loc(&self) -> Loc {
467 let token = self.peek();
468 Loc::new(token.line as u32, token.column as u32)
469 }
470
471 fn prev_loc(&self) -> Loc {
473 let token = &self.tokens[self.current.saturating_sub(1)];
474 Loc::new(token.line as u32, token.column as u32)
475 }
476
477 fn declaration(&mut self) -> Result<Stmt, ParserError> {
478 let type_qualifier = self.parse_optional_type_qualifier();
480
481 let var_kind = if self.match_token(&[TokenType::Varip]) {
483 VarKind::Varip
484 } else if self.match_token(&[TokenType::Var]) {
485 VarKind::Var
486 } else if type_qualifier.is_some() {
487 VarKind::Plain
490 } else {
491 return self.check_type_annotated_declaration();
493 };
494
495 let type_annotation = self.parse_optional_type_annotation();
497 self.typed_var_declaration_with_qualifier(type_qualifier, type_annotation, var_kind)
498 }
499
500 fn check_type_annotated_declaration(&mut self) -> Result<Stmt, ParserError> {
501 if self.match_token(&[TokenType::Type]) {
503 return self.type_declaration(false);
504 }
505
506 if self.match_token(&[TokenType::Enum]) {
508 return self.enum_declaration(false);
509 }
510
511 if self.match_token(&[TokenType::Method]) {
513 return self.method_declaration(false);
514 }
515
516 if self.match_token(&[TokenType::Int, TokenType::Float]) {
518 let type_name = self.tokens[self.current - 1].lexeme.clone();
519 let type_name = self.parse_type_suffix(type_name)?;
521 return self.typed_var_declaration(Some(type_name), VarKind::Plain);
522 }
523
524 if let Some(type_annotation) = self.parse_optional_type_annotation() {
526 return self.typed_var_declaration(Some(type_annotation), VarKind::Plain);
527 }
528
529 self.statement()
530 }
531
532 fn type_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
533 let loc = self.cur_loc();
535 let type_name = self.expect_identifier()?;
536
537 self.consume(TokenType::Newline, "Expected newline after type name")?;
539
540 self.consume(TokenType::Indent, "Expected indent for type fields")?;
542
543 let fields = self.parse_indented_fields(|p| {
545 let type_qualifier = p.parse_optional_type_qualifier();
547
548 let field_type = if p.match_token(&[TokenType::Int, TokenType::Float]) {
551 p.tokens[p.current - 1].lexeme.clone()
552 } else if let TokenType::Ident(type_name) = &p.peek().typ {
553 let type_name = type_name.clone();
554 p.advance();
555 type_name
556 } else {
557 return Err(ParserError::UnexpectedToken(
558 p.peek().typ.clone(),
559 p.peek().line,
560 ));
561 };
562
563 let field_loc = p.cur_loc();
565 let field_name = p.expect_identifier()?;
566
567 let default_value = if p.match_token(&[TokenType::Assign]) {
569 Some(p.expression()?)
570 } else {
571 None
572 };
573
574 Ok(pine_ast::TypeField {
575 name: field_name,
576 type_qualifier,
577 type_annotation: field_type,
578 default_value,
579 loc: field_loc,
580 })
581 })?;
582
583 Ok(Stmt::TypeDecl {
584 name: type_name,
585 fields,
586 export,
587 loc,
588 })
589 }
590
591 fn enum_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
592 let loc = self.cur_loc();
594 let enum_name = self.expect_identifier()?;
595
596 self.consume(TokenType::Newline, "Expected newline after enum name")?;
598
599 self.consume(TokenType::Indent, "Expected indent for enum fields")?;
601
602 let fields = self.parse_indented_fields(|p| {
604 let field_loc = p.cur_loc();
606 let field_name = p.expect_identifier()?;
607
608 let title = if p.match_token(&[TokenType::Assign]) {
610 if let TokenType::String(s) = &p.peek().typ {
612 let s = s.clone();
613 p.advance();
614 Some(s)
615 } else {
616 return Err(ParserError::UnexpectedToken(
617 p.peek().typ.clone(),
618 p.peek().line,
619 ));
620 }
621 } else {
622 None
623 };
624
625 Ok(pine_ast::EnumField {
626 name: field_name,
627 title,
628 loc: field_loc,
629 })
630 })?;
631
632 Ok(Stmt::EnumDecl {
633 name: enum_name,
634 fields,
635 export,
636 loc,
637 })
638 }
639
640 fn export_statement(&mut self) -> Result<Stmt, ParserError> {
641 if self.match_token(&[TokenType::Type]) {
643 return self.type_declaration(true);
644 }
645
646 if self.match_token(&[TokenType::Enum]) {
648 return self.enum_declaration(true);
649 }
650
651 let is_method = self.match_token(&[TokenType::Method]);
654
655 if is_method {
656 return self.method_declaration(true);
657 }
658
659 let loc = self.cur_loc();
661 let func_name = self.expect_identifier()?;
662
663 if self.check(&TokenType::LParen) {
665 self.advance(); let params = self.function_params()?;
669 self.consume(TokenType::RParen, "Expected ')' after function parameters")?;
670 self.consume(TokenType::Arrow, "Expected '=>'")?;
671
672 self.match_token(&[TokenType::Newline]);
674
675 let body = self.parse_block()?;
677
678 Ok(Stmt::FunctionDecl {
679 name: func_name,
680 params,
681 body,
682 export: true,
683 loc,
684 })
685 } else {
686 Ok(Stmt::Export {
688 item: pine_ast::ExportItem::Type(func_name),
689 })
690 }
691 }
692
693 fn import_statement(&mut self) -> Result<Stmt, ParserError> {
694 let path = if let TokenType::Ident(p) = &self.peek().typ {
696 let mut path_parts = vec![p.clone()];
697 self.advance();
698
699 while self.match_token(&[TokenType::Slash]) {
701 if let TokenType::Ident(part) = &self.peek().typ {
702 path_parts.push(part.clone());
703 self.advance();
704 } else if let TokenType::IntLiteral(n) = &self.peek().typ {
705 path_parts.push(n.to_string());
707 self.advance();
708 } else {
709 return Err(ParserError::UnexpectedToken(
710 self.peek().typ.clone(),
711 self.peek().line,
712 ));
713 }
714 }
715
716 path_parts.join("/")
717 } else {
718 return Err(ParserError::ExpectedVariableName(self.peek().line));
719 };
720
721 if let TokenType::Ident(kw) = &self.peek().typ {
723 if kw != "as" {
724 return Err(ParserError::UnexpectedToken(
725 self.peek().typ.clone(),
726 self.peek().line,
727 ));
728 }
729 self.advance();
730 } else {
731 return Err(ParserError::UnexpectedToken(
732 self.peek().typ.clone(),
733 self.peek().line,
734 ));
735 }
736
737 let loc = self.cur_loc();
739 let alias = self.expect_identifier()?;
740
741 Ok(Stmt::Import { path, alias, loc })
742 }
743
744 fn method_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
745 let loc = self.cur_loc();
747 let method_name = self.expect_identifier()?;
748
749 self.consume(TokenType::LParen, "Expected '(' after method name")?;
751
752 let mut params = Vec::new();
754
755 if !self.check(&TokenType::RParen) {
756 loop {
757 let type_qualifier = self.parse_optional_type_qualifier();
759
760 let type_annotation = self.parse_optional_type_annotation();
762
763 let param_loc = self.cur_loc();
765 let param_name = self.expect_identifier()?;
766
767 let default_value = if self.match_token(&[TokenType::Assign]) {
769 Some(self.expression()?)
770 } else {
771 None
772 };
773
774 params.push(pine_ast::MethodParam {
775 type_qualifier,
776 type_annotation,
777 name: param_name,
778 default_value,
779 loc: param_loc,
780 });
781
782 if !self.match_token(&[TokenType::Comma]) {
783 break;
784 }
785 }
786 }
787
788 self.consume(TokenType::RParen, "Expected ')' after parameters")?;
789
790 self.consume(TokenType::Arrow, "Expected '=>' after method parameters")?;
792
793 self.match_token(&[TokenType::Newline]);
795
796 let body = self.parse_block()?;
798
799 Ok(Stmt::MethodDecl {
800 name: method_name,
801 params,
802 body,
803 export,
804 loc,
805 })
806 }
807
808 fn typed_var_declaration(
809 &mut self,
810 type_annotation: Option<String>,
811 var_kind: VarKind,
812 ) -> Result<Stmt, ParserError> {
813 self.typed_var_declaration_with_qualifier(None, type_annotation, var_kind)
814 }
815
816 fn typed_var_declaration_with_qualifier(
817 &mut self,
818 type_qualifier: Option<pine_ast::TypeQualifier>,
819 type_annotation: Option<String>,
820 var_kind: VarKind,
821 ) -> Result<Stmt, ParserError> {
822 let loc = self.cur_loc();
823 let name = self.expect_identifier()?;
824
825 let initializer = if self.match_token(&[TokenType::Assign]) {
826 Some(self.parse_indented_expression()?)
827 } else {
828 None
829 };
830
831 Ok(Stmt::VarDecl {
832 name,
833 type_qualifier,
834 type_annotation,
835 initializer,
836 var_kind,
837 loc,
838 })
839 }
840
841 fn statement(&mut self) -> Result<Stmt, ParserError> {
842 if self.match_token(&[TokenType::Export]) {
844 return self.export_statement();
845 }
846
847 if self.match_token(&[TokenType::Import]) {
849 return self.import_statement();
850 }
851
852 if self.match_token(&[TokenType::If]) {
854 return self.if_statement();
855 }
856
857 if self.match_token(&[TokenType::For]) {
859 return self.for_statement();
860 }
861
862 if self.match_token(&[TokenType::While]) {
864 return self.while_statement();
865 }
866
867 if self.match_token(&[TokenType::Break]) {
869 return Ok(Stmt::Break);
870 }
871
872 if self.match_token(&[TokenType::Continue]) {
874 return Ok(Stmt::Continue);
875 }
876
877 if self.check(&TokenType::LBracket) {
880 let tuple_loc = self.cur_loc();
881 if let Some((names, value)) = self.try_parse(|p| {
882 p.advance(); let mut names = vec![];
885
886 if !p.check(&TokenType::RBracket) {
888 loop {
889 if let TokenType::Ident(name) = &p.peek().typ {
890 names.push(name.clone());
891 p.advance();
892 } else {
893 return Err(ParserError::ExpectedVariableName(p.peek().line));
895 }
896
897 if !p.match_token(&[TokenType::Comma]) {
898 break;
899 }
900 }
901 }
902
903 p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
904 p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
905
906 p.skip_newlines();
908
909 let value = p.expression()?;
910
911 Ok((names, value))
912 }) {
913 return Ok(Stmt::TupleAssignment {
914 names,
915 value,
916 loc: tuple_loc,
917 });
918 }
919 }
920
921 if let TokenType::Ident(name) = &self.peek().typ {
926 let name = name.clone();
927 let name_loc = self.cur_loc();
928
929 if let Some((param_structs, body)) = self.try_parse(|p| {
931 p.advance(); p.consume(TokenType::LParen, "Expected '('")?;
933
934 let params = p.function_params()?;
935 p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
936 p.consume(TokenType::Arrow, "Expected '=>'")?;
937
938 p.match_token(&[TokenType::Newline]);
940
941 let body = p.parse_block()?;
943
944 Ok((params, body))
945 }) {
946 let initializer = Some(Expr::Function {
948 params: param_structs,
949 body,
950 });
951 return Ok(Stmt::VarDecl {
952 name,
953 type_qualifier: None,
954 type_annotation: None,
955 initializer,
956 var_kind: VarKind::Plain,
957 loc: name_loc,
958 });
959 }
960
961 if let Some(stmt) = self.try_parse(|p| {
963 p.advance(); if p.match_token(&[TokenType::Assign]) {
966 let initializer = Some(p.parse_indented_expression()?);
968
969 Ok(Stmt::VarDecl {
970 name: name.clone(),
971 type_qualifier: None,
972 type_annotation: None,
973 initializer,
974 var_kind: VarKind::Plain,
975 loc: name_loc,
976 })
977 } else if p.match_token(&[TokenType::ColonAssign]) {
978 let value = p.parse_indented_expression()?;
980
981 Ok(Stmt::Assignment {
982 target: Expr::Variable {
983 name: name.clone(),
984 loc: name_loc,
985 },
986 value,
987 })
988 } else if p.match_token(&[
989 TokenType::PlusAssign,
990 TokenType::MinusAssign,
991 TokenType::StarAssign,
992 TokenType::SlashAssign,
993 ]) {
994 let op_tok = &p.tokens[p.current - 1];
996 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
997 let op = op_tok
998 .typ
999 .to_binop()
1000 .expect("compound assign token should convert to binop");
1001
1002 let right = p.parse_indented_expression()?;
1003
1004 let value = Expr::Binary {
1005 left: Box::new(Expr::Variable {
1006 name: name.clone(),
1007 loc: name_loc,
1008 }),
1009 op,
1010 right: Box::new(right),
1011 loc: op_loc,
1012 };
1013 Ok(Stmt::Assignment {
1014 target: Expr::Variable {
1015 name: name.clone(),
1016 loc: name_loc,
1017 },
1018 value,
1019 })
1020 } else {
1021 Err(ParserError::UnexpectedToken(
1023 p.peek().typ.clone(),
1024 p.peek().line,
1025 ))
1026 }
1027 }) {
1028 return Ok(stmt);
1029 }
1030 }
1031
1032 self.expression_statement()
1033 }
1034
1035 fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
1036 self.parse_comma_separated(&TokenType::RParen, |p| {
1037 let type_qualifier = p.parse_optional_type_qualifier();
1039
1040 let type_annotation = p.parse_optional_type_annotation();
1042
1043 let param_loc = p.cur_loc();
1044 let name = p.expect_identifier()?;
1045
1046 let default_value = if p.match_token(&[TokenType::Assign]) {
1048 Some(p.expression()?)
1049 } else {
1050 None
1051 };
1052
1053 Ok(pine_ast::FunctionParam {
1054 type_qualifier,
1055 type_annotation,
1056 name,
1057 default_value,
1058 loc: param_loc,
1059 })
1060 })
1061 }
1062
1063 fn for_statement(&mut self) -> Result<Stmt, ParserError> {
1064 if self.check(&TokenType::LBracket) {
1066 self.advance(); let loc = self.cur_loc();
1069 let index_var = self.expect_identifier()?;
1070
1071 self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
1072
1073 let item_var = self.expect_identifier()?;
1074
1075 self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
1076 self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
1077
1078 let collection = self.expression()?;
1079
1080 self.match_token(&[TokenType::Newline]);
1082
1083 let body = self.parse_block()?;
1084
1085 return Ok(Stmt::ForIn {
1086 index_var: Some(index_var),
1087 item_var,
1088 collection,
1089 body,
1090 loc,
1091 });
1092 }
1093
1094 let loc = self.cur_loc();
1096 let var_name = self.expect_identifier()?;
1097
1098 if self.check(&TokenType::In) {
1100 self.advance(); let collection = self.expression()?;
1103
1104 self.match_token(&[TokenType::Newline]);
1106
1107 let body = self.parse_block()?;
1108
1109 Ok(Stmt::ForIn {
1110 index_var: None,
1111 item_var: var_name,
1112 collection,
1113 body,
1114 loc,
1115 })
1116 } else {
1117 self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1119 let from = self.expression()?;
1120 self.consume(TokenType::To, "Expected 'to' in for loop")?;
1121 let to = self.expression()?;
1122
1123 self.match_token(&[TokenType::Newline]);
1125
1126 let body = self.parse_block()?;
1128
1129 Ok(Stmt::For {
1130 var_name,
1131 from,
1132 to,
1133 body,
1134 loc,
1135 })
1136 }
1137 }
1138
1139 fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1140 let condition = self.expression()?;
1142
1143 self.match_token(&[TokenType::Newline]);
1145
1146 let body = self.parse_block()?;
1148
1149 Ok(Stmt::While { condition, body })
1150 }
1151
1152 fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1153 let condition = self.expression()?;
1155
1156 self.match_token(&[TokenType::Newline]);
1158
1159 let then_branch = self.parse_block()?;
1161
1162 let mut else_if_branches = Vec::new();
1164
1165 loop {
1166 self.skip_newlines();
1168
1169 if self.check(&TokenType::Else) {
1171 if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1172 p.advance(); if p.match_token(&[TokenType::If]) {
1175 let else_if_condition = p.expression()?;
1177 p.match_token(&[TokenType::Newline]);
1178 let else_if_body = p.parse_block()?;
1179 Ok((else_if_condition, else_if_body))
1180 } else {
1181 Err(ParserError::UnexpectedToken(
1182 p.peek().typ.clone(),
1183 p.peek().line,
1184 ))
1185 }
1186 }) {
1187 else_if_branches.push((else_if_condition, else_if_body));
1188 } else {
1189 break;
1190 }
1191 } else {
1192 break;
1193 }
1194 }
1195
1196 self.skip_newlines();
1198
1199 let else_branch = if self.match_token(&[TokenType::Else]) {
1200 self.match_token(&[TokenType::Newline]);
1202
1203 Some(self.parse_block()?)
1204 } else {
1205 None
1206 };
1207
1208 Ok(Stmt::If {
1209 condition,
1210 then_branch,
1211 else_if_branches,
1212 else_branch,
1213 })
1214 }
1215
1216 fn if_expression(&mut self) -> Result<Expr, ParserError> {
1217 self.consume(TokenType::If, "Expected 'if'")?;
1219
1220 let condition = self.expression()?;
1222
1223 self.match_token(&[TokenType::Newline]);
1225
1226 self.match_token(&[TokenType::Indent]);
1228
1229 let then_expr = self.expression()?;
1231
1232 self.skip_newlines();
1234 self.match_token(&[TokenType::Dedent]);
1235
1236 let mut else_if_branches = Vec::new();
1238
1239 loop {
1240 self.skip_newlines();
1242
1243 if self.check(&TokenType::Else) {
1245 if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1246 p.advance(); if p.match_token(&[TokenType::If]) {
1249 let else_if_condition = p.expression()?;
1251 p.match_token(&[TokenType::Newline]);
1252 p.match_token(&[TokenType::Indent]);
1253 let else_if_expr = p.expression()?;
1254 p.skip_newlines();
1255 p.match_token(&[TokenType::Dedent]);
1256 Ok((else_if_condition, else_if_expr))
1257 } else {
1258 Err(ParserError::UnexpectedToken(
1259 p.peek().typ.clone(),
1260 p.peek().line,
1261 ))
1262 }
1263 }) {
1264 else_if_branches.push((else_if_condition, else_if_expr));
1265 } else {
1266 break;
1267 }
1268 } else {
1269 break;
1270 }
1271 }
1272
1273 self.skip_newlines();
1275 let else_expr = if self.match_token(&[TokenType::Else]) {
1276 self.match_token(&[TokenType::Newline]);
1278
1279 self.match_token(&[TokenType::Indent]);
1281
1282 let expr = self.expression()?;
1284
1285 self.skip_newlines();
1287 self.match_token(&[TokenType::Dedent]);
1288
1289 Some(Box::new(expr))
1290 } else {
1291 None };
1293
1294 Ok(Expr::IfExpr {
1295 condition: Box::new(condition),
1296 then_expr: Box::new(then_expr),
1297 else_if_branches,
1298 else_expr,
1299 })
1300 }
1301
1302 fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1303 let mut stmts = vec![];
1304
1305 if !self.match_token(&[TokenType::Indent]) {
1307 if !self.check(&TokenType::Newline)
1310 && !self.check(&TokenType::Else)
1311 && !self.is_at_end()
1312 {
1313 stmts.push(self.declaration()?);
1314 }
1315 return Ok(stmts);
1316 }
1317
1318 loop {
1320 self.skip_newlines();
1322
1323 if self.check(&TokenType::Else) {
1325 break;
1326 }
1327
1328 if self.check(&TokenType::Dedent) {
1330 self.advance(); self.skip_newlines();
1334 if self.check(&TokenType::Else) {
1335 break;
1336 }
1337
1338 break;
1340 }
1341
1342 if self.is_at_end() {
1344 break;
1345 }
1346
1347 stmts.push(self.declaration()?);
1349 }
1350
1351 Ok(stmts)
1352 }
1353
1354 fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1355 let expr = self.expression()?;
1356
1357 if self.match_token(&[TokenType::ColonAssign]) {
1359 let value = self.parse_indented_expression()?;
1360 return Ok(Stmt::Assignment {
1361 target: expr,
1362 value,
1363 });
1364 }
1365
1366 Ok(Stmt::Expression(expr))
1367 }
1368
1369 fn expression(&mut self) -> Result<Expr, ParserError> {
1371 self.ternary()
1372 }
1373
1374 fn binary_left_assoc(
1376 &mut self,
1377 operators: &[TokenType],
1378 next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1379 ) -> Result<Expr, ParserError> {
1380 let mut expr = next_precedence(self)?;
1381
1382 loop {
1383 self.skip_newlines();
1385
1386 if !self.match_token(operators) {
1387 break;
1388 }
1389
1390 let op_tok = &self.tokens[self.current - 1];
1391 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1392 let op = op_tok
1393 .typ
1394 .to_binop()
1395 .expect("matched operator token should convert to binop");
1396
1397 self.skip_newlines_and_indent();
1399
1400 let right = next_precedence(self)?;
1401 expr = Expr::Binary {
1402 left: Box::new(expr),
1403 op,
1404 right: Box::new(right),
1405 loc: op_loc,
1406 };
1407 }
1408
1409 Ok(expr)
1410 }
1411
1412 fn ternary(&mut self) -> Result<Expr, ParserError> {
1413 if self.check(&TokenType::If) {
1415 return self.if_expression();
1416 }
1417
1418 let mut expr = self.logical_or()?;
1419
1420 self.skip_newlines();
1422
1423 self.try_consume_indent_if_followed_by(&TokenType::Question);
1425
1426 if self.match_token(&[TokenType::Question]) {
1427 self.skip_newlines_and_indent();
1429
1430 let then_expr = self.expression()?;
1431
1432 self.skip_newlines();
1434
1435 self.try_consume_indent_if_followed_by(&TokenType::Colon);
1437
1438 self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1439
1440 self.skip_newlines_and_indent();
1442
1443 let else_expr = self.expression()?;
1444 expr = Expr::Ternary {
1445 condition: Box::new(expr),
1446 then_expr: Box::new(then_expr),
1447 else_expr: Box::new(else_expr),
1448 };
1449 }
1450
1451 Ok(expr)
1452 }
1453
1454 fn logical_or(&mut self) -> Result<Expr, ParserError> {
1455 self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1456 }
1457
1458 fn logical_and(&mut self) -> Result<Expr, ParserError> {
1459 self.binary_left_assoc(&[TokenType::And], Self::equality)
1460 }
1461
1462 fn equality(&mut self) -> Result<Expr, ParserError> {
1463 self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1464 }
1465
1466 fn comparison(&mut self) -> Result<Expr, ParserError> {
1467 self.binary_left_assoc(
1468 &[
1469 TokenType::Greater,
1470 TokenType::Less,
1471 TokenType::GreaterEqual,
1472 TokenType::LessEqual,
1473 ],
1474 Self::term,
1475 )
1476 }
1477
1478 fn term(&mut self) -> Result<Expr, ParserError> {
1479 let mut expr = self.factor()?;
1480
1481 loop {
1482 self.skip_newlines();
1484
1485 self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1487
1488 if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
1489 break;
1490 }
1491
1492 let op_tok = &self.tokens[self.current - 1];
1493 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1494 let op = op_tok
1495 .typ
1496 .to_binop()
1497 .expect("term token should convert to binop");
1498 self.skip_newlines_and_indent();
1500 let right = self.factor()?;
1501 expr = Expr::Binary {
1502 left: Box::new(expr),
1503 op,
1504 right: Box::new(right),
1505 loc: op_loc,
1506 };
1507 }
1508
1509 Ok(expr)
1510 }
1511
1512 fn factor(&mut self) -> Result<Expr, ParserError> {
1513 self.binary_left_assoc(
1514 &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1515 Self::unary,
1516 )
1517 }
1518
1519 fn unary(&mut self) -> Result<Expr, ParserError> {
1520 if self.match_token(&[TokenType::Minus]) {
1521 let expr = self.unary()?;
1522 return Ok(Expr::Unary {
1523 op: UnOp::Neg,
1524 expr: Box::new(expr),
1525 });
1526 }
1527
1528 if self.match_token(&[TokenType::Not]) {
1529 let expr = self.unary()?;
1530 return Ok(Expr::Unary {
1531 op: UnOp::Not,
1532 expr: Box::new(expr),
1533 });
1534 }
1535
1536 self.postfix()
1537 }
1538
1539 fn postfix(&mut self) -> Result<Expr, ParserError> {
1540 let mut expr = self.primary()?;
1541
1542 if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1547 return Ok(expr);
1548 }
1549
1550 loop {
1551 if self.match_token(&[TokenType::Dot]) {
1552 let member = match &self.peek().typ {
1555 TokenType::Ident(name) => {
1556 let name = name.clone();
1557 self.advance();
1558 name
1559 }
1560 TokenType::Int => {
1561 self.advance();
1562 "int".to_string()
1563 }
1564 TokenType::Float => {
1565 self.advance();
1566 "float".to_string()
1567 }
1568 _ => {
1569 let lexeme = self.peek().lexeme.clone();
1571 if !lexeme.is_empty() {
1572 self.advance();
1573 lexeme
1574 } else {
1575 return Err(ParserError::ExpectedIdentifierAfterDot(self.peek().line));
1576 }
1577 }
1578 };
1579 let member_loc = self.prev_loc();
1580 expr = Expr::MemberAccess {
1581 object: Box::new(expr),
1582 member,
1583 member_loc,
1584 };
1585 } else if self.match_token(&[TokenType::LBracket]) {
1586 let index = self.expression()?;
1588 self.consume(TokenType::RBracket, "Expected ']'")?;
1589 expr = Expr::Index {
1590 expr: Box::new(expr),
1591 index: Box::new(index),
1592 };
1593 } else if self.check(&TokenType::Less) {
1594 let type_args = self.try_parse_type_args().unwrap_or_default();
1598
1599 if self.match_token(&[TokenType::LParen]) {
1601 let lparen = &self.tokens[self.current - 1];
1602 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1603 let id = self.next_call_id();
1604 let args = self.arguments()?;
1605 self.consume(TokenType::RParen, "Expected ')'")?;
1606 expr = Expr::Call {
1607 callee: Box::new(expr),
1608 type_args,
1609 args,
1610 id,
1611 loc: call_loc,
1612 };
1613 } else {
1614 break;
1616 }
1617 } else if self.match_token(&[TokenType::LParen]) {
1618 let lparen = &self.tokens[self.current - 1];
1620 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1621 let id = self.next_call_id();
1622 let args = self.arguments()?;
1623 self.consume(TokenType::RParen, "Expected ')'")?;
1624 expr = Expr::Call {
1625 callee: Box::new(expr),
1626 type_args: vec![],
1627 args,
1628 id,
1629 loc: call_loc,
1630 };
1631 } else {
1632 break;
1633 }
1634 }
1635
1636 Ok(expr)
1637 }
1638
1639 fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1640 let mut args = vec![];
1641
1642 if !self.check(&TokenType::RParen) {
1643 loop {
1644 let key_name = match &self.peek().typ {
1649 TokenType::Ident(name) => Some(name.clone()),
1650 TokenType::Type => Some("type".to_string()),
1651 _ => None,
1652 };
1653 if let Some(name) = key_name {
1654 if let Some((name, value)) = self.try_parse(|p| {
1655 p.advance(); if p.check(&TokenType::Assign) {
1657 p.advance(); let value = p.expression()?;
1660 Ok((name.clone(), value))
1661 } else {
1662 Err(ParserError::UnexpectedToken(
1663 p.peek().typ.clone(),
1664 p.peek().line,
1665 ))
1666 }
1667 }) {
1668 args.push(Argument::Named { name, value });
1669 } else {
1670 let expr = self.expression()?;
1672 args.push(Argument::Positional(expr));
1673 }
1674 } else {
1675 let expr = self.expression()?;
1676 args.push(Argument::Positional(expr));
1677 }
1678
1679 if !self.match_token(&[TokenType::Comma]) {
1680 break;
1681 }
1682 }
1683 }
1684
1685 Ok(args)
1686 }
1687
1688 fn primary(&mut self) -> Result<Expr, ParserError> {
1689 if let TokenType::IntLiteral(n) = self.peek().typ {
1690 self.advance();
1691 return Ok(Expr::Literal(Literal::Int(n)));
1692 }
1693
1694 if let TokenType::Number(n) = self.peek().typ {
1695 self.advance();
1696 return Ok(Expr::Literal(Literal::Number(n)));
1697 }
1698
1699 if let TokenType::String(ref s) = self.peek().typ {
1700 let s = s.clone();
1701 self.advance();
1702 return Ok(Expr::Literal(Literal::String(s)));
1703 }
1704
1705 if let TokenType::Bool(b) = self.peek().typ {
1706 self.advance();
1707 return Ok(Expr::Literal(Literal::Bool(b)));
1708 }
1709
1710 if let TokenType::HexColor(ref hex) = self.peek().typ {
1711 let hex = hex.clone();
1712 self.advance();
1713 return Ok(Expr::Literal(Literal::HexColor(hex)));
1714 }
1715
1716 if self.match_token(&[TokenType::Na]) {
1718 return Ok(Expr::Literal(Literal::Na));
1719 }
1720
1721 if self.match_token(&[TokenType::Int, TokenType::Float]) {
1724 let name = self.tokens[self.current - 1].lexeme.clone();
1725 return Ok(Expr::Variable {
1726 name,
1727 loc: self.prev_loc(),
1728 });
1729 }
1730
1731 if let TokenType::Ident(ref name) = self.peek().typ {
1732 let name = name.clone();
1733 let loc = self.cur_loc();
1734 self.advance();
1735 return Ok(Expr::Variable { name, loc });
1736 }
1737
1738 if self.match_token(&[TokenType::LParen]) {
1739 self.skip_newlines();
1741 let had_indent = self.match_token(&[TokenType::Indent]);
1742
1743 let expr = self.expression()?;
1744
1745 self.skip_newlines();
1747 if had_indent {
1748 self.match_token(&[TokenType::Dedent]);
1749 }
1750
1751 self.consume(TokenType::RParen, "Expected ')'")?;
1752 return Ok(expr);
1753 }
1754
1755 if self.match_token(&[TokenType::Switch]) {
1757 let value = Box::new(self.expression()?);
1758
1759 self.match_token(&[TokenType::Newline]);
1761
1762 let has_indent = self.match_token(&[TokenType::Indent]);
1764
1765 let mut cases = vec![];
1766
1767 loop {
1769 self.skip_newlines();
1771
1772 if self.check(&TokenType::Dedent) {
1774 if has_indent {
1775 self.advance(); }
1777 break;
1778 }
1779
1780 if self.is_at_end() {
1782 break;
1783 }
1784
1785 if self.match_token(&[TokenType::Arrow]) {
1787 self.skip_newlines();
1789
1790 let result = self.expression()?;
1792
1793 let default_pattern = Expr::Literal(Literal::Bool(true));
1795 cases.push((default_pattern, result));
1796 continue;
1797 }
1798
1799 if let Some((pattern, result)) = self.try_parse(|p| {
1801 let pattern = p.expression()?;
1803
1804 if !p.match_token(&[TokenType::Arrow]) {
1806 return Err(ParserError::UnexpectedToken(
1807 p.peek().typ.clone(),
1808 p.peek().line,
1809 ));
1810 }
1811
1812 p.skip_newlines();
1814
1815 let result = p.expression()?;
1817 Ok((pattern, result))
1818 }) {
1819 cases.push((pattern, result));
1820 } else {
1821 break;
1822 }
1823 }
1824
1825 return Ok(Expr::Switch { value, cases });
1826 }
1827
1828 if self.match_token(&[TokenType::LBracket]) {
1830 self.skip_newlines_and_indent();
1832
1833 let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1834
1835 self.skip_newlines_and_dedent();
1837
1838 self.consume(TokenType::RBracket, "Expected ']'")?;
1839 return Ok(Expr::Array(elements));
1840 }
1841
1842 Err(ParserError::UnexpectedToken(
1843 self.peek().typ.clone(),
1844 self.peek().line,
1845 ))
1846 }
1847}
1848
1849#[cfg(test)]
1850mod tests {
1851 use super::*;
1852 use pine_lexer::Lexer;
1853
1854 fn parse_expr(input: &str) -> eyre::Result<Expr> {
1855 let mut lexer = Lexer::new(input);
1856 let tokens = lexer.tokenize()?;
1857 let mut parser = Parser::new(tokens);
1858 let stmts = parser.parse()?;
1859
1860 if let Some(Stmt::Expression(expr)) = stmts.first() {
1861 Ok(expr.clone())
1862 } else {
1863 Err(eyre::eyre!("Expected expression statement".to_string()))
1864 }
1865 }
1866
1867 #[test]
1868 fn test_literals() {
1869 let expr = parse_expr("42").unwrap();
1871 assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1872
1873 let expr = parse_expr(r#""hello""#).unwrap();
1875 assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1876
1877 let expr = parse_expr("true").unwrap();
1879 assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1880 }
1881
1882 #[test]
1883 fn test_variables() {
1884 let expr = parse_expr("close").unwrap();
1885 assert_eq!(expr, Expr::var("close"));
1886
1887 let expr = parse_expr("my_var").unwrap();
1888 assert_eq!(expr, Expr::var("my_var"));
1889 }
1890
1891 #[test]
1892 fn test_historical_references() {
1893 let expr = parse_expr("close[1]").unwrap();
1895 assert!(matches!(expr, Expr::Index { .. }));
1896 if let Expr::Index { expr: base, index } = expr {
1897 assert_eq!(*base, Expr::var("close"));
1898 assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1899 }
1900
1901 let expr = parse_expr("high[5]").unwrap();
1903 if let Expr::Index { expr: base, index } = expr {
1904 assert_eq!(*base, Expr::var("high"));
1905 assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1906 }
1907 }
1908
1909 #[test]
1910 fn test_function_calls() {
1911 let expr = parse_expr("sma(close, 14)").unwrap();
1913 if let Expr::Call {
1914 callee,
1915 type_args,
1916 args,
1917 ..
1918 } = expr
1919 {
1920 assert_eq!(*callee, Expr::var("sma"));
1921 assert_eq!(type_args.len(), 0);
1922 assert_eq!(args.len(), 2);
1923 assert_eq!(args[0], Argument::Positional(Expr::var("close")));
1924 assert_eq!(
1925 args[1],
1926 Argument::Positional(Expr::Literal(Literal::Int(14)))
1927 );
1928 } else {
1929 panic!("Expected function call");
1930 }
1931
1932 let expr = parse_expr("foo()").unwrap();
1934 if let Expr::Call {
1935 callee,
1936 type_args,
1937 args,
1938 ..
1939 } = expr
1940 {
1941 assert_eq!(*callee, Expr::var("foo"));
1942 assert_eq!(type_args.len(), 0);
1943 assert_eq!(args.len(), 0);
1944 }
1945 }
1946
1947 #[test]
1948 fn test_arithmetic_expressions() {
1949 let expr = parse_expr("2 + 3").unwrap();
1951 if let Expr::Binary {
1952 left, op, right, ..
1953 } = expr
1954 {
1955 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1956 assert_eq!(op, BinOp::Add);
1957 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1958 }
1959
1960 let expr = parse_expr("2 + 3 * 4").unwrap();
1962 if let Expr::Binary {
1963 left,
1964 op: op1,
1965 right,
1966 ..
1967 } = expr
1968 {
1969 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1970 assert_eq!(op1, BinOp::Add);
1971 if let Expr::Binary {
1972 left: l2,
1973 op: op2,
1974 right: r2,
1975 ..
1976 } = *right
1977 {
1978 assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
1979 assert_eq!(op2, BinOp::Mul);
1980 assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
1981 }
1982 }
1983
1984 let expr = parse_expr("10 / 2").unwrap();
1986 if let Expr::Binary {
1987 left, op, right, ..
1988 } = expr
1989 {
1990 assert_eq!(*left, Expr::Literal(Literal::Int(10)));
1991 assert_eq!(op, BinOp::Div);
1992 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
1993 }
1994
1995 let expr = parse_expr("5 - 3").unwrap();
1997 if let Expr::Binary {
1998 left, op, right, ..
1999 } = expr
2000 {
2001 assert_eq!(*left, Expr::Literal(Literal::Int(5)));
2002 assert_eq!(op, BinOp::Sub);
2003 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
2004 }
2005 }
2006
2007 #[test]
2008 fn test_comparison_expressions() {
2009 let expr = parse_expr("close > open").unwrap();
2011 if let Expr::Binary {
2012 left, op, right, ..
2013 } = expr
2014 {
2015 assert_eq!(*left, Expr::var("close"));
2016 assert_eq!(op, BinOp::Greater);
2017 assert_eq!(*right, Expr::var("open"));
2018 }
2019
2020 let expr = parse_expr("rsi < 30").unwrap();
2022 if let Expr::Binary {
2023 left, op, right, ..
2024 } = expr
2025 {
2026 assert_eq!(*left, Expr::var("rsi"));
2027 assert_eq!(op, BinOp::Less);
2028 assert_eq!(*right, Expr::Literal(Literal::Int(30)));
2029 }
2030
2031 let expr = parse_expr("x == 5").unwrap();
2033 if let Expr::Binary {
2034 left, op, right, ..
2035 } = expr
2036 {
2037 assert_eq!(*left, Expr::var("x"));
2038 assert_eq!(op, BinOp::Eq);
2039 assert_eq!(*right, Expr::Literal(Literal::Int(5)));
2040 }
2041 }
2042
2043 #[test]
2044 fn test_unary_expressions() {
2045 let expr = parse_expr("-5").unwrap();
2047 if let Expr::Unary { op, expr } = expr {
2048 assert_eq!(op, UnOp::Neg);
2049 assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
2050 }
2051
2052 let expr = parse_expr("--10").unwrap();
2054 if let Expr::Unary { op: op1, expr: e1 } = expr {
2055 assert_eq!(op1, UnOp::Neg);
2056 if let Expr::Unary { op: op2, expr: e2 } = *e1 {
2057 assert_eq!(op2, UnOp::Neg);
2058 assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
2059 }
2060 }
2061 }
2062
2063 #[test]
2064 fn test_var_declarations() {
2065 let mut lexer = Lexer::new("var x = 10");
2066 let tokens = lexer.tokenize().unwrap();
2067 let mut parser = Parser::new(tokens);
2068 let stmts = parser.parse().unwrap();
2069
2070 assert_eq!(stmts.len(), 1);
2071 if let Stmt::VarDecl {
2072 name,
2073 type_qualifier,
2074 type_annotation,
2075 initializer,
2076 var_kind,
2077 ..
2078 } = &stmts[0]
2079 {
2080 assert_eq!(name, "x");
2081 assert_eq!(*type_qualifier, None);
2082 assert_eq!(*type_annotation, None);
2083 assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
2084 assert_eq!(
2085 initializer.as_ref().unwrap(),
2086 &Expr::Literal(Literal::Int(10))
2087 );
2088 } else {
2089 panic!("Expected VarDecl");
2090 }
2091
2092 let mut lexer = Lexer::new("var y");
2094 let tokens = lexer.tokenize().unwrap();
2095 let mut parser = Parser::new(tokens);
2096 let stmts = parser.parse().unwrap();
2097
2098 if let Stmt::VarDecl {
2099 name, initializer, ..
2100 } = &stmts[0]
2101 {
2102 assert_eq!(name, "y");
2103 assert!(initializer.is_none());
2104 }
2105 }
2106
2107 #[test]
2108 fn test_pinescript_examples() {
2109 let expr = parse_expr("close[1] > close[2]").unwrap();
2111 assert!(matches!(
2112 expr,
2113 Expr::Binary {
2114 op: BinOp::Greater,
2115 ..
2116 }
2117 ));
2118
2119 let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2121 if let Expr::Binary {
2122 left, op, right, ..
2123 } = expr
2124 {
2125 assert_eq!(op, BinOp::Greater);
2126 assert!(matches!(*left, Expr::Call { .. }));
2127 assert!(matches!(*right, Expr::Call { .. }));
2128 }
2129
2130 let expr = parse_expr("(high + low) / 2").unwrap();
2132 if let Expr::Binary {
2133 left,
2134 op: div_op,
2135 right,
2136 ..
2137 } = expr
2138 {
2139 assert_eq!(div_op, BinOp::Div);
2140 assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2141 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2142 }
2143 }
2144
2145 fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2147 walkdir::WalkDir::new(dir)
2148 .into_iter()
2149 .filter_map(|e| e.ok())
2150 .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2151 .map(|e| e.path().to_path_buf())
2152 .collect()
2153 }
2154
2155 #[test]
2156 fn test_parse_testdata_files() -> eyre::Result<()> {
2157 use std::fs;
2158 use std::path::Path;
2159
2160 let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2161
2162 let filter = std::env::var("TEST_FILE").ok();
2163 let debug = std::env::var("DEBUG").is_ok();
2164 let generate_ast = std::env::var("GENERATE_AST").is_ok();
2165
2166 let pine_files = collect_pine_files_recursive(&testdata_dir);
2167
2168 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2169 let content = fs::read_to_string(path)?;
2170
2171 let mut lexer = Lexer::new(&content);
2172 let tokens = lexer.tokenize()?;
2173
2174 if debug {
2175 println!("Tokens: {:#?}", tokens);
2176 }
2177
2178 let mut parser = Parser::new(tokens);
2179 let ast = parser.parse()?;
2180
2181 if debug {
2182 let ast_json = serde_json::to_string(&ast)?;
2183 println!("AST JSON: {:?}", ast_json);
2184 }
2185
2186 let json_path = path.with_file_name(format!(
2188 "{}_ast.json",
2189 path.file_stem().unwrap().to_str().unwrap()
2190 ));
2191
2192 if generate_ast {
2193 let json = serde_json::to_string_pretty(&ast)?;
2195 fs::write(&json_path, &json)?;
2196 } else if json_path.exists() {
2197 let expected_json = fs::read_to_string(&json_path)?;
2199 let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2200
2201 if ast != expected_ast {
2202 return Err(eyre::eyre!(
2203 "AST mismatch, expected AST from {:?}",
2204 json_path
2205 ));
2206 }
2207 }
2208
2209 Ok(())
2210 };
2211
2212 for path in pine_files {
2213 let filename = path.file_name().unwrap().to_str().unwrap();
2214
2215 if let Some(ref filter_name) = filter {
2217 if filename != filter_name {
2218 continue;
2219 }
2220 }
2221
2222 if let Err(e) = process_file(&path) {
2223 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2224 }
2225 }
2226
2227 Ok(())
2228 }
2229
2230 #[test]
2231 #[ignore]
2232 fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2233 use std::fs;
2234 use std::path::Path;
2235
2236 let testdata_dir =
2237 Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2238
2239 let filter = std::env::var("TEST_FILE").ok();
2240 let debug = std::env::var("DEBUG").is_ok();
2241
2242 let pine_files = collect_pine_files_recursive(&testdata_dir);
2243
2244 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2245 let content = fs::read_to_string(path)?;
2246
2247 let mut lexer = Lexer::new(&content);
2248 let tokens = lexer.tokenize()?;
2249
2250 if debug {
2251 println!("Tokens: {:#?}", tokens);
2252 }
2253
2254 let mut parser = Parser::new(tokens);
2255 let ast = parser.parse()?;
2256
2257 let ast_json = serde_json::to_string(&ast)?;
2258
2259 if debug {
2260 println!("AST JSON: {:?}", ast_json);
2261 }
2262
2263 Ok(())
2264 };
2265
2266 for path in pine_files {
2267 let filename = path.file_name().unwrap().to_str().unwrap();
2268
2269 if let Some(ref filter_name) = filter {
2271 if filename != filter_name {
2272 continue;
2273 }
2274 }
2275
2276 if let Err(e) = process_file(&path) {
2277 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2278 }
2279 }
2280
2281 Ok(())
2282 }
2283}