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 loc: self.prev_loc(),
871 });
872 }
873
874 if self.match_token(&[TokenType::Continue]) {
876 return Ok(Stmt::Continue {
877 loc: self.prev_loc(),
878 });
879 }
880
881 if self.check(&TokenType::LBracket) {
884 let tuple_loc = self.cur_loc();
885 if let Some((names, value)) = self.try_parse(|p| {
886 p.advance(); let mut names = vec![];
889
890 if !p.check(&TokenType::RBracket) {
892 loop {
893 if let TokenType::Ident(name) = &p.peek().typ {
894 names.push(name.clone());
895 p.advance();
896 } else {
897 return Err(ParserError::ExpectedVariableName(p.peek().line));
899 }
900
901 if !p.match_token(&[TokenType::Comma]) {
902 break;
903 }
904 }
905 }
906
907 p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
908 p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
909
910 p.skip_newlines();
912
913 let value = p.expression()?;
914
915 Ok((names, value))
916 }) {
917 return Ok(Stmt::TupleAssignment {
918 names,
919 value,
920 loc: tuple_loc,
921 });
922 }
923 }
924
925 if let TokenType::Ident(name) = &self.peek().typ {
930 let name = name.clone();
931 let name_loc = self.cur_loc();
932
933 if let Some((param_structs, body)) = self.try_parse(|p| {
935 p.advance(); p.consume(TokenType::LParen, "Expected '('")?;
937
938 let params = p.function_params()?;
939 p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
940 p.consume(TokenType::Arrow, "Expected '=>'")?;
941
942 p.match_token(&[TokenType::Newline]);
944
945 let body = p.parse_block()?;
947
948 Ok((params, body))
949 }) {
950 let initializer = Some(Expr::Function {
952 params: param_structs,
953 body,
954 });
955 return Ok(Stmt::VarDecl {
956 name,
957 type_qualifier: None,
958 type_annotation: None,
959 initializer,
960 var_kind: VarKind::Plain,
961 loc: name_loc,
962 });
963 }
964
965 if let Some(stmt) = self.try_parse(|p| {
967 p.advance(); if p.match_token(&[TokenType::Assign]) {
970 let initializer = Some(p.parse_indented_expression()?);
972
973 Ok(Stmt::VarDecl {
974 name: name.clone(),
975 type_qualifier: None,
976 type_annotation: None,
977 initializer,
978 var_kind: VarKind::Plain,
979 loc: name_loc,
980 })
981 } else if p.match_token(&[TokenType::ColonAssign]) {
982 let value = p.parse_indented_expression()?;
984
985 Ok(Stmt::Assignment {
986 target: Expr::Variable {
987 name: name.clone(),
988 loc: name_loc,
989 },
990 value,
991 })
992 } else if p.match_token(&[
993 TokenType::PlusAssign,
994 TokenType::MinusAssign,
995 TokenType::StarAssign,
996 TokenType::SlashAssign,
997 ]) {
998 let op_tok = &p.tokens[p.current - 1];
1000 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1001 let op = op_tok
1002 .typ
1003 .to_binop()
1004 .expect("compound assign token should convert to binop");
1005
1006 let right = p.parse_indented_expression()?;
1007
1008 let value = Expr::Binary {
1009 left: Box::new(Expr::Variable {
1010 name: name.clone(),
1011 loc: name_loc,
1012 }),
1013 op,
1014 right: Box::new(right),
1015 loc: op_loc,
1016 };
1017 Ok(Stmt::Assignment {
1018 target: Expr::Variable {
1019 name: name.clone(),
1020 loc: name_loc,
1021 },
1022 value,
1023 })
1024 } else {
1025 Err(ParserError::UnexpectedToken(
1027 p.peek().typ.clone(),
1028 p.peek().line,
1029 ))
1030 }
1031 }) {
1032 return Ok(stmt);
1033 }
1034 }
1035
1036 self.expression_statement()
1037 }
1038
1039 fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
1040 self.parse_comma_separated(&TokenType::RParen, |p| {
1041 let type_qualifier = p.parse_optional_type_qualifier();
1043
1044 let type_annotation = p.parse_optional_type_annotation();
1046
1047 let param_loc = p.cur_loc();
1048 let name = p.expect_identifier()?;
1049
1050 let default_value = if p.match_token(&[TokenType::Assign]) {
1052 Some(p.expression()?)
1053 } else {
1054 None
1055 };
1056
1057 Ok(pine_ast::FunctionParam {
1058 type_qualifier,
1059 type_annotation,
1060 name,
1061 default_value,
1062 loc: param_loc,
1063 })
1064 })
1065 }
1066
1067 fn for_statement(&mut self) -> Result<Stmt, ParserError> {
1068 if self.check(&TokenType::LBracket) {
1070 self.advance(); let loc = self.cur_loc();
1073 let index_var = self.expect_identifier()?;
1074
1075 self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
1076
1077 let item_var = self.expect_identifier()?;
1078
1079 self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
1080 self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
1081
1082 let collection = self.expression()?;
1083
1084 self.match_token(&[TokenType::Newline]);
1086
1087 let body = self.parse_block()?;
1088
1089 return Ok(Stmt::ForIn {
1090 index_var: Some(index_var),
1091 item_var,
1092 collection,
1093 body,
1094 loc,
1095 });
1096 }
1097
1098 let loc = self.cur_loc();
1100 let var_name = self.expect_identifier()?;
1101
1102 if self.check(&TokenType::In) {
1104 self.advance(); let collection = self.expression()?;
1107
1108 self.match_token(&[TokenType::Newline]);
1110
1111 let body = self.parse_block()?;
1112
1113 Ok(Stmt::ForIn {
1114 index_var: None,
1115 item_var: var_name,
1116 collection,
1117 body,
1118 loc,
1119 })
1120 } else {
1121 self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1123 let from = self.expression()?;
1124 self.consume(TokenType::To, "Expected 'to' in for loop")?;
1125 let to = self.expression()?;
1126
1127 let step = if matches!(&self.peek().typ, TokenType::Ident(name) if name == "by") {
1130 self.advance(); Some(self.expression()?)
1132 } else {
1133 None
1134 };
1135
1136 self.match_token(&[TokenType::Newline]);
1138
1139 let body = self.parse_block()?;
1141
1142 Ok(Stmt::For {
1143 var_name,
1144 from,
1145 to,
1146 step,
1147 body,
1148 loc,
1149 })
1150 }
1151 }
1152
1153 fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1154 let condition = self.expression()?;
1156
1157 self.match_token(&[TokenType::Newline]);
1159
1160 let body = self.parse_block()?;
1162
1163 Ok(Stmt::While { condition, body })
1164 }
1165
1166 fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1167 let condition = self.expression()?;
1169
1170 self.match_token(&[TokenType::Newline]);
1172
1173 let then_branch = self.parse_block()?;
1175
1176 let mut else_if_branches = Vec::new();
1178
1179 loop {
1180 self.skip_newlines();
1182
1183 if self.check(&TokenType::Else) {
1185 if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1186 p.advance(); if p.match_token(&[TokenType::If]) {
1189 let else_if_condition = p.expression()?;
1191 p.match_token(&[TokenType::Newline]);
1192 let else_if_body = p.parse_block()?;
1193 Ok((else_if_condition, else_if_body))
1194 } else {
1195 Err(ParserError::UnexpectedToken(
1196 p.peek().typ.clone(),
1197 p.peek().line,
1198 ))
1199 }
1200 }) {
1201 else_if_branches.push((else_if_condition, else_if_body));
1202 } else {
1203 break;
1204 }
1205 } else {
1206 break;
1207 }
1208 }
1209
1210 self.skip_newlines();
1212
1213 let else_branch = if self.match_token(&[TokenType::Else]) {
1214 self.match_token(&[TokenType::Newline]);
1216
1217 Some(self.parse_block()?)
1218 } else {
1219 None
1220 };
1221
1222 Ok(Stmt::If {
1223 condition,
1224 then_branch,
1225 else_if_branches,
1226 else_branch,
1227 })
1228 }
1229
1230 fn if_expression(&mut self) -> Result<Expr, ParserError> {
1231 self.consume(TokenType::If, "Expected 'if'")?;
1233
1234 let condition = self.expression()?;
1236
1237 self.match_token(&[TokenType::Newline]);
1239
1240 self.match_token(&[TokenType::Indent]);
1242
1243 let then_expr = self.expression()?;
1245
1246 self.skip_newlines();
1248 self.match_token(&[TokenType::Dedent]);
1249
1250 let mut else_if_branches = Vec::new();
1252
1253 loop {
1254 self.skip_newlines();
1256
1257 if self.check(&TokenType::Else) {
1259 if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1260 p.advance(); if p.match_token(&[TokenType::If]) {
1263 let else_if_condition = p.expression()?;
1265 p.match_token(&[TokenType::Newline]);
1266 p.match_token(&[TokenType::Indent]);
1267 let else_if_expr = p.expression()?;
1268 p.skip_newlines();
1269 p.match_token(&[TokenType::Dedent]);
1270 Ok((else_if_condition, else_if_expr))
1271 } else {
1272 Err(ParserError::UnexpectedToken(
1273 p.peek().typ.clone(),
1274 p.peek().line,
1275 ))
1276 }
1277 }) {
1278 else_if_branches.push((else_if_condition, else_if_expr));
1279 } else {
1280 break;
1281 }
1282 } else {
1283 break;
1284 }
1285 }
1286
1287 self.skip_newlines();
1289 let else_expr = if self.match_token(&[TokenType::Else]) {
1290 self.match_token(&[TokenType::Newline]);
1292
1293 self.match_token(&[TokenType::Indent]);
1295
1296 let expr = self.expression()?;
1298
1299 self.skip_newlines();
1301 self.match_token(&[TokenType::Dedent]);
1302
1303 Some(Box::new(expr))
1304 } else {
1305 None };
1307
1308 Ok(Expr::IfExpr {
1309 condition: Box::new(condition),
1310 then_expr: Box::new(then_expr),
1311 else_if_branches,
1312 else_expr,
1313 })
1314 }
1315
1316 fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1317 let mut stmts = vec![];
1318
1319 if !self.match_token(&[TokenType::Indent]) {
1321 if !self.check(&TokenType::Newline)
1324 && !self.check(&TokenType::Else)
1325 && !self.is_at_end()
1326 {
1327 stmts.push(self.declaration()?);
1328 }
1329 return Ok(stmts);
1330 }
1331
1332 loop {
1334 self.skip_newlines();
1336
1337 if self.check(&TokenType::Else) {
1339 break;
1340 }
1341
1342 if self.check(&TokenType::Dedent) {
1344 self.advance(); self.skip_newlines();
1348 if self.check(&TokenType::Else) {
1349 break;
1350 }
1351
1352 break;
1354 }
1355
1356 if self.is_at_end() {
1358 break;
1359 }
1360
1361 stmts.push(self.declaration()?);
1363 }
1364
1365 Ok(stmts)
1366 }
1367
1368 fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1369 let expr = self.expression()?;
1370
1371 if self.match_token(&[TokenType::ColonAssign]) {
1373 let value = self.parse_indented_expression()?;
1374 return Ok(Stmt::Assignment {
1375 target: expr,
1376 value,
1377 });
1378 }
1379
1380 Ok(Stmt::Expression(expr))
1381 }
1382
1383 fn expression(&mut self) -> Result<Expr, ParserError> {
1385 self.ternary()
1386 }
1387
1388 fn binary_left_assoc(
1390 &mut self,
1391 operators: &[TokenType],
1392 next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1393 ) -> Result<Expr, ParserError> {
1394 let mut expr = next_precedence(self)?;
1395
1396 loop {
1397 self.skip_newlines();
1399
1400 if !self.match_token(operators) {
1401 break;
1402 }
1403
1404 let op_tok = &self.tokens[self.current - 1];
1405 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1406 let op = op_tok
1407 .typ
1408 .to_binop()
1409 .expect("matched operator token should convert to binop");
1410
1411 self.skip_newlines_and_indent();
1413
1414 let right = next_precedence(self)?;
1415 expr = Expr::Binary {
1416 left: Box::new(expr),
1417 op,
1418 right: Box::new(right),
1419 loc: op_loc,
1420 };
1421 }
1422
1423 Ok(expr)
1424 }
1425
1426 fn ternary(&mut self) -> Result<Expr, ParserError> {
1427 if self.check(&TokenType::If) {
1429 return self.if_expression();
1430 }
1431
1432 let mut expr = self.logical_or()?;
1433
1434 self.skip_newlines();
1436
1437 self.try_consume_indent_if_followed_by(&TokenType::Question);
1439
1440 if self.match_token(&[TokenType::Question]) {
1441 self.skip_newlines_and_indent();
1443
1444 let then_expr = self.expression()?;
1445
1446 self.skip_newlines();
1448
1449 self.try_consume_indent_if_followed_by(&TokenType::Colon);
1451
1452 self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1453
1454 self.skip_newlines_and_indent();
1456
1457 let else_expr = self.expression()?;
1458 expr = Expr::Ternary {
1459 condition: Box::new(expr),
1460 then_expr: Box::new(then_expr),
1461 else_expr: Box::new(else_expr),
1462 };
1463 }
1464
1465 Ok(expr)
1466 }
1467
1468 fn logical_or(&mut self) -> Result<Expr, ParserError> {
1469 self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1470 }
1471
1472 fn logical_and(&mut self) -> Result<Expr, ParserError> {
1473 self.binary_left_assoc(&[TokenType::And], Self::equality)
1474 }
1475
1476 fn equality(&mut self) -> Result<Expr, ParserError> {
1477 self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1478 }
1479
1480 fn comparison(&mut self) -> Result<Expr, ParserError> {
1481 self.binary_left_assoc(
1482 &[
1483 TokenType::Greater,
1484 TokenType::Less,
1485 TokenType::GreaterEqual,
1486 TokenType::LessEqual,
1487 ],
1488 Self::term,
1489 )
1490 }
1491
1492 fn term(&mut self) -> Result<Expr, ParserError> {
1493 let mut expr = self.factor()?;
1494
1495 loop {
1496 self.skip_newlines();
1498
1499 self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1501
1502 if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
1503 break;
1504 }
1505
1506 let op_tok = &self.tokens[self.current - 1];
1507 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1508 let op = op_tok
1509 .typ
1510 .to_binop()
1511 .expect("term token should convert to binop");
1512 self.skip_newlines_and_indent();
1514 let right = self.factor()?;
1515 expr = Expr::Binary {
1516 left: Box::new(expr),
1517 op,
1518 right: Box::new(right),
1519 loc: op_loc,
1520 };
1521 }
1522
1523 Ok(expr)
1524 }
1525
1526 fn factor(&mut self) -> Result<Expr, ParserError> {
1527 self.binary_left_assoc(
1528 &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1529 Self::unary,
1530 )
1531 }
1532
1533 fn unary(&mut self) -> Result<Expr, ParserError> {
1534 if self.match_token(&[TokenType::Minus]) {
1535 let expr = self.unary()?;
1536 return Ok(Expr::Unary {
1537 op: UnOp::Neg,
1538 expr: Box::new(expr),
1539 });
1540 }
1541
1542 if self.match_token(&[TokenType::Not]) {
1543 let expr = self.unary()?;
1544 return Ok(Expr::Unary {
1545 op: UnOp::Not,
1546 expr: Box::new(expr),
1547 });
1548 }
1549
1550 self.postfix()
1551 }
1552
1553 fn postfix(&mut self) -> Result<Expr, ParserError> {
1554 let mut expr = self.primary()?;
1555
1556 if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1561 return Ok(expr);
1562 }
1563
1564 loop {
1565 if self.match_token(&[TokenType::Dot]) {
1566 let member = match &self.peek().typ {
1569 TokenType::Ident(name) => {
1570 let name = name.clone();
1571 self.advance();
1572 name
1573 }
1574 TokenType::Int => {
1575 self.advance();
1576 "int".to_string()
1577 }
1578 TokenType::Float => {
1579 self.advance();
1580 "float".to_string()
1581 }
1582 _ => {
1583 let lexeme = self.peek().lexeme.clone();
1585 if !lexeme.is_empty() {
1586 self.advance();
1587 lexeme
1588 } else {
1589 return Err(ParserError::ExpectedIdentifierAfterDot(self.peek().line));
1590 }
1591 }
1592 };
1593 let member_loc = self.prev_loc();
1594 expr = Expr::MemberAccess {
1595 object: Box::new(expr),
1596 member,
1597 member_loc,
1598 };
1599 } else if self.match_token(&[TokenType::LBracket]) {
1600 let index = self.expression()?;
1602 self.consume(TokenType::RBracket, "Expected ']'")?;
1603 expr = Expr::Index {
1604 expr: Box::new(expr),
1605 index: Box::new(index),
1606 id: self.next_call_id(),
1607 };
1608 } else if self.check(&TokenType::Less) {
1609 let type_args = self.try_parse_type_args().unwrap_or_default();
1613
1614 if self.match_token(&[TokenType::LParen]) {
1616 let lparen = &self.tokens[self.current - 1];
1617 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1618 let id = self.next_call_id();
1619 let args = self.arguments()?;
1620 self.consume(TokenType::RParen, "Expected ')'")?;
1621 expr = Expr::Call {
1622 callee: Box::new(expr),
1623 type_args,
1624 args,
1625 id,
1626 loc: call_loc,
1627 };
1628 } else {
1629 break;
1631 }
1632 } else if self.match_token(&[TokenType::LParen]) {
1633 let lparen = &self.tokens[self.current - 1];
1635 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1636 let id = self.next_call_id();
1637 let args = self.arguments()?;
1638 self.consume(TokenType::RParen, "Expected ')'")?;
1639 expr = Expr::Call {
1640 callee: Box::new(expr),
1641 type_args: vec![],
1642 args,
1643 id,
1644 loc: call_loc,
1645 };
1646 } else {
1647 break;
1648 }
1649 }
1650
1651 Ok(expr)
1652 }
1653
1654 fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1655 let mut args = vec![];
1656
1657 if !self.check(&TokenType::RParen) {
1658 loop {
1659 let key_name = match &self.peek().typ {
1664 TokenType::Ident(name) => Some(name.clone()),
1665 TokenType::Type => Some("type".to_string()),
1666 _ => None,
1667 };
1668 if let Some(name) = key_name {
1669 if let Some((name, value)) = self.try_parse(|p| {
1670 p.advance(); if p.check(&TokenType::Assign) {
1672 p.advance(); let value = p.expression()?;
1675 Ok((name.clone(), value))
1676 } else {
1677 Err(ParserError::UnexpectedToken(
1678 p.peek().typ.clone(),
1679 p.peek().line,
1680 ))
1681 }
1682 }) {
1683 args.push(Argument::Named { name, value });
1684 } else {
1685 let expr = self.expression()?;
1687 args.push(Argument::Positional(expr));
1688 }
1689 } else {
1690 let expr = self.expression()?;
1691 args.push(Argument::Positional(expr));
1692 }
1693
1694 if !self.match_token(&[TokenType::Comma]) {
1695 break;
1696 }
1697 }
1698 }
1699
1700 Ok(args)
1701 }
1702
1703 fn primary(&mut self) -> Result<Expr, ParserError> {
1704 if let TokenType::IntLiteral(n) = self.peek().typ {
1705 self.advance();
1706 return Ok(Expr::Literal(Literal::Int(n)));
1707 }
1708
1709 if let TokenType::Number(n) = self.peek().typ {
1710 self.advance();
1711 return Ok(Expr::Literal(Literal::Number(n)));
1712 }
1713
1714 if let TokenType::String(ref s) = self.peek().typ {
1715 let s = s.clone();
1716 self.advance();
1717 return Ok(Expr::Literal(Literal::String(s)));
1718 }
1719
1720 if let TokenType::Bool(b) = self.peek().typ {
1721 self.advance();
1722 return Ok(Expr::Literal(Literal::Bool(b)));
1723 }
1724
1725 if let TokenType::HexColor(ref hex) = self.peek().typ {
1726 let hex = hex.clone();
1727 self.advance();
1728 return Ok(Expr::Literal(Literal::HexColor(hex)));
1729 }
1730
1731 if self.match_token(&[TokenType::Na]) {
1733 return Ok(Expr::Literal(Literal::Na));
1734 }
1735
1736 if self.match_token(&[TokenType::Int, TokenType::Float]) {
1739 let name = self.tokens[self.current - 1].lexeme.clone();
1740 return Ok(Expr::Variable {
1741 name,
1742 loc: self.prev_loc(),
1743 });
1744 }
1745
1746 if let TokenType::Ident(ref name) = self.peek().typ {
1747 let name = name.clone();
1748 let loc = self.cur_loc();
1749 self.advance();
1750 return Ok(Expr::Variable { name, loc });
1751 }
1752
1753 if self.match_token(&[TokenType::LParen]) {
1754 self.skip_newlines();
1756 let had_indent = self.match_token(&[TokenType::Indent]);
1757
1758 let expr = self.expression()?;
1759
1760 self.skip_newlines();
1762 if had_indent {
1763 self.match_token(&[TokenType::Dedent]);
1764 }
1765
1766 self.consume(TokenType::RParen, "Expected ')'")?;
1767 return Ok(expr);
1768 }
1769
1770 if self.match_token(&[TokenType::Switch]) {
1772 let value = if self.check(&TokenType::Newline) {
1776 Box::new(Expr::Literal(Literal::Bool(true)))
1777 } else {
1778 Box::new(self.expression()?)
1779 };
1780
1781 self.match_token(&[TokenType::Newline]);
1783
1784 let has_indent = self.match_token(&[TokenType::Indent]);
1786
1787 let mut cases = vec![];
1788
1789 loop {
1791 self.skip_newlines();
1793
1794 if self.check(&TokenType::Dedent) {
1796 if has_indent {
1797 self.advance(); }
1799 break;
1800 }
1801
1802 if self.is_at_end() {
1804 break;
1805 }
1806
1807 if self.match_token(&[TokenType::Arrow]) {
1809 self.skip_newlines();
1811
1812 let result = self.expression()?;
1814
1815 let default_pattern = Expr::Literal(Literal::Bool(true));
1817 cases.push((default_pattern, result));
1818 continue;
1819 }
1820
1821 if let Some((pattern, result)) = self.try_parse(|p| {
1823 let pattern = p.expression()?;
1825
1826 if !p.match_token(&[TokenType::Arrow]) {
1828 return Err(ParserError::UnexpectedToken(
1829 p.peek().typ.clone(),
1830 p.peek().line,
1831 ));
1832 }
1833
1834 p.skip_newlines();
1836
1837 let result = p.expression()?;
1839 Ok((pattern, result))
1840 }) {
1841 cases.push((pattern, result));
1842 } else {
1843 break;
1844 }
1845 }
1846
1847 return Ok(Expr::Switch { value, cases });
1848 }
1849
1850 if self.match_token(&[TokenType::LBracket]) {
1852 self.skip_newlines_and_indent();
1854
1855 let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1856
1857 self.skip_newlines_and_dedent();
1859
1860 self.consume(TokenType::RBracket, "Expected ']'")?;
1861 return Ok(Expr::Array(elements));
1862 }
1863
1864 Err(ParserError::UnexpectedToken(
1865 self.peek().typ.clone(),
1866 self.peek().line,
1867 ))
1868 }
1869}
1870
1871#[cfg(test)]
1872mod tests {
1873 use super::*;
1874 use pine_lexer::Lexer;
1875
1876 fn parse_expr(input: &str) -> eyre::Result<Expr> {
1877 let mut lexer = Lexer::new(input);
1878 let tokens = lexer.tokenize()?;
1879 let mut parser = Parser::new(tokens);
1880 let stmts = parser.parse()?;
1881
1882 if let Some(Stmt::Expression(expr)) = stmts.first() {
1883 Ok(expr.clone())
1884 } else {
1885 Err(eyre::eyre!("Expected expression statement".to_string()))
1886 }
1887 }
1888
1889 #[test]
1890 fn test_literals() {
1891 let expr = parse_expr("42").unwrap();
1893 assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1894
1895 let expr = parse_expr(r#""hello""#).unwrap();
1897 assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1898
1899 let expr = parse_expr("true").unwrap();
1901 assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1902 }
1903
1904 #[test]
1905 fn test_variables() {
1906 let expr = parse_expr("close").unwrap();
1907 assert_eq!(expr, Expr::var("close"));
1908
1909 let expr = parse_expr("my_var").unwrap();
1910 assert_eq!(expr, Expr::var("my_var"));
1911 }
1912
1913 #[test]
1914 fn test_historical_references() {
1915 let expr = parse_expr("close[1]").unwrap();
1917 assert!(matches!(expr, Expr::Index { .. }));
1918 if let Expr::Index {
1919 expr: base, index, ..
1920 } = expr
1921 {
1922 assert_eq!(*base, Expr::var("close"));
1923 assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1924 }
1925
1926 let expr = parse_expr("high[5]").unwrap();
1928 if let Expr::Index {
1929 expr: base, index, ..
1930 } = expr
1931 {
1932 assert_eq!(*base, Expr::var("high"));
1933 assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1934 }
1935 }
1936
1937 #[test]
1938 fn test_function_calls() {
1939 let expr = parse_expr("sma(close, 14)").unwrap();
1941 if let Expr::Call {
1942 callee,
1943 type_args,
1944 args,
1945 ..
1946 } = expr
1947 {
1948 assert_eq!(*callee, Expr::var("sma"));
1949 assert_eq!(type_args.len(), 0);
1950 assert_eq!(args.len(), 2);
1951 assert_eq!(args[0], Argument::Positional(Expr::var("close")));
1952 assert_eq!(
1953 args[1],
1954 Argument::Positional(Expr::Literal(Literal::Int(14)))
1955 );
1956 } else {
1957 panic!("Expected function call");
1958 }
1959
1960 let expr = parse_expr("foo()").unwrap();
1962 if let Expr::Call {
1963 callee,
1964 type_args,
1965 args,
1966 ..
1967 } = expr
1968 {
1969 assert_eq!(*callee, Expr::var("foo"));
1970 assert_eq!(type_args.len(), 0);
1971 assert_eq!(args.len(), 0);
1972 }
1973 }
1974
1975 #[test]
1976 fn test_arithmetic_expressions() {
1977 let expr = parse_expr("2 + 3").unwrap();
1979 if let Expr::Binary {
1980 left, op, right, ..
1981 } = expr
1982 {
1983 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1984 assert_eq!(op, BinOp::Add);
1985 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1986 }
1987
1988 let expr = parse_expr("2 + 3 * 4").unwrap();
1990 if let Expr::Binary {
1991 left,
1992 op: op1,
1993 right,
1994 ..
1995 } = expr
1996 {
1997 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1998 assert_eq!(op1, BinOp::Add);
1999 if let Expr::Binary {
2000 left: l2,
2001 op: op2,
2002 right: r2,
2003 ..
2004 } = *right
2005 {
2006 assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
2007 assert_eq!(op2, BinOp::Mul);
2008 assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
2009 }
2010 }
2011
2012 let expr = parse_expr("10 / 2").unwrap();
2014 if let Expr::Binary {
2015 left, op, right, ..
2016 } = expr
2017 {
2018 assert_eq!(*left, Expr::Literal(Literal::Int(10)));
2019 assert_eq!(op, BinOp::Div);
2020 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2021 }
2022
2023 let expr = parse_expr("5 - 3").unwrap();
2025 if let Expr::Binary {
2026 left, op, right, ..
2027 } = expr
2028 {
2029 assert_eq!(*left, Expr::Literal(Literal::Int(5)));
2030 assert_eq!(op, BinOp::Sub);
2031 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
2032 }
2033 }
2034
2035 #[test]
2036 fn test_comparison_expressions() {
2037 let expr = parse_expr("close > open").unwrap();
2039 if let Expr::Binary {
2040 left, op, right, ..
2041 } = expr
2042 {
2043 assert_eq!(*left, Expr::var("close"));
2044 assert_eq!(op, BinOp::Greater);
2045 assert_eq!(*right, Expr::var("open"));
2046 }
2047
2048 let expr = parse_expr("rsi < 30").unwrap();
2050 if let Expr::Binary {
2051 left, op, right, ..
2052 } = expr
2053 {
2054 assert_eq!(*left, Expr::var("rsi"));
2055 assert_eq!(op, BinOp::Less);
2056 assert_eq!(*right, Expr::Literal(Literal::Int(30)));
2057 }
2058
2059 let expr = parse_expr("x == 5").unwrap();
2061 if let Expr::Binary {
2062 left, op, right, ..
2063 } = expr
2064 {
2065 assert_eq!(*left, Expr::var("x"));
2066 assert_eq!(op, BinOp::Eq);
2067 assert_eq!(*right, Expr::Literal(Literal::Int(5)));
2068 }
2069 }
2070
2071 #[test]
2072 fn test_unary_expressions() {
2073 let expr = parse_expr("-5").unwrap();
2075 if let Expr::Unary { op, expr } = expr {
2076 assert_eq!(op, UnOp::Neg);
2077 assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
2078 }
2079
2080 let expr = parse_expr("--10").unwrap();
2082 if let Expr::Unary { op: op1, expr: e1 } = expr {
2083 assert_eq!(op1, UnOp::Neg);
2084 if let Expr::Unary { op: op2, expr: e2 } = *e1 {
2085 assert_eq!(op2, UnOp::Neg);
2086 assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
2087 }
2088 }
2089 }
2090
2091 #[test]
2092 fn test_var_declarations() {
2093 let mut lexer = Lexer::new("var x = 10");
2094 let tokens = lexer.tokenize().unwrap();
2095 let mut parser = Parser::new(tokens);
2096 let stmts = parser.parse().unwrap();
2097
2098 assert_eq!(stmts.len(), 1);
2099 if let Stmt::VarDecl {
2100 name,
2101 type_qualifier,
2102 type_annotation,
2103 initializer,
2104 var_kind,
2105 ..
2106 } = &stmts[0]
2107 {
2108 assert_eq!(name, "x");
2109 assert_eq!(*type_qualifier, None);
2110 assert_eq!(*type_annotation, None);
2111 assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
2112 assert_eq!(
2113 initializer.as_ref().unwrap(),
2114 &Expr::Literal(Literal::Int(10))
2115 );
2116 } else {
2117 panic!("Expected VarDecl");
2118 }
2119
2120 let mut lexer = Lexer::new("var y");
2122 let tokens = lexer.tokenize().unwrap();
2123 let mut parser = Parser::new(tokens);
2124 let stmts = parser.parse().unwrap();
2125
2126 if let Stmt::VarDecl {
2127 name, initializer, ..
2128 } = &stmts[0]
2129 {
2130 assert_eq!(name, "y");
2131 assert!(initializer.is_none());
2132 }
2133 }
2134
2135 #[test]
2136 fn test_pinescript_examples() {
2137 let expr = parse_expr("close[1] > close[2]").unwrap();
2139 assert!(matches!(
2140 expr,
2141 Expr::Binary {
2142 op: BinOp::Greater,
2143 ..
2144 }
2145 ));
2146
2147 let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2149 if let Expr::Binary {
2150 left, op, right, ..
2151 } = expr
2152 {
2153 assert_eq!(op, BinOp::Greater);
2154 assert!(matches!(*left, Expr::Call { .. }));
2155 assert!(matches!(*right, Expr::Call { .. }));
2156 }
2157
2158 let expr = parse_expr("(high + low) / 2").unwrap();
2160 if let Expr::Binary {
2161 left,
2162 op: div_op,
2163 right,
2164 ..
2165 } = expr
2166 {
2167 assert_eq!(div_op, BinOp::Div);
2168 assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2169 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2170 }
2171 }
2172
2173 fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2175 walkdir::WalkDir::new(dir)
2176 .into_iter()
2177 .filter_map(|e| e.ok())
2178 .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2179 .map(|e| e.path().to_path_buf())
2180 .collect()
2181 }
2182
2183 #[test]
2184 fn test_parse_testdata_files() -> eyre::Result<()> {
2185 use std::fs;
2186 use std::path::Path;
2187
2188 let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2189
2190 let filter = std::env::var("TEST_FILE").ok();
2191 let debug = std::env::var("DEBUG").is_ok();
2192 let generate_ast = std::env::var("GENERATE_AST").is_ok();
2193
2194 let pine_files = collect_pine_files_recursive(&testdata_dir);
2195
2196 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2197 let content = fs::read_to_string(path)?;
2198
2199 let mut lexer = Lexer::new(&content);
2200 let tokens = lexer.tokenize()?;
2201
2202 if debug {
2203 println!("Tokens: {:#?}", tokens);
2204 }
2205
2206 let mut parser = Parser::new(tokens);
2207 let ast = parser.parse()?;
2208
2209 if debug {
2210 let ast_json = serde_json::to_string(&ast)?;
2211 println!("AST JSON: {:?}", ast_json);
2212 }
2213
2214 let json_path = path.with_file_name(format!(
2216 "{}_ast.json",
2217 path.file_stem().unwrap().to_str().unwrap()
2218 ));
2219
2220 if generate_ast {
2221 let json = serde_json::to_string_pretty(&ast)?;
2223 fs::write(&json_path, &json)?;
2224 } else if json_path.exists() {
2225 let expected_json = fs::read_to_string(&json_path)?;
2227 let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2228
2229 if ast != expected_ast {
2230 return Err(eyre::eyre!(
2231 "AST mismatch, expected AST from {:?}",
2232 json_path
2233 ));
2234 }
2235 }
2236
2237 Ok(())
2238 };
2239
2240 for path in pine_files {
2241 let filename = path.file_name().unwrap().to_str().unwrap();
2242
2243 if let Some(ref filter_name) = filter {
2245 if filename != filter_name {
2246 continue;
2247 }
2248 }
2249
2250 if let Err(e) = process_file(&path) {
2251 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2252 }
2253 }
2254
2255 Ok(())
2256 }
2257
2258 #[test]
2259 #[ignore]
2260 fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2261 use std::fs;
2262 use std::path::Path;
2263
2264 let testdata_dir =
2265 Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2266
2267 let filter = std::env::var("TEST_FILE").ok();
2268 let debug = std::env::var("DEBUG").is_ok();
2269
2270 let pine_files = collect_pine_files_recursive(&testdata_dir);
2271
2272 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2273 let content = fs::read_to_string(path)?;
2274
2275 let mut lexer = Lexer::new(&content);
2276 let tokens = lexer.tokenize()?;
2277
2278 if debug {
2279 println!("Tokens: {:#?}", tokens);
2280 }
2281
2282 let mut parser = Parser::new(tokens);
2283 let ast = parser.parse()?;
2284
2285 let ast_json = serde_json::to_string(&ast)?;
2286
2287 if debug {
2288 println!("AST JSON: {:?}", ast_json);
2289 }
2290
2291 Ok(())
2292 };
2293
2294 for path in pine_files {
2295 let filename = path.file_name().unwrap().to_str().unwrap();
2296
2297 if let Some(ref filter_name) = filter {
2299 if filename != filter_name {
2300 continue;
2301 }
2302 }
2303
2304 if let Err(e) = process_file(&path) {
2305 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2306 }
2307 }
2308
2309 Ok(())
2310 }
2311}