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
30impl From<ParserError> for String {
31 fn from(err: ParserError) -> String {
32 err.to_string()
33 }
34}
35
36trait TokenTypeExt {
38 fn to_binop(&self) -> Option<BinOp>;
39}
40
41impl TokenTypeExt for TokenType {
42 fn to_binop(&self) -> Option<BinOp> {
44 match self {
45 TokenType::Plus => Some(BinOp::Add),
46 TokenType::Minus => Some(BinOp::Sub),
47 TokenType::Star => Some(BinOp::Mul),
48 TokenType::Slash => Some(BinOp::Div),
49 TokenType::Percent => Some(BinOp::Mod),
50 TokenType::Equal => Some(BinOp::Eq),
51 TokenType::NotEqual => Some(BinOp::NotEq),
52 TokenType::Less => Some(BinOp::Less),
53 TokenType::Greater => Some(BinOp::Greater),
54 TokenType::LessEqual => Some(BinOp::LessEq),
55 TokenType::GreaterEqual => Some(BinOp::GreaterEq),
56 TokenType::And => Some(BinOp::And),
57 TokenType::Or => Some(BinOp::Or),
58 TokenType::PlusAssign => Some(BinOp::Add),
59 TokenType::MinusAssign => Some(BinOp::Sub),
60 TokenType::StarAssign => Some(BinOp::Mul),
61 TokenType::SlashAssign => Some(BinOp::Div),
62 _ => None,
63 }
64 }
65}
66
67pub struct Parser {
68 tokens: Vec<Token>,
69 current: usize,
70 next_call_id: u32,
71}
72
73impl Parser {
74 pub fn new(tokens: Vec<Token>) -> Self {
75 Self {
76 tokens,
77 current: 0,
78 next_call_id: 1,
79 }
80 }
81
82 fn next_call_id(&mut self) -> u32 {
83 let id = self.next_call_id;
84 self.next_call_id += 1;
85 id
86 }
87
88 fn peek(&self) -> &Token {
89 &self.tokens[self.current]
90 }
91
92 fn is_at_end(&self) -> bool {
93 matches!(self.peek().typ, TokenType::Eof)
94 }
95
96 fn advance(&mut self) -> &Token {
97 if !self.is_at_end() {
98 self.current += 1;
99 }
100 &self.tokens[self.current - 1]
101 }
102
103 fn check(&self, typ: &TokenType) -> bool {
104 !self.is_at_end() && &self.peek().typ == typ
105 }
106
107 fn match_token(&mut self, types: &[TokenType]) -> bool {
108 for typ in types {
109 if self.check(typ) {
110 self.advance();
111 return true;
112 }
113 }
114 false
115 }
116
117 fn try_parse<T, F>(&mut self, f: F) -> Option<T>
120 where
121 F: FnOnce(&mut Self) -> Result<T, ParserError>,
122 {
123 let saved_pos = self.current;
124 match f(self) {
125 Ok(val) => Some(val),
126 Err(_) => {
127 self.current = saved_pos;
128 None
129 }
130 }
131 }
132
133 fn try_parse_type_args(&mut self) -> Option<Vec<String>> {
136 self.try_parse(|p| {
137 p.consume(TokenType::Less, "Expected '<'")?;
138
139 let mut type_args = vec![];
140
141 loop {
142 let type_name = match &p.peek().typ {
144 TokenType::Ident(name) => name.clone(),
145 TokenType::Int => "int".to_string(),
146 TokenType::Float => "float".to_string(),
147 _ => {
148 return Err(ParserError::UnexpectedToken(
149 p.peek().typ.clone(),
150 p.peek().line,
151 ))
152 }
153 };
154 p.advance();
155 type_args.push(type_name);
156
157 if p.match_token(&[TokenType::Comma]) {
159 continue;
160 } else if p.match_token(&[TokenType::Greater]) {
161 break;
162 } else {
163 return Err(ParserError::UnexpectedToken(
164 p.peek().typ.clone(),
165 p.peek().line,
166 ));
167 }
168 }
169
170 Ok(type_args)
171 })
172 }
173
174 fn skip_newlines(&mut self) {
176 while self.match_token(&[TokenType::Newline]) {}
177 }
178
179 fn skip_whitespace(&mut self) {
181 while self.match_token(&[TokenType::Newline, TokenType::Indent, TokenType::Dedent]) {}
182 }
183
184 fn parse_array_suffix(&mut self, type_name: String) -> Result<String, ParserError> {
186 if self.match_token(&[TokenType::LBracket]) {
187 self.consume(TokenType::RBracket, "Expected ']' after '[' in array type")?;
188 Ok(format!("{}[]", type_name))
189 } else {
190 Ok(type_name)
191 }
192 }
193
194 fn parse_indented_expression(&mut self) -> Result<Expr, ParserError> {
197 self.skip_newlines();
198
199 let has_indent = self.match_token(&[TokenType::Indent]);
201
202 let expr = self.expression()?;
203
204 if has_indent {
206 self.match_token(&[TokenType::Dedent]);
207 }
208
209 Ok(expr)
210 }
211
212 fn consume(&mut self, typ: TokenType, message: &str) -> Result<&Token, ParserError> {
213 if self.check(&typ) {
214 Ok(self.advance())
215 } else {
216 Err(ParserError::ExpectedToken {
217 expected: message.to_string(),
218 found: self.peek().typ.clone(),
219 line: self.peek().line,
220 })
221 }
222 }
223
224 fn expect_identifier(&mut self) -> Result<String, ParserError> {
226 if let TokenType::Ident(name) = &self.peek().typ {
227 let name = name.clone();
228 self.advance();
229 Ok(name)
230 } else {
231 Err(ParserError::ExpectedVariableName(self.peek().line))
232 }
233 }
234
235 fn parse_indented_fields<T, F>(&mut self, parse_field: F) -> Result<Vec<T>, ParserError>
237 where
238 F: Fn(&mut Self) -> Result<T, ParserError>,
239 {
240 let mut fields = Vec::new();
241
242 loop {
243 self.skip_newlines();
245
246 if self.check(&TokenType::Dedent) {
248 self.advance();
249 break;
250 }
251
252 if self.is_at_end() {
254 break;
255 }
256
257 fields.push(parse_field(self)?);
259 }
260
261 Ok(fields)
262 }
263
264 fn skip_newlines_and_indent(&mut self) {
266 self.skip_newlines();
267 self.match_token(&[TokenType::Indent]);
268 }
269
270 fn skip_newlines_and_dedent(&mut self) {
272 self.skip_newlines();
273 self.match_token(&[TokenType::Dedent]);
274 }
275
276 fn try_consume_indent_if_followed_by(&mut self, expected: &TokenType) {
278 if self.check(&TokenType::Indent) {
279 self.try_parse(|p| {
280 p.advance(); if p.check(expected) {
282 Ok(())
283 } else {
284 Err(ParserError::UnexpectedToken(
285 p.peek().typ.clone(),
286 p.peek().line,
287 ))
288 }
289 });
290 }
291 }
292
293 fn try_consume_layout_token_if_followed_by(&mut self, expected: &[TokenType]) {
295 if self.check(&TokenType::Indent) || self.check(&TokenType::Dedent) {
296 self.try_parse(|p| {
297 p.advance(); for typ in expected {
299 if p.check(typ) {
300 return Ok(());
301 }
302 }
303 Err(ParserError::UnexpectedToken(
304 p.peek().typ.clone(),
305 p.peek().line,
306 ))
307 });
308 }
309 }
310
311 fn parse_optional_type_qualifier(&mut self) -> Option<pine_ast::TypeQualifier> {
313 use pine_ast::TypeQualifier;
314 if self.match_token(&[TokenType::Const]) {
315 Some(TypeQualifier::Const)
316 } else if let TokenType::Ident(name) = &self.peek().typ {
317 match name.as_str() {
318 "input" => {
319 self.advance();
320 Some(TypeQualifier::Input)
321 }
322 "simple" => {
323 self.advance();
324 Some(TypeQualifier::Simple)
325 }
326 "series" => {
327 self.advance();
328 Some(TypeQualifier::Series)
329 }
330 _ => None,
331 }
332 } else {
333 None
334 }
335 }
336
337 fn parse_optional_type_annotation(&mut self) -> Option<String> {
341 if self.match_token(&[TokenType::Int, TokenType::Float]) {
342 let type_name = self.tokens[self.current - 1].lexeme.clone();
343 self.parse_array_suffix(type_name).ok()
345 } else if let TokenType::Ident(type_name) = &self.peek().typ {
346 let type_name = type_name.clone();
347 self.try_parse(|p| {
348 p.advance(); let final_type = p.parse_array_suffix(type_name.clone())?;
352
353 if !matches!(p.peek().typ, TokenType::Ident(_)) {
355 return Err(ParserError::ExpectedVariableName(p.peek().line));
356 }
357
358 Ok(final_type)
359 })
360 } else {
361 None
362 }
363 }
364
365 fn parse_comma_separated<T, F>(
368 &mut self,
369 closing_delimiter: &TokenType,
370 parse_item: F,
371 ) -> Result<Vec<T>, ParserError>
372 where
373 F: Fn(&mut Self) -> Result<T, ParserError>,
374 {
375 let mut items = vec![];
376
377 if !self.check(closing_delimiter) {
378 loop {
379 items.push(parse_item(self)?);
380
381 self.skip_newlines();
383
384 if !self.match_token(&[TokenType::Comma]) {
385 break;
386 }
387
388 self.skip_newlines_and_indent();
390 }
391 }
392
393 Ok(items)
394 }
395
396 pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
398 let mut statements = vec![];
399
400 while !self.is_at_end() {
401 self.skip_whitespace();
403
404 if self.is_at_end() {
406 break;
407 }
408
409 statements.push(self.declaration()?);
410 }
411
412 Ok(statements)
413 }
414
415 fn declaration(&mut self) -> Result<Stmt, ParserError> {
417 let type_qualifier = self.parse_optional_type_qualifier();
419
420 let var_kind = if self.match_token(&[TokenType::Varip]) {
422 VarKind::Varip
423 } else if self.match_token(&[TokenType::Var]) {
424 VarKind::Var
425 } else if type_qualifier.is_some() {
426 VarKind::Plain
429 } else {
430 return self.check_type_annotated_declaration();
432 };
433
434 let type_annotation = self.parse_optional_type_annotation();
436 self.typed_var_declaration_with_qualifier(type_qualifier, type_annotation, var_kind)
437 }
438
439 fn check_type_annotated_declaration(&mut self) -> Result<Stmt, ParserError> {
440 if self.match_token(&[TokenType::Type]) {
442 return self.type_declaration(false);
443 }
444
445 if self.match_token(&[TokenType::Enum]) {
447 return self.enum_declaration(false);
448 }
449
450 if self.match_token(&[TokenType::Method]) {
452 return self.method_declaration(false);
453 }
454
455 if self.match_token(&[TokenType::Int, TokenType::Float]) {
457 let type_name = self.tokens[self.current - 1].lexeme.clone();
458 let type_name = self.parse_array_suffix(type_name)?;
460 return self.typed_var_declaration(Some(type_name), VarKind::Plain);
461 }
462
463 if let Some(type_annotation) = self.parse_optional_type_annotation() {
465 return self.typed_var_declaration(Some(type_annotation), VarKind::Plain);
466 }
467
468 self.statement()
469 }
470
471 fn type_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
472 let type_name = self.expect_identifier()?;
474
475 self.consume(TokenType::Newline, "Expected newline after type name")?;
477
478 self.consume(TokenType::Indent, "Expected indent for type fields")?;
480
481 let fields = self.parse_indented_fields(|p| {
483 let type_qualifier = p.parse_optional_type_qualifier();
485
486 let field_type = if p.match_token(&[TokenType::Int, TokenType::Float]) {
489 p.tokens[p.current - 1].lexeme.clone()
490 } else if let TokenType::Ident(type_name) = &p.peek().typ {
491 let type_name = type_name.clone();
492 p.advance();
493 type_name
494 } else {
495 return Err(ParserError::UnexpectedToken(
496 p.peek().typ.clone(),
497 p.peek().line,
498 ));
499 };
500
501 let field_name = p.expect_identifier()?;
503
504 let default_value = if p.match_token(&[TokenType::Assign]) {
506 Some(p.expression()?)
507 } else {
508 None
509 };
510
511 Ok(pine_ast::TypeField {
512 name: field_name,
513 type_qualifier,
514 type_annotation: field_type,
515 default_value,
516 })
517 })?;
518
519 Ok(Stmt::TypeDecl {
520 name: type_name,
521 fields,
522 export,
523 })
524 }
525
526 fn enum_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
527 let enum_name = self.expect_identifier()?;
529
530 self.consume(TokenType::Newline, "Expected newline after enum name")?;
532
533 self.consume(TokenType::Indent, "Expected indent for enum fields")?;
535
536 let fields = self.parse_indented_fields(|p| {
538 let field_name = p.expect_identifier()?;
540
541 let title = if p.match_token(&[TokenType::Assign]) {
543 if let TokenType::String(s) = &p.peek().typ {
545 let s = s.clone();
546 p.advance();
547 Some(s)
548 } else {
549 return Err(ParserError::UnexpectedToken(
550 p.peek().typ.clone(),
551 p.peek().line,
552 ));
553 }
554 } else {
555 None
556 };
557
558 Ok(pine_ast::EnumField {
559 name: field_name,
560 title,
561 })
562 })?;
563
564 Ok(Stmt::EnumDecl {
565 name: enum_name,
566 fields,
567 export,
568 })
569 }
570
571 fn export_statement(&mut self) -> Result<Stmt, ParserError> {
572 if self.match_token(&[TokenType::Type]) {
574 return self.type_declaration(true);
575 }
576
577 if self.match_token(&[TokenType::Enum]) {
579 return self.enum_declaration(true);
580 }
581
582 let is_method = self.match_token(&[TokenType::Method]);
585
586 if is_method {
587 return self.method_declaration(true);
588 }
589
590 let func_name = self.expect_identifier()?;
592
593 if self.check(&TokenType::LParen) {
595 self.advance(); let params = self.function_params()?;
599 self.consume(TokenType::RParen, "Expected ')' after function parameters")?;
600 self.consume(TokenType::Arrow, "Expected '=>'")?;
601
602 self.match_token(&[TokenType::Newline]);
604
605 let body = self.parse_block()?;
607
608 Ok(Stmt::FunctionDecl {
609 name: func_name,
610 params,
611 body,
612 export: true,
613 })
614 } else {
615 Ok(Stmt::Export {
617 item: pine_ast::ExportItem::Type(func_name),
618 })
619 }
620 }
621
622 fn import_statement(&mut self) -> Result<Stmt, ParserError> {
623 let path = if let TokenType::Ident(p) = &self.peek().typ {
625 let mut path_parts = vec![p.clone()];
626 self.advance();
627
628 while self.match_token(&[TokenType::Slash]) {
630 if let TokenType::Ident(part) = &self.peek().typ {
631 path_parts.push(part.clone());
632 self.advance();
633 } else if let TokenType::IntLiteral(n) = &self.peek().typ {
634 path_parts.push(n.to_string());
636 self.advance();
637 } else {
638 return Err(ParserError::UnexpectedToken(
639 self.peek().typ.clone(),
640 self.peek().line,
641 ));
642 }
643 }
644
645 path_parts.join("/")
646 } else {
647 return Err(ParserError::ExpectedVariableName(self.peek().line));
648 };
649
650 if let TokenType::Ident(kw) = &self.peek().typ {
652 if kw != "as" {
653 return Err(ParserError::UnexpectedToken(
654 self.peek().typ.clone(),
655 self.peek().line,
656 ));
657 }
658 self.advance();
659 } else {
660 return Err(ParserError::UnexpectedToken(
661 self.peek().typ.clone(),
662 self.peek().line,
663 ));
664 }
665
666 let alias = self.expect_identifier()?;
668
669 Ok(Stmt::Import { path, alias })
670 }
671
672 fn method_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
673 let method_name = self.expect_identifier()?;
675
676 self.consume(TokenType::LParen, "Expected '(' after method name")?;
678
679 let mut params = Vec::new();
681
682 if !self.check(&TokenType::RParen) {
683 loop {
684 let type_qualifier = self.parse_optional_type_qualifier();
686
687 let type_annotation = self.parse_optional_type_annotation();
689
690 let param_name = self.expect_identifier()?;
692
693 let default_value = if self.match_token(&[TokenType::Assign]) {
695 Some(self.expression()?)
696 } else {
697 None
698 };
699
700 params.push(pine_ast::MethodParam {
701 type_qualifier,
702 type_annotation,
703 name: param_name,
704 default_value,
705 });
706
707 if !self.match_token(&[TokenType::Comma]) {
708 break;
709 }
710 }
711 }
712
713 self.consume(TokenType::RParen, "Expected ')' after parameters")?;
714
715 self.consume(TokenType::Arrow, "Expected '=>' after method parameters")?;
717
718 self.match_token(&[TokenType::Newline]);
720
721 let body = self.parse_block()?;
723
724 Ok(Stmt::MethodDecl {
725 name: method_name,
726 params,
727 body,
728 export,
729 })
730 }
731
732 fn typed_var_declaration(
733 &mut self,
734 type_annotation: Option<String>,
735 var_kind: VarKind,
736 ) -> Result<Stmt, ParserError> {
737 self.typed_var_declaration_with_qualifier(None, type_annotation, var_kind)
738 }
739
740 fn typed_var_declaration_with_qualifier(
741 &mut self,
742 type_qualifier: Option<pine_ast::TypeQualifier>,
743 type_annotation: Option<String>,
744 var_kind: VarKind,
745 ) -> Result<Stmt, ParserError> {
746 let name = self.expect_identifier()?;
747
748 let initializer = if self.match_token(&[TokenType::Assign]) {
749 Some(self.parse_indented_expression()?)
750 } else {
751 None
752 };
753
754 Ok(Stmt::VarDecl {
755 name,
756 type_qualifier,
757 type_annotation,
758 initializer,
759 var_kind,
760 })
761 }
762
763 fn statement(&mut self) -> Result<Stmt, ParserError> {
764 if self.match_token(&[TokenType::Export]) {
766 return self.export_statement();
767 }
768
769 if self.match_token(&[TokenType::Import]) {
771 return self.import_statement();
772 }
773
774 if self.match_token(&[TokenType::If]) {
776 return self.if_statement();
777 }
778
779 if self.match_token(&[TokenType::For]) {
781 return self.for_statement();
782 }
783
784 if self.match_token(&[TokenType::While]) {
786 return self.while_statement();
787 }
788
789 if self.match_token(&[TokenType::Break]) {
791 return Ok(Stmt::Break);
792 }
793
794 if self.match_token(&[TokenType::Continue]) {
796 return Ok(Stmt::Continue);
797 }
798
799 if self.check(&TokenType::LBracket) {
802 if let Some((names, value)) = self.try_parse(|p| {
803 p.advance(); let mut names = vec![];
806
807 if !p.check(&TokenType::RBracket) {
809 loop {
810 if let TokenType::Ident(name) = &p.peek().typ {
811 names.push(name.clone());
812 p.advance();
813 } else {
814 return Err(ParserError::ExpectedVariableName(p.peek().line));
816 }
817
818 if !p.match_token(&[TokenType::Comma]) {
819 break;
820 }
821 }
822 }
823
824 p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
825 p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
826
827 p.skip_newlines();
829
830 let value = p.expression()?;
831
832 Ok((names, value))
833 }) {
834 return Ok(Stmt::TupleAssignment { names, value });
835 }
836 }
837
838 if let TokenType::Ident(name) = &self.peek().typ {
843 let name = name.clone();
844
845 if let Some((param_structs, body)) = self.try_parse(|p| {
847 p.advance(); p.consume(TokenType::LParen, "Expected '('")?;
849
850 let params = p.function_params()?;
851 p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
852 p.consume(TokenType::Arrow, "Expected '=>'")?;
853
854 p.match_token(&[TokenType::Newline]);
856
857 let body = p.parse_block()?;
859
860 Ok((params, body))
861 }) {
862 let initializer = Some(Expr::Function {
864 params: param_structs,
865 body,
866 });
867 return Ok(Stmt::VarDecl {
868 name,
869 type_qualifier: None,
870 type_annotation: None,
871 initializer,
872 var_kind: VarKind::Plain,
873 });
874 }
875
876 if let Some(stmt) = self.try_parse(|p| {
878 p.advance(); if p.match_token(&[TokenType::Assign]) {
881 let initializer = Some(p.parse_indented_expression()?);
883
884 Ok(Stmt::VarDecl {
885 name: name.clone(),
886 type_qualifier: None,
887 type_annotation: None,
888 initializer,
889 var_kind: VarKind::Plain,
890 })
891 } else if p.match_token(&[TokenType::ColonAssign]) {
892 let value = p.parse_indented_expression()?;
894
895 Ok(Stmt::Assignment {
896 target: Expr::Variable(name.clone()),
897 value,
898 })
899 } else if p.match_token(&[
900 TokenType::PlusAssign,
901 TokenType::MinusAssign,
902 TokenType::StarAssign,
903 TokenType::SlashAssign,
904 ]) {
905 let op_tok = &p.tokens[p.current - 1];
907 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
908 let op = op_tok
909 .typ
910 .to_binop()
911 .expect("compound assign token should convert to binop");
912
913 let right = p.parse_indented_expression()?;
914
915 let value = Expr::Binary {
916 left: Box::new(Expr::Variable(name.clone())),
917 op,
918 right: Box::new(right),
919 loc: op_loc,
920 };
921 Ok(Stmt::Assignment {
922 target: Expr::Variable(name.clone()),
923 value,
924 })
925 } else {
926 Err(ParserError::UnexpectedToken(
928 p.peek().typ.clone(),
929 p.peek().line,
930 ))
931 }
932 }) {
933 return Ok(stmt);
934 }
935 }
936
937 self.expression_statement()
938 }
939
940 fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
941 self.parse_comma_separated(&TokenType::RParen, |p| {
942 let type_qualifier = p.parse_optional_type_qualifier();
944
945 let type_annotation = p.parse_optional_type_annotation();
947
948 let name = p.expect_identifier()?;
949
950 let default_value = if p.match_token(&[TokenType::Assign]) {
952 Some(p.expression()?)
953 } else {
954 None
955 };
956
957 Ok(pine_ast::FunctionParam {
958 type_qualifier,
959 type_annotation,
960 name,
961 default_value,
962 })
963 })
964 }
965
966 fn for_statement(&mut self) -> Result<Stmt, ParserError> {
967 if self.check(&TokenType::LBracket) {
969 self.advance(); let index_var = self.expect_identifier()?;
972
973 self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
974
975 let item_var = self.expect_identifier()?;
976
977 self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
978 self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
979
980 let collection = self.expression()?;
981
982 self.match_token(&[TokenType::Newline]);
984
985 let body = self.parse_block()?;
986
987 return Ok(Stmt::ForIn {
988 index_var: Some(index_var),
989 item_var,
990 collection,
991 body,
992 });
993 }
994
995 let var_name = self.expect_identifier()?;
997
998 if self.check(&TokenType::In) {
1000 self.advance(); let collection = self.expression()?;
1003
1004 self.match_token(&[TokenType::Newline]);
1006
1007 let body = self.parse_block()?;
1008
1009 Ok(Stmt::ForIn {
1010 index_var: None,
1011 item_var: var_name,
1012 collection,
1013 body,
1014 })
1015 } else {
1016 self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1018 let from = self.expression()?;
1019 self.consume(TokenType::To, "Expected 'to' in for loop")?;
1020 let to = self.expression()?;
1021
1022 self.match_token(&[TokenType::Newline]);
1024
1025 let body = self.parse_block()?;
1027
1028 Ok(Stmt::For {
1029 var_name,
1030 from,
1031 to,
1032 body,
1033 })
1034 }
1035 }
1036
1037 fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1038 let condition = self.expression()?;
1040
1041 self.match_token(&[TokenType::Newline]);
1043
1044 let body = self.parse_block()?;
1046
1047 Ok(Stmt::While { condition, body })
1048 }
1049
1050 fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1051 let condition = self.expression()?;
1053
1054 self.match_token(&[TokenType::Newline]);
1056
1057 let then_branch = self.parse_block()?;
1059
1060 let mut else_if_branches = Vec::new();
1062
1063 loop {
1064 self.skip_newlines();
1066
1067 if self.check(&TokenType::Else) {
1069 if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1070 p.advance(); if p.match_token(&[TokenType::If]) {
1073 let else_if_condition = p.expression()?;
1075 p.match_token(&[TokenType::Newline]);
1076 let else_if_body = p.parse_block()?;
1077 Ok((else_if_condition, else_if_body))
1078 } else {
1079 Err(ParserError::UnexpectedToken(
1080 p.peek().typ.clone(),
1081 p.peek().line,
1082 ))
1083 }
1084 }) {
1085 else_if_branches.push((else_if_condition, else_if_body));
1086 } else {
1087 break;
1088 }
1089 } else {
1090 break;
1091 }
1092 }
1093
1094 self.skip_newlines();
1096
1097 let else_branch = if self.match_token(&[TokenType::Else]) {
1098 self.match_token(&[TokenType::Newline]);
1100
1101 Some(self.parse_block()?)
1102 } else {
1103 None
1104 };
1105
1106 Ok(Stmt::If {
1107 condition,
1108 then_branch,
1109 else_if_branches,
1110 else_branch,
1111 })
1112 }
1113
1114 fn if_expression(&mut self) -> Result<Expr, ParserError> {
1115 self.consume(TokenType::If, "Expected 'if'")?;
1117
1118 let condition = self.expression()?;
1120
1121 self.match_token(&[TokenType::Newline]);
1123
1124 self.match_token(&[TokenType::Indent]);
1126
1127 let then_expr = self.expression()?;
1129
1130 self.skip_newlines();
1132 self.match_token(&[TokenType::Dedent]);
1133
1134 let mut else_if_branches = Vec::new();
1136
1137 loop {
1138 self.skip_newlines();
1140
1141 if self.check(&TokenType::Else) {
1143 if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1144 p.advance(); if p.match_token(&[TokenType::If]) {
1147 let else_if_condition = p.expression()?;
1149 p.match_token(&[TokenType::Newline]);
1150 p.match_token(&[TokenType::Indent]);
1151 let else_if_expr = p.expression()?;
1152 p.skip_newlines();
1153 p.match_token(&[TokenType::Dedent]);
1154 Ok((else_if_condition, else_if_expr))
1155 } else {
1156 Err(ParserError::UnexpectedToken(
1157 p.peek().typ.clone(),
1158 p.peek().line,
1159 ))
1160 }
1161 }) {
1162 else_if_branches.push((else_if_condition, else_if_expr));
1163 } else {
1164 break;
1165 }
1166 } else {
1167 break;
1168 }
1169 }
1170
1171 self.skip_newlines();
1173 let else_expr = if self.match_token(&[TokenType::Else]) {
1174 self.match_token(&[TokenType::Newline]);
1176
1177 self.match_token(&[TokenType::Indent]);
1179
1180 let expr = self.expression()?;
1182
1183 self.skip_newlines();
1185 self.match_token(&[TokenType::Dedent]);
1186
1187 Some(Box::new(expr))
1188 } else {
1189 None };
1191
1192 Ok(Expr::IfExpr {
1193 condition: Box::new(condition),
1194 then_expr: Box::new(then_expr),
1195 else_if_branches,
1196 else_expr,
1197 })
1198 }
1199
1200 fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1201 let mut stmts = vec![];
1202
1203 if !self.match_token(&[TokenType::Indent]) {
1205 if !self.check(&TokenType::Newline)
1208 && !self.check(&TokenType::Else)
1209 && !self.is_at_end()
1210 {
1211 stmts.push(self.declaration()?);
1212 }
1213 return Ok(stmts);
1214 }
1215
1216 loop {
1218 self.skip_newlines();
1220
1221 if self.check(&TokenType::Else) {
1223 break;
1224 }
1225
1226 if self.check(&TokenType::Dedent) {
1228 self.advance(); self.skip_newlines();
1232 if self.check(&TokenType::Else) {
1233 break;
1234 }
1235
1236 break;
1238 }
1239
1240 if self.is_at_end() {
1242 break;
1243 }
1244
1245 stmts.push(self.declaration()?);
1247 }
1248
1249 Ok(stmts)
1250 }
1251
1252 fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1253 let expr = self.expression()?;
1254
1255 if self.match_token(&[TokenType::ColonAssign]) {
1257 let value = self.parse_indented_expression()?;
1258 return Ok(Stmt::Assignment {
1259 target: expr,
1260 value,
1261 });
1262 }
1263
1264 Ok(Stmt::Expression(expr))
1265 }
1266
1267 fn expression(&mut self) -> Result<Expr, ParserError> {
1269 self.ternary()
1270 }
1271
1272 fn binary_left_assoc(
1274 &mut self,
1275 operators: &[TokenType],
1276 next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1277 ) -> Result<Expr, ParserError> {
1278 let mut expr = next_precedence(self)?;
1279
1280 loop {
1281 self.skip_newlines();
1283
1284 if !self.match_token(operators) {
1285 break;
1286 }
1287
1288 let op_tok = &self.tokens[self.current - 1];
1289 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1290 let op = op_tok
1291 .typ
1292 .to_binop()
1293 .expect("matched operator token should convert to binop");
1294
1295 self.skip_newlines_and_indent();
1297
1298 let right = next_precedence(self)?;
1299 expr = Expr::Binary {
1300 left: Box::new(expr),
1301 op,
1302 right: Box::new(right),
1303 loc: op_loc,
1304 };
1305 }
1306
1307 Ok(expr)
1308 }
1309
1310 fn ternary(&mut self) -> Result<Expr, ParserError> {
1311 if self.check(&TokenType::If) {
1313 return self.if_expression();
1314 }
1315
1316 let mut expr = self.logical_or()?;
1317
1318 self.skip_newlines();
1320
1321 self.try_consume_indent_if_followed_by(&TokenType::Question);
1323
1324 if self.match_token(&[TokenType::Question]) {
1325 self.skip_newlines_and_indent();
1327
1328 let then_expr = self.expression()?;
1329
1330 self.skip_newlines();
1332
1333 self.try_consume_indent_if_followed_by(&TokenType::Colon);
1335
1336 self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1337
1338 self.skip_newlines_and_indent();
1340
1341 let else_expr = self.expression()?;
1342 expr = Expr::Ternary {
1343 condition: Box::new(expr),
1344 then_expr: Box::new(then_expr),
1345 else_expr: Box::new(else_expr),
1346 };
1347 }
1348
1349 Ok(expr)
1350 }
1351
1352 fn logical_or(&mut self) -> Result<Expr, ParserError> {
1353 self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1354 }
1355
1356 fn logical_and(&mut self) -> Result<Expr, ParserError> {
1357 self.binary_left_assoc(&[TokenType::And], Self::equality)
1358 }
1359
1360 fn equality(&mut self) -> Result<Expr, ParserError> {
1361 self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1362 }
1363
1364 fn comparison(&mut self) -> Result<Expr, ParserError> {
1365 self.binary_left_assoc(
1366 &[
1367 TokenType::Greater,
1368 TokenType::Less,
1369 TokenType::GreaterEqual,
1370 TokenType::LessEqual,
1371 ],
1372 Self::term,
1373 )
1374 }
1375
1376 fn term(&mut self) -> Result<Expr, ParserError> {
1377 let mut expr = self.factor()?;
1378
1379 loop {
1380 self.skip_newlines();
1382
1383 self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1385
1386 if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
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("term token should convert to binop");
1396 self.skip_newlines_and_indent();
1398 let right = self.factor()?;
1399 expr = Expr::Binary {
1400 left: Box::new(expr),
1401 op,
1402 right: Box::new(right),
1403 loc: op_loc,
1404 };
1405 }
1406
1407 Ok(expr)
1408 }
1409
1410 fn factor(&mut self) -> Result<Expr, ParserError> {
1411 self.binary_left_assoc(
1412 &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1413 Self::unary,
1414 )
1415 }
1416
1417 fn unary(&mut self) -> Result<Expr, ParserError> {
1418 if self.match_token(&[TokenType::Minus]) {
1419 let expr = self.unary()?;
1420 return Ok(Expr::Unary {
1421 op: UnOp::Neg,
1422 expr: Box::new(expr),
1423 });
1424 }
1425
1426 if self.match_token(&[TokenType::Not]) {
1427 let expr = self.unary()?;
1428 return Ok(Expr::Unary {
1429 op: UnOp::Not,
1430 expr: Box::new(expr),
1431 });
1432 }
1433
1434 self.postfix()
1435 }
1436
1437 fn postfix(&mut self) -> Result<Expr, ParserError> {
1438 let mut expr = self.primary()?;
1439
1440 loop {
1441 if self.match_token(&[TokenType::Dot]) {
1442 let member = match &self.peek().typ {
1445 TokenType::Ident(name) => {
1446 let name = name.clone();
1447 self.advance();
1448 name
1449 }
1450 TokenType::Int => {
1451 self.advance();
1452 "int".to_string()
1453 }
1454 TokenType::Float => {
1455 self.advance();
1456 "float".to_string()
1457 }
1458 _ => {
1459 let lexeme = self.peek().lexeme.clone();
1461 if !lexeme.is_empty() {
1462 self.advance();
1463 lexeme
1464 } else {
1465 return Err(ParserError::ExpectedIdentifierAfterDot(self.peek().line));
1466 }
1467 }
1468 };
1469 expr = Expr::MemberAccess {
1470 object: Box::new(expr),
1471 member,
1472 };
1473 } else if self.match_token(&[TokenType::LBracket]) {
1474 let index = self.expression()?;
1476 self.consume(TokenType::RBracket, "Expected ']'")?;
1477 expr = Expr::Index {
1478 expr: Box::new(expr),
1479 index: Box::new(index),
1480 };
1481 } else if self.check(&TokenType::Less) {
1482 let type_args = self.try_parse_type_args().unwrap_or_default();
1486
1487 if self.match_token(&[TokenType::LParen]) {
1489 let lparen = &self.tokens[self.current - 1];
1490 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1491 let id = self.next_call_id();
1492 let args = self.arguments()?;
1493 self.consume(TokenType::RParen, "Expected ')'")?;
1494 expr = Expr::Call {
1495 callee: Box::new(expr),
1496 type_args,
1497 args,
1498 id,
1499 loc: call_loc,
1500 };
1501 } else {
1502 break;
1504 }
1505 } else if self.match_token(&[TokenType::LParen]) {
1506 let lparen = &self.tokens[self.current - 1];
1508 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1509 let id = self.next_call_id();
1510 let args = self.arguments()?;
1511 self.consume(TokenType::RParen, "Expected ')'")?;
1512 expr = Expr::Call {
1513 callee: Box::new(expr),
1514 type_args: vec![],
1515 args,
1516 id,
1517 loc: call_loc,
1518 };
1519 } else {
1520 break;
1521 }
1522 }
1523
1524 Ok(expr)
1525 }
1526
1527 fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1528 let mut args = vec![];
1529
1530 if !self.check(&TokenType::RParen) {
1531 loop {
1532 let key_name = match &self.peek().typ {
1537 TokenType::Ident(name) => Some(name.clone()),
1538 TokenType::Type => Some("type".to_string()),
1539 _ => None,
1540 };
1541 if let Some(name) = key_name {
1542 if let Some((name, value)) = self.try_parse(|p| {
1543 p.advance(); if p.check(&TokenType::Assign) {
1545 p.advance(); let value = p.expression()?;
1548 Ok((name.clone(), value))
1549 } else {
1550 Err(ParserError::UnexpectedToken(
1551 p.peek().typ.clone(),
1552 p.peek().line,
1553 ))
1554 }
1555 }) {
1556 args.push(Argument::Named { name, value });
1557 } else {
1558 let expr = self.expression()?;
1560 args.push(Argument::Positional(expr));
1561 }
1562 } else {
1563 let expr = self.expression()?;
1564 args.push(Argument::Positional(expr));
1565 }
1566
1567 if !self.match_token(&[TokenType::Comma]) {
1568 break;
1569 }
1570 }
1571 }
1572
1573 Ok(args)
1574 }
1575
1576 fn primary(&mut self) -> Result<Expr, ParserError> {
1577 if let TokenType::IntLiteral(n) = self.peek().typ {
1578 self.advance();
1579 return Ok(Expr::Literal(Literal::Int(n)));
1580 }
1581
1582 if let TokenType::Number(n) = self.peek().typ {
1583 self.advance();
1584 return Ok(Expr::Literal(Literal::Number(n)));
1585 }
1586
1587 if let TokenType::String(ref s) = self.peek().typ {
1588 let s = s.clone();
1589 self.advance();
1590 return Ok(Expr::Literal(Literal::String(s)));
1591 }
1592
1593 if let TokenType::Bool(b) = self.peek().typ {
1594 self.advance();
1595 return Ok(Expr::Literal(Literal::Bool(b)));
1596 }
1597
1598 if let TokenType::HexColor(ref hex) = self.peek().typ {
1599 let hex = hex.clone();
1600 self.advance();
1601 return Ok(Expr::Literal(Literal::HexColor(hex)));
1602 }
1603
1604 if self.match_token(&[TokenType::Na]) {
1606 return Ok(Expr::Literal(Literal::Na));
1607 }
1608
1609 if self.match_token(&[TokenType::Int, TokenType::Float]) {
1612 let name = self.tokens[self.current - 1].lexeme.clone();
1613 return Ok(Expr::Variable(name));
1614 }
1615
1616 if let TokenType::Ident(ref name) = self.peek().typ {
1617 let name = name.clone();
1618 self.advance();
1619 return Ok(Expr::Variable(name));
1620 }
1621
1622 if self.match_token(&[TokenType::LParen]) {
1623 self.skip_newlines();
1625 let had_indent = self.match_token(&[TokenType::Indent]);
1626
1627 let expr = self.expression()?;
1628
1629 self.skip_newlines();
1631 if had_indent {
1632 self.match_token(&[TokenType::Dedent]);
1633 }
1634
1635 self.consume(TokenType::RParen, "Expected ')'")?;
1636 return Ok(expr);
1637 }
1638
1639 if self.match_token(&[TokenType::Switch]) {
1641 let value = Box::new(self.expression()?);
1642
1643 self.match_token(&[TokenType::Newline]);
1645
1646 let has_indent = self.match_token(&[TokenType::Indent]);
1648
1649 let mut cases = vec![];
1650
1651 loop {
1653 self.skip_newlines();
1655
1656 if self.check(&TokenType::Dedent) {
1658 if has_indent {
1659 self.advance(); }
1661 break;
1662 }
1663
1664 if self.is_at_end() {
1666 break;
1667 }
1668
1669 if self.match_token(&[TokenType::Arrow]) {
1671 self.skip_newlines();
1673
1674 let result = self.expression()?;
1676
1677 let default_pattern = Expr::Literal(Literal::Bool(true));
1679 cases.push((default_pattern, result));
1680 continue;
1681 }
1682
1683 if let Some((pattern, result)) = self.try_parse(|p| {
1685 let pattern = p.expression()?;
1687
1688 if !p.match_token(&[TokenType::Arrow]) {
1690 return Err(ParserError::UnexpectedToken(
1691 p.peek().typ.clone(),
1692 p.peek().line,
1693 ));
1694 }
1695
1696 p.skip_newlines();
1698
1699 let result = p.expression()?;
1701 Ok((pattern, result))
1702 }) {
1703 cases.push((pattern, result));
1704 } else {
1705 break;
1706 }
1707 }
1708
1709 return Ok(Expr::Switch { value, cases });
1710 }
1711
1712 if self.match_token(&[TokenType::LBracket]) {
1714 self.skip_newlines_and_indent();
1716
1717 let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1718
1719 self.skip_newlines_and_dedent();
1721
1722 self.consume(TokenType::RBracket, "Expected ']'")?;
1723 return Ok(Expr::Array(elements));
1724 }
1725
1726 Err(ParserError::UnexpectedToken(
1727 self.peek().typ.clone(),
1728 self.peek().line,
1729 ))
1730 }
1731}
1732
1733#[cfg(test)]
1734mod tests {
1735 use super::*;
1736 use pine_lexer::Lexer;
1737
1738 fn parse_expr(input: &str) -> eyre::Result<Expr> {
1739 let mut lexer = Lexer::new(input);
1740 let tokens = lexer.tokenize()?;
1741 let mut parser = Parser::new(tokens);
1742 let stmts = parser.parse()?;
1743
1744 if let Some(Stmt::Expression(expr)) = stmts.first() {
1745 Ok(expr.clone())
1746 } else {
1747 Err(eyre::eyre!("Expected expression statement".to_string()))
1748 }
1749 }
1750
1751 #[test]
1752 fn test_literals() {
1753 let expr = parse_expr("42").unwrap();
1755 assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1756
1757 let expr = parse_expr(r#""hello""#).unwrap();
1759 assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1760
1761 let expr = parse_expr("true").unwrap();
1763 assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1764 }
1765
1766 #[test]
1767 fn test_variables() {
1768 let expr = parse_expr("close").unwrap();
1769 assert_eq!(expr, Expr::Variable("close".to_string()));
1770
1771 let expr = parse_expr("my_var").unwrap();
1772 assert_eq!(expr, Expr::Variable("my_var".to_string()));
1773 }
1774
1775 #[test]
1776 fn test_historical_references() {
1777 let expr = parse_expr("close[1]").unwrap();
1779 assert!(matches!(expr, Expr::Index { .. }));
1780 if let Expr::Index { expr: base, index } = expr {
1781 assert_eq!(*base, Expr::Variable("close".to_string()));
1782 assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1783 }
1784
1785 let expr = parse_expr("high[5]").unwrap();
1787 if let Expr::Index { expr: base, index } = expr {
1788 assert_eq!(*base, Expr::Variable("high".to_string()));
1789 assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1790 }
1791 }
1792
1793 #[test]
1794 fn test_function_calls() {
1795 let expr = parse_expr("sma(close, 14)").unwrap();
1797 if let Expr::Call {
1798 callee,
1799 type_args,
1800 args,
1801 ..
1802 } = expr
1803 {
1804 assert_eq!(*callee, Expr::Variable("sma".to_string()));
1805 assert_eq!(type_args.len(), 0);
1806 assert_eq!(args.len(), 2);
1807 assert_eq!(
1808 args[0],
1809 Argument::Positional(Expr::Variable("close".to_string()))
1810 );
1811 assert_eq!(
1812 args[1],
1813 Argument::Positional(Expr::Literal(Literal::Int(14)))
1814 );
1815 } else {
1816 panic!("Expected function call");
1817 }
1818
1819 let expr = parse_expr("foo()").unwrap();
1821 if let Expr::Call {
1822 callee,
1823 type_args,
1824 args,
1825 ..
1826 } = expr
1827 {
1828 assert_eq!(*callee, Expr::Variable("foo".to_string()));
1829 assert_eq!(type_args.len(), 0);
1830 assert_eq!(args.len(), 0);
1831 }
1832 }
1833
1834 #[test]
1835 fn test_arithmetic_expressions() {
1836 let expr = parse_expr("2 + 3").unwrap();
1838 if let Expr::Binary {
1839 left, op, right, ..
1840 } = expr
1841 {
1842 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1843 assert_eq!(op, BinOp::Add);
1844 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1845 }
1846
1847 let expr = parse_expr("2 + 3 * 4").unwrap();
1849 if let Expr::Binary {
1850 left,
1851 op: op1,
1852 right,
1853 ..
1854 } = expr
1855 {
1856 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1857 assert_eq!(op1, BinOp::Add);
1858 if let Expr::Binary {
1859 left: l2,
1860 op: op2,
1861 right: r2,
1862 ..
1863 } = *right
1864 {
1865 assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
1866 assert_eq!(op2, BinOp::Mul);
1867 assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
1868 }
1869 }
1870
1871 let expr = parse_expr("10 / 2").unwrap();
1873 if let Expr::Binary {
1874 left, op, right, ..
1875 } = expr
1876 {
1877 assert_eq!(*left, Expr::Literal(Literal::Int(10)));
1878 assert_eq!(op, BinOp::Div);
1879 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
1880 }
1881
1882 let expr = parse_expr("5 - 3").unwrap();
1884 if let Expr::Binary {
1885 left, op, right, ..
1886 } = expr
1887 {
1888 assert_eq!(*left, Expr::Literal(Literal::Int(5)));
1889 assert_eq!(op, BinOp::Sub);
1890 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1891 }
1892 }
1893
1894 #[test]
1895 fn test_comparison_expressions() {
1896 let expr = parse_expr("close > open").unwrap();
1898 if let Expr::Binary {
1899 left, op, right, ..
1900 } = expr
1901 {
1902 assert_eq!(*left, Expr::Variable("close".to_string()));
1903 assert_eq!(op, BinOp::Greater);
1904 assert_eq!(*right, Expr::Variable("open".to_string()));
1905 }
1906
1907 let expr = parse_expr("rsi < 30").unwrap();
1909 if let Expr::Binary {
1910 left, op, right, ..
1911 } = expr
1912 {
1913 assert_eq!(*left, Expr::Variable("rsi".to_string()));
1914 assert_eq!(op, BinOp::Less);
1915 assert_eq!(*right, Expr::Literal(Literal::Int(30)));
1916 }
1917
1918 let expr = parse_expr("x == 5").unwrap();
1920 if let Expr::Binary {
1921 left, op, right, ..
1922 } = expr
1923 {
1924 assert_eq!(*left, Expr::Variable("x".to_string()));
1925 assert_eq!(op, BinOp::Eq);
1926 assert_eq!(*right, Expr::Literal(Literal::Int(5)));
1927 }
1928 }
1929
1930 #[test]
1931 fn test_unary_expressions() {
1932 let expr = parse_expr("-5").unwrap();
1934 if let Expr::Unary { op, expr } = expr {
1935 assert_eq!(op, UnOp::Neg);
1936 assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
1937 }
1938
1939 let expr = parse_expr("--10").unwrap();
1941 if let Expr::Unary { op: op1, expr: e1 } = expr {
1942 assert_eq!(op1, UnOp::Neg);
1943 if let Expr::Unary { op: op2, expr: e2 } = *e1 {
1944 assert_eq!(op2, UnOp::Neg);
1945 assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
1946 }
1947 }
1948 }
1949
1950 #[test]
1951 fn test_var_declarations() {
1952 let mut lexer = Lexer::new("var x = 10");
1953 let tokens = lexer.tokenize().unwrap();
1954 let mut parser = Parser::new(tokens);
1955 let stmts = parser.parse().unwrap();
1956
1957 assert_eq!(stmts.len(), 1);
1958 if let Stmt::VarDecl {
1959 name,
1960 type_qualifier,
1961 type_annotation,
1962 initializer,
1963 var_kind,
1964 } = &stmts[0]
1965 {
1966 assert_eq!(name, "x");
1967 assert_eq!(*type_qualifier, None);
1968 assert_eq!(*type_annotation, None);
1969 assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
1970 assert_eq!(
1971 initializer.as_ref().unwrap(),
1972 &Expr::Literal(Literal::Int(10))
1973 );
1974 } else {
1975 panic!("Expected VarDecl");
1976 }
1977
1978 let mut lexer = Lexer::new("var y");
1980 let tokens = lexer.tokenize().unwrap();
1981 let mut parser = Parser::new(tokens);
1982 let stmts = parser.parse().unwrap();
1983
1984 if let Stmt::VarDecl {
1985 name, initializer, ..
1986 } = &stmts[0]
1987 {
1988 assert_eq!(name, "y");
1989 assert!(initializer.is_none());
1990 }
1991 }
1992
1993 #[test]
1994 fn test_pinescript_examples() {
1995 let expr = parse_expr("close[1] > close[2]").unwrap();
1997 assert!(matches!(
1998 expr,
1999 Expr::Binary {
2000 op: BinOp::Greater,
2001 ..
2002 }
2003 ));
2004
2005 let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2007 if let Expr::Binary {
2008 left, op, right, ..
2009 } = expr
2010 {
2011 assert_eq!(op, BinOp::Greater);
2012 assert!(matches!(*left, Expr::Call { .. }));
2013 assert!(matches!(*right, Expr::Call { .. }));
2014 }
2015
2016 let expr = parse_expr("(high + low) / 2").unwrap();
2018 if let Expr::Binary {
2019 left,
2020 op: div_op,
2021 right,
2022 ..
2023 } = expr
2024 {
2025 assert_eq!(div_op, BinOp::Div);
2026 assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2027 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2028 }
2029 }
2030
2031 fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2033 walkdir::WalkDir::new(dir)
2034 .into_iter()
2035 .filter_map(|e| e.ok())
2036 .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2037 .map(|e| e.path().to_path_buf())
2038 .collect()
2039 }
2040
2041 #[test]
2042 fn test_parse_testdata_files() -> eyre::Result<()> {
2043 use std::fs;
2044 use std::path::Path;
2045
2046 let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2047
2048 let filter = std::env::var("TEST_FILE").ok();
2049 let debug = std::env::var("DEBUG").is_ok();
2050 let generate_ast = std::env::var("GENERATE_AST").is_ok();
2051
2052 let pine_files = collect_pine_files_recursive(&testdata_dir);
2053
2054 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2055 let content = fs::read_to_string(path)?;
2056
2057 let mut lexer = Lexer::new(&content);
2058 let tokens = lexer.tokenize()?;
2059
2060 if debug {
2061 println!("Tokens: {:#?}", tokens);
2062 }
2063
2064 let mut parser = Parser::new(tokens);
2065 let ast = parser.parse()?;
2066
2067 if debug {
2068 let ast_json = serde_json::to_string(&ast)?;
2069 println!("AST JSON: {:?}", ast_json);
2070 }
2071
2072 let json_path = path.with_file_name(format!(
2074 "{}_ast.json",
2075 path.file_stem().unwrap().to_str().unwrap()
2076 ));
2077
2078 if generate_ast {
2079 let json = serde_json::to_string_pretty(&ast)?;
2081 fs::write(&json_path, &json)?;
2082 } else if json_path.exists() {
2083 let expected_json = fs::read_to_string(&json_path)?;
2085 let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2086
2087 if ast != expected_ast {
2088 return Err(eyre::eyre!(
2089 "AST mismatch, expected AST from {:?}",
2090 json_path
2091 ));
2092 }
2093 }
2094
2095 Ok(())
2096 };
2097
2098 for path in pine_files {
2099 let filename = path.file_name().unwrap().to_str().unwrap();
2100
2101 if let Some(ref filter_name) = filter {
2103 if filename != filter_name {
2104 continue;
2105 }
2106 }
2107
2108 if let Err(e) = process_file(&path) {
2109 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2110 }
2111 }
2112
2113 Ok(())
2114 }
2115
2116 #[test]
2117 #[ignore]
2118 fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2119 use std::fs;
2120 use std::path::Path;
2121
2122 let testdata_dir =
2123 Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2124
2125 let filter = std::env::var("TEST_FILE").ok();
2126 let debug = std::env::var("DEBUG").is_ok();
2127
2128 let pine_files = collect_pine_files_recursive(&testdata_dir);
2129
2130 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2131 let content = fs::read_to_string(path)?;
2132
2133 let mut lexer = Lexer::new(&content);
2134 let tokens = lexer.tokenize()?;
2135
2136 if debug {
2137 println!("Tokens: {:#?}", tokens);
2138 }
2139
2140 let mut parser = Parser::new(tokens);
2141 let ast = parser.parse()?;
2142
2143 let ast_json = serde_json::to_string(&ast)?;
2144
2145 if debug {
2146 println!("AST JSON: {:?}", ast_json);
2147 }
2148
2149 Ok(())
2150 };
2151
2152 for path in pine_files {
2153 let filename = path.file_name().unwrap().to_str().unwrap();
2154
2155 if let Some(ref filter_name) = filter {
2157 if filename != filter_name {
2158 continue;
2159 }
2160 }
2161
2162 if let Err(e) = process_file(&path) {
2163 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2164 }
2165 }
2166
2167 Ok(())
2168 }
2169}