1pub use pine_ast::{Argument, BinOp, Comment, Expr, Literal, Loc, Program, Stmt, UnOp, VarKind};
2use pine_lexer::{Token, TokenType};
3use thiserror::Error;
4
5#[derive(Error, Debug)]
6pub enum ParserErrorKind {
7 #[error("Unexpected token: {0:?}")]
8 UnexpectedToken(TokenType),
9
10 #[error("{expected} but found {found:?}")]
11 ExpectedToken { expected: String, found: TokenType },
12
13 #[error("Expected variable name")]
14 ExpectedVariableName,
15
16 #[error("Expected parameter name")]
17 ExpectedParameterName,
18
19 #[error("Can only call identifiers or member access")]
20 InvalidCallTarget,
21
22 #[error("Expected identifier after '.'")]
23 ExpectedIdentifierAfterDot,
24
25 #[error(transparent)]
26 Lexer(pine_lexer::LexerError),
27}
28
29#[derive(Debug)]
31pub struct ParserError {
32 pub loc: Loc,
33 pub kind: ParserErrorKind,
34}
35
36impl ParserError {
37 pub fn location(&self) -> (u32, u32) {
39 (self.loc.line, self.loc.column)
40 }
41}
42
43impl std::fmt::Display for ParserError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match &self.kind {
46 ParserErrorKind::Lexer(e) => write!(f, "{e}"),
48 kind => write!(f, "{kind} at line {}", self.loc.line),
49 }
50 }
51}
52
53impl std::error::Error for ParserError {
54 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
55 Some(&self.kind)
56 }
57}
58
59impl From<pine_lexer::LexerError> for ParserError {
60 fn from(e: pine_lexer::LexerError) -> Self {
61 let (line, column) = e.location();
62 ParserError {
63 loc: Loc::new(line, column),
64 kind: ParserErrorKind::Lexer(e),
65 }
66 }
67}
68
69impl From<ParserError> for String {
70 fn from(err: ParserError) -> String {
71 err.to_string()
72 }
73}
74
75trait TokenTypeExt {
77 fn to_binop(&self) -> Option<BinOp>;
78}
79
80impl TokenTypeExt for TokenType {
81 fn to_binop(&self) -> Option<BinOp> {
83 match self {
84 TokenType::Plus => Some(BinOp::Add),
85 TokenType::Minus => Some(BinOp::Sub),
86 TokenType::Star => Some(BinOp::Mul),
87 TokenType::Slash => Some(BinOp::Div),
88 TokenType::Percent => Some(BinOp::Mod),
89 TokenType::Equal => Some(BinOp::Eq),
90 TokenType::NotEqual => Some(BinOp::NotEq),
91 TokenType::Less => Some(BinOp::Less),
92 TokenType::Greater => Some(BinOp::Greater),
93 TokenType::LessEqual => Some(BinOp::LessEq),
94 TokenType::GreaterEqual => Some(BinOp::GreaterEq),
95 TokenType::And => Some(BinOp::And),
96 TokenType::Or => Some(BinOp::Or),
97 TokenType::PlusAssign => Some(BinOp::Add),
98 TokenType::MinusAssign => Some(BinOp::Sub),
99 TokenType::StarAssign => Some(BinOp::Mul),
100 TokenType::SlashAssign => Some(BinOp::Div),
101 _ => None,
102 }
103 }
104}
105
106pub struct Parser {
107 tokens: Vec<Token>,
108 comments: Vec<Comment>,
109 current: usize,
110 next_call_id: u32,
111}
112
113impl Parser {
114 pub fn new(tokens: Vec<Token>) -> Self {
115 let mut comments = Vec::new();
116 let tokens = tokens
117 .into_iter()
118 .filter(|t| {
119 if let TokenType::Comment(text) = &t.typ {
120 comments.push(Comment {
121 line: t.line as u32,
122 text: text.clone(),
123 });
124 }
125 !matches!(t.typ, TokenType::Comment(_) | TokenType::BlankLine)
126 })
127 .collect();
128 Self {
129 tokens,
130 comments,
131 current: 0,
132 next_call_id: 1,
133 }
134 }
135
136 pub fn parse_program(mut self) -> Result<Program, ParserError> {
137 let statements = self.parse()?;
138 Ok(Program::new(statements).with_comments(self.comments))
139 }
140
141 pub fn parse_source(source: &str) -> Result<Program, ParserError> {
143 let tokens = pine_lexer::Lexer::new(source).tokenize()?;
144 Self::new(tokens).parse_program()
145 }
146
147 fn next_call_id(&mut self) -> u32 {
148 let id = self.next_call_id;
149 self.next_call_id += 1;
150 id
151 }
152
153 fn peek(&self) -> &Token {
154 &self.tokens[self.current]
155 }
156
157 fn is_at_end(&self) -> bool {
158 matches!(self.peek().typ, TokenType::Eof)
159 }
160
161 fn advance(&mut self) -> &Token {
162 if !self.is_at_end() {
163 self.current += 1;
164 }
165 &self.tokens[self.current - 1]
166 }
167
168 fn check(&self, typ: &TokenType) -> bool {
169 !self.is_at_end() && &self.peek().typ == typ
170 }
171
172 fn match_token(&mut self, types: &[TokenType]) -> bool {
173 for typ in types {
174 if self.check(typ) {
175 self.advance();
176 return true;
177 }
178 }
179 false
180 }
181
182 fn try_parse<T, F>(&mut self, f: F) -> Option<T>
185 where
186 F: FnOnce(&mut Self) -> Result<T, ParserError>,
187 {
188 let saved_pos = self.current;
189 match f(self) {
190 Ok(val) => Some(val),
191 Err(_) => {
192 self.current = saved_pos;
193 None
194 }
195 }
196 }
197
198 fn try_parse_type_args(&mut self) -> Option<Vec<String>> {
201 self.try_parse(|p| {
202 p.consume(TokenType::Less, "Expected '<'")?;
203
204 let mut type_args = vec![];
205
206 loop {
207 let type_name = match &p.peek().typ {
209 TokenType::Ident(name) => name.clone(),
210 TokenType::Int => "int".to_string(),
211 TokenType::Float => "float".to_string(),
212 _ => return Err(p.unexpected()),
213 };
214 p.advance();
215 type_args.push(type_name);
216
217 if p.match_token(&[TokenType::Comma]) {
219 continue;
220 } else if p.match_token(&[TokenType::Greater]) {
221 break;
222 } else {
223 return Err(p.unexpected());
224 }
225 }
226
227 Ok(type_args)
228 })
229 }
230
231 fn skip_newlines(&mut self) {
233 while self.match_token(&[TokenType::Newline]) {}
234 }
235
236 fn skip_whitespace(&mut self) {
238 while self.match_token(&[TokenType::Newline, TokenType::Indent, TokenType::Dedent]) {}
239 }
240
241 fn parse_type_suffix(&mut self, type_name: String) -> Result<String, ParserError> {
247 if self.match_token(&[TokenType::LBracket]) {
248 self.consume(TokenType::RBracket, "Expected ']' after '[' in array type")?;
249 Ok(format!("{type_name}[]"))
250 } else if matches!(type_name.as_str(), "array" | "matrix" | "map")
251 && self.match_token(&[TokenType::Less])
252 {
253 let mut args = Vec::new();
254 loop {
255 args.push(self.parse_type()?);
256 if !self.match_token(&[TokenType::Comma]) {
257 break;
258 }
259 }
260 self.consume(
261 TokenType::Greater,
262 "Expected '>' after generic type arguments",
263 )?;
264 Ok(format!("{type_name}<{}>", args.join(", ")))
265 } else {
266 Ok(type_name)
267 }
268 }
269
270 fn parse_type(&mut self) -> Result<String, ParserError> {
272 let base = match &self.peek().typ {
273 TokenType::Int => "int".to_string(),
274 TokenType::Float => "float".to_string(),
275 TokenType::Ident(name) => name.clone(),
276 _ => return Err(self.unexpected()),
277 };
278 self.advance();
279 self.parse_type_suffix(base)
280 }
281
282 fn parse_indented_expression(&mut self) -> Result<Expr, ParserError> {
285 self.skip_newlines();
286
287 let has_indent = self.match_token(&[TokenType::Indent]);
289
290 let expr = self.expression()?;
291
292 if has_indent {
294 self.match_token(&[TokenType::Dedent]);
295 }
296
297 Ok(expr)
298 }
299
300 fn consume(&mut self, typ: TokenType, message: &str) -> Result<&Token, ParserError> {
301 if self.check(&typ) {
302 Ok(self.advance())
303 } else {
304 Err(self.error(ParserErrorKind::ExpectedToken {
305 expected: message.to_string(),
306 found: self.peek().typ.clone(),
307 }))
308 }
309 }
310
311 fn expect_identifier(&mut self) -> Result<String, ParserError> {
313 if let TokenType::Ident(name) = &self.peek().typ {
314 let name = name.clone();
315 self.advance();
316 Ok(name)
317 } else {
318 Err(self.error(ParserErrorKind::ExpectedVariableName))
319 }
320 }
321
322 fn parse_indented_fields<T, F>(&mut self, parse_field: F) -> Result<Vec<T>, ParserError>
324 where
325 F: Fn(&mut Self) -> Result<T, ParserError>,
326 {
327 let mut fields = Vec::new();
328
329 loop {
330 self.skip_newlines();
332
333 if self.check(&TokenType::Dedent) {
335 self.advance();
336 break;
337 }
338
339 if self.is_at_end() {
341 break;
342 }
343
344 fields.push(parse_field(self)?);
346 }
347
348 Ok(fields)
349 }
350
351 fn skip_newlines_and_indent(&mut self) {
353 self.skip_newlines();
354 self.match_token(&[TokenType::Indent]);
355 }
356
357 fn skip_newlines_and_dedent(&mut self) {
359 self.skip_newlines();
360 self.match_token(&[TokenType::Dedent]);
361 }
362
363 fn try_consume_indent_if_followed_by(&mut self, expected: &TokenType) {
365 if self.check(&TokenType::Indent) {
366 self.try_parse(|p| {
367 p.advance(); if p.check(expected) {
369 Ok(())
370 } else {
371 Err(p.unexpected())
372 }
373 });
374 }
375 }
376
377 fn try_consume_layout_token_if_followed_by(&mut self, expected: &[TokenType]) {
379 if self.check(&TokenType::Indent) || self.check(&TokenType::Dedent) {
380 self.try_parse(|p| {
381 p.advance(); for typ in expected {
383 if p.check(typ) {
384 return Ok(());
385 }
386 }
387 Err(p.unexpected())
388 });
389 }
390 }
391
392 fn parse_optional_type_qualifier(&mut self) -> Option<pine_ast::TypeQualifier> {
394 use pine_ast::TypeQualifier;
395 if self.match_token(&[TokenType::Const]) {
396 Some(TypeQualifier::Const)
397 } else if let TokenType::Ident(name) = &self.peek().typ {
398 match name.as_str() {
399 "input" => {
400 self.advance();
401 Some(TypeQualifier::Input)
402 }
403 "simple" => {
404 self.advance();
405 Some(TypeQualifier::Simple)
406 }
407 "series" => {
408 self.advance();
409 Some(TypeQualifier::Series)
410 }
411 _ => None,
412 }
413 } else {
414 None
415 }
416 }
417
418 fn parse_optional_type_annotation(&mut self) -> Option<String> {
422 if self.match_token(&[TokenType::Int, TokenType::Float]) {
423 let type_name = self.tokens[self.current - 1].lexeme.clone();
424 self.parse_type_suffix(type_name).ok()
426 } else if let TokenType::Ident(type_name) = &self.peek().typ {
427 let type_name = type_name.clone();
428 self.try_parse(|p| {
429 p.advance(); let final_type = p.parse_type_suffix(type_name.clone())?;
433
434 if !matches!(p.peek().typ, TokenType::Ident(_)) {
436 return Err(p.error(ParserErrorKind::ExpectedVariableName));
437 }
438
439 Ok(final_type)
440 })
441 } else {
442 None
443 }
444 }
445
446 fn parse_comma_separated<T, F>(
449 &mut self,
450 closing_delimiter: &TokenType,
451 parse_item: F,
452 ) -> Result<Vec<T>, ParserError>
453 where
454 F: Fn(&mut Self) -> Result<T, ParserError>,
455 {
456 let mut items = vec![];
457
458 if !self.check(closing_delimiter) {
459 loop {
460 items.push(parse_item(self)?);
461
462 self.skip_newlines();
464
465 if !self.match_token(&[TokenType::Comma]) {
466 break;
467 }
468
469 self.skip_newlines_and_indent();
471 }
472 }
473
474 Ok(items)
475 }
476
477 pub fn parse(&mut self) -> Result<Vec<Stmt>, ParserError> {
479 let mut statements = vec![];
480
481 while !self.is_at_end() {
482 self.skip_whitespace();
484
485 if self.is_at_end() {
487 break;
488 }
489
490 statements.push(self.declaration()?);
491 }
492
493 Ok(statements)
494 }
495
496 fn cur_loc(&self) -> Loc {
499 let token = self.peek();
500 Loc::new(token.line as u32, token.column as u32)
501 }
502
503 fn prev_loc(&self) -> Loc {
505 let token = &self.tokens[self.current.saturating_sub(1)];
506 Loc::new(token.line as u32, token.column as u32)
507 }
508
509 fn error(&self, kind: ParserErrorKind) -> ParserError {
511 ParserError {
512 loc: self.cur_loc(),
513 kind,
514 }
515 }
516
517 fn unexpected(&self) -> ParserError {
519 self.error(ParserErrorKind::UnexpectedToken(self.peek().typ.clone()))
520 }
521
522 fn declaration(&mut self) -> Result<Stmt, ParserError> {
523 let type_qualifier = self.parse_optional_type_qualifier();
525
526 let var_kind = if self.match_token(&[TokenType::Varip]) {
528 VarKind::Varip
529 } else if self.match_token(&[TokenType::Var]) {
530 VarKind::Var
531 } else if type_qualifier.is_some() {
532 VarKind::Plain
535 } else {
536 return self.check_type_annotated_declaration();
538 };
539
540 let type_annotation = self.parse_optional_type_annotation();
542 self.typed_var_declaration_with_qualifier(type_qualifier, type_annotation, var_kind)
543 }
544
545 fn check_type_annotated_declaration(&mut self) -> Result<Stmt, ParserError> {
546 if self.match_token(&[TokenType::Type]) {
548 return self.type_declaration(false);
549 }
550
551 if self.match_token(&[TokenType::Enum]) {
553 return self.enum_declaration(false);
554 }
555
556 if self.match_token(&[TokenType::Method]) {
558 return self.method_declaration(false);
559 }
560
561 if self.match_token(&[TokenType::Int, TokenType::Float]) {
563 let type_name = self.tokens[self.current - 1].lexeme.clone();
564 let type_name = self.parse_type_suffix(type_name)?;
566 return self.typed_var_declaration(Some(type_name), VarKind::Plain);
567 }
568
569 if let Some(type_annotation) = self.parse_optional_type_annotation() {
571 return self.typed_var_declaration(Some(type_annotation), VarKind::Plain);
572 }
573
574 self.statement()
575 }
576
577 fn type_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
578 let loc = self.cur_loc();
580 let type_name = self.expect_identifier()?;
581
582 self.consume(TokenType::Newline, "Expected newline after type name")?;
584
585 self.consume(TokenType::Indent, "Expected indent for type fields")?;
587
588 let fields = self.parse_indented_fields(|p| {
590 let type_qualifier = p.parse_optional_type_qualifier();
592
593 let field_type = p.parse_type()?;
597
598 let field_loc = p.cur_loc();
600 let field_name = p.expect_identifier()?;
601
602 let default_value = if p.match_token(&[TokenType::Assign]) {
604 Some(p.expression()?)
605 } else {
606 None
607 };
608
609 Ok(pine_ast::TypeField {
610 name: field_name,
611 type_qualifier,
612 type_annotation: field_type,
613 default_value,
614 loc: field_loc,
615 })
616 })?;
617
618 Ok(Stmt::TypeDecl {
619 name: type_name,
620 fields,
621 export,
622 loc,
623 })
624 }
625
626 fn enum_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
627 let loc = self.cur_loc();
629 let enum_name = self.expect_identifier()?;
630
631 self.consume(TokenType::Newline, "Expected newline after enum name")?;
633
634 self.consume(TokenType::Indent, "Expected indent for enum fields")?;
636
637 let fields = self.parse_indented_fields(|p| {
639 let field_loc = p.cur_loc();
641 let field_name = p.expect_identifier()?;
642
643 let title = if p.match_token(&[TokenType::Assign]) {
645 if let TokenType::String(s) = &p.peek().typ {
647 let s = s.clone();
648 p.advance();
649 Some(s)
650 } else {
651 return Err(p.unexpected());
652 }
653 } else {
654 None
655 };
656
657 Ok(pine_ast::EnumField {
658 name: field_name,
659 title,
660 loc: field_loc,
661 })
662 })?;
663
664 Ok(Stmt::EnumDecl {
665 name: enum_name,
666 fields,
667 export,
668 loc,
669 })
670 }
671
672 fn export_statement(&mut self) -> Result<Stmt, ParserError> {
673 if self.match_token(&[TokenType::Type]) {
675 return self.type_declaration(true);
676 }
677
678 if self.match_token(&[TokenType::Enum]) {
680 return self.enum_declaration(true);
681 }
682
683 let is_method = self.match_token(&[TokenType::Method]);
686
687 if is_method {
688 return self.method_declaration(true);
689 }
690
691 let loc = self.cur_loc();
693 let func_name = self.expect_identifier()?;
694
695 if self.check(&TokenType::LParen) {
697 self.advance(); let params = self.function_params()?;
701 self.consume(TokenType::RParen, "Expected ')' after function parameters")?;
702 self.consume(TokenType::Arrow, "Expected '=>'")?;
703
704 self.match_token(&[TokenType::Newline]);
706
707 let body = self.parse_block()?;
709
710 Ok(Stmt::FunctionDecl {
711 name: func_name,
712 params,
713 body,
714 export: true,
715 loc,
716 })
717 } else {
718 Ok(Stmt::Export {
720 item: pine_ast::ExportItem::Type(func_name),
721 })
722 }
723 }
724
725 fn import_statement(&mut self) -> Result<Stmt, ParserError> {
726 let path = if let TokenType::Ident(p) = &self.peek().typ {
728 let mut path_parts = vec![p.clone()];
729 self.advance();
730
731 while self.match_token(&[TokenType::Slash]) {
733 if let TokenType::Ident(part) = &self.peek().typ {
734 path_parts.push(part.clone());
735 self.advance();
736 } else if let TokenType::IntLiteral(n) = &self.peek().typ {
737 path_parts.push(n.to_string());
739 self.advance();
740 } else {
741 return Err(self.unexpected());
742 }
743 }
744
745 path_parts.join("/")
746 } else {
747 return Err(self.error(ParserErrorKind::ExpectedVariableName));
748 };
749
750 if let TokenType::Ident(kw) = &self.peek().typ {
752 if kw != "as" {
753 return Err(self.unexpected());
754 }
755 self.advance();
756 } else {
757 return Err(self.unexpected());
758 }
759
760 let loc = self.cur_loc();
762 let alias = self.expect_identifier()?;
763
764 Ok(Stmt::Import { path, alias, loc })
765 }
766
767 fn method_declaration(&mut self, export: bool) -> Result<Stmt, ParserError> {
768 let loc = self.cur_loc();
770 let method_name = self.expect_identifier()?;
771
772 self.consume(TokenType::LParen, "Expected '(' after method name")?;
774
775 let mut params = Vec::new();
777
778 if !self.check(&TokenType::RParen) {
779 loop {
780 let type_qualifier = self.parse_optional_type_qualifier();
782
783 let type_annotation = self.parse_optional_type_annotation();
785
786 let param_loc = self.cur_loc();
788 let param_name = self.expect_identifier()?;
789
790 let default_value = if self.match_token(&[TokenType::Assign]) {
792 Some(self.expression()?)
793 } else {
794 None
795 };
796
797 params.push(pine_ast::MethodParam {
798 type_qualifier,
799 type_annotation,
800 name: param_name,
801 default_value,
802 loc: param_loc,
803 });
804
805 if !self.match_token(&[TokenType::Comma]) {
806 break;
807 }
808 }
809 }
810
811 self.consume(TokenType::RParen, "Expected ')' after parameters")?;
812
813 self.consume(TokenType::Arrow, "Expected '=>' after method parameters")?;
815
816 self.match_token(&[TokenType::Newline]);
818
819 let body = self.parse_block()?;
821
822 Ok(Stmt::MethodDecl {
823 name: method_name,
824 params,
825 body,
826 export,
827 loc,
828 })
829 }
830
831 fn typed_var_declaration(
832 &mut self,
833 type_annotation: Option<String>,
834 var_kind: VarKind,
835 ) -> Result<Stmt, ParserError> {
836 self.typed_var_declaration_with_qualifier(None, type_annotation, var_kind)
837 }
838
839 fn typed_var_declaration_with_qualifier(
840 &mut self,
841 type_qualifier: Option<pine_ast::TypeQualifier>,
842 type_annotation: Option<String>,
843 var_kind: VarKind,
844 ) -> Result<Stmt, ParserError> {
845 let loc = self.cur_loc();
846 let name = self.expect_identifier()?;
847
848 let initializer = if self.match_token(&[TokenType::Assign]) {
849 Some(self.parse_indented_expression()?)
850 } else {
851 None
852 };
853
854 Ok(Stmt::VarDecl {
855 name,
856 type_qualifier,
857 type_annotation,
858 initializer,
859 var_kind,
860 loc,
861 })
862 }
863
864 fn statement(&mut self) -> Result<Stmt, ParserError> {
865 if self.match_token(&[TokenType::Export]) {
867 return self.export_statement();
868 }
869
870 if self.match_token(&[TokenType::Import]) {
872 return self.import_statement();
873 }
874
875 if self.match_token(&[TokenType::If]) {
877 return self.if_statement();
878 }
879
880 if self.match_token(&[TokenType::For]) {
882 return self.for_statement();
883 }
884
885 if self.match_token(&[TokenType::While]) {
887 return self.while_statement();
888 }
889
890 if self.match_token(&[TokenType::Break]) {
892 return Ok(Stmt::Break {
893 loc: self.prev_loc(),
894 });
895 }
896
897 if self.match_token(&[TokenType::Continue]) {
899 return Ok(Stmt::Continue {
900 loc: self.prev_loc(),
901 });
902 }
903
904 if self.check(&TokenType::LBracket) {
907 let tuple_loc = self.cur_loc();
908 if let Some((names, value)) = self.try_parse(|p| {
909 p.advance(); let mut names = vec![];
912
913 if !p.check(&TokenType::RBracket) {
915 loop {
916 if let TokenType::Ident(name) = &p.peek().typ {
917 names.push(name.clone());
918 p.advance();
919 } else {
920 return Err(p.error(ParserErrorKind::ExpectedVariableName));
922 }
923
924 if !p.match_token(&[TokenType::Comma]) {
925 break;
926 }
927 }
928 }
929
930 p.consume(TokenType::RBracket, "Expected ']' in tuple destructuring")?;
931 p.consume(TokenType::Assign, "Expected '=' after tuple pattern")?;
932
933 p.skip_newlines();
935
936 let value = p.expression()?;
937
938 Ok((names, value))
939 }) {
940 return Ok(Stmt::TupleAssignment {
941 names,
942 value,
943 loc: tuple_loc,
944 });
945 }
946 }
947
948 if let TokenType::Ident(name) = &self.peek().typ {
953 let name = name.clone();
954 let name_loc = self.cur_loc();
955
956 if let Some((param_structs, body)) = self.try_parse(|p| {
958 p.advance(); p.consume(TokenType::LParen, "Expected '('")?;
960
961 let params = p.function_params()?;
962 p.consume(TokenType::RParen, "Expected ')' after function parameters")?;
963 p.consume(TokenType::Arrow, "Expected '=>'")?;
964
965 p.match_token(&[TokenType::Newline]);
967
968 let body = p.parse_block()?;
970
971 Ok((params, body))
972 }) {
973 let initializer = Some(Expr::Function {
975 params: param_structs,
976 body,
977 });
978 return Ok(Stmt::VarDecl {
979 name,
980 type_qualifier: None,
981 type_annotation: None,
982 initializer,
983 var_kind: VarKind::Plain,
984 loc: name_loc,
985 });
986 }
987
988 if let Some(stmt) = self.try_parse(|p| {
990 p.advance(); if p.match_token(&[TokenType::Assign]) {
993 let initializer = Some(p.parse_indented_expression()?);
995
996 Ok(Stmt::VarDecl {
997 name: name.clone(),
998 type_qualifier: None,
999 type_annotation: None,
1000 initializer,
1001 var_kind: VarKind::Plain,
1002 loc: name_loc,
1003 })
1004 } else if p.match_token(&[TokenType::ColonAssign]) {
1005 let value = p.parse_indented_expression()?;
1007
1008 Ok(Stmt::Assignment {
1009 target: Expr::Variable {
1010 name: name.clone(),
1011 loc: name_loc,
1012 },
1013 value,
1014 })
1015 } else if p.match_token(&[
1016 TokenType::PlusAssign,
1017 TokenType::MinusAssign,
1018 TokenType::StarAssign,
1019 TokenType::SlashAssign,
1020 ]) {
1021 let op_tok = &p.tokens[p.current - 1];
1023 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1024 let op = op_tok
1025 .typ
1026 .to_binop()
1027 .expect("compound assign token should convert to binop");
1028
1029 let right = p.parse_indented_expression()?;
1030
1031 let value = Expr::Binary {
1032 left: Box::new(Expr::Variable {
1033 name: name.clone(),
1034 loc: name_loc,
1035 }),
1036 op,
1037 right: Box::new(right),
1038 loc: op_loc,
1039 };
1040 Ok(Stmt::Assignment {
1041 target: Expr::Variable {
1042 name: name.clone(),
1043 loc: name_loc,
1044 },
1045 value,
1046 })
1047 } else {
1048 Err(p.unexpected())
1050 }
1051 }) {
1052 return Ok(stmt);
1053 }
1054 }
1055
1056 self.expression_statement()
1057 }
1058
1059 fn function_params(&mut self) -> Result<Vec<pine_ast::FunctionParam>, ParserError> {
1060 self.parse_comma_separated(&TokenType::RParen, |p| {
1061 let type_qualifier = p.parse_optional_type_qualifier();
1063
1064 let type_annotation = p.parse_optional_type_annotation();
1066
1067 let param_loc = p.cur_loc();
1068 let name = p.expect_identifier()?;
1069
1070 let default_value = if p.match_token(&[TokenType::Assign]) {
1072 Some(p.expression()?)
1073 } else {
1074 None
1075 };
1076
1077 Ok(pine_ast::FunctionParam {
1078 type_qualifier,
1079 type_annotation,
1080 name,
1081 default_value,
1082 loc: param_loc,
1083 })
1084 })
1085 }
1086
1087 fn for_statement(&mut self) -> Result<Stmt, ParserError> {
1088 if self.check(&TokenType::LBracket) {
1090 self.advance(); let loc = self.cur_loc();
1093 let index_var = self.expect_identifier()?;
1094
1095 self.consume(TokenType::Comma, "Expected ',' in for...in tuple")?;
1096
1097 let item_var = self.expect_identifier()?;
1098
1099 self.consume(TokenType::RBracket, "Expected ']' after for...in tuple")?;
1100 self.consume(TokenType::In, "Expected 'in' in for...in loop")?;
1101
1102 let collection = self.expression()?;
1103
1104 self.match_token(&[TokenType::Newline]);
1106
1107 let body = self.parse_block()?;
1108
1109 return Ok(Stmt::ForIn {
1110 index_var: Some(index_var),
1111 item_var,
1112 collection,
1113 body,
1114 loc,
1115 });
1116 }
1117
1118 let loc = self.cur_loc();
1120 let var_name = self.expect_identifier()?;
1121
1122 if self.check(&TokenType::In) {
1124 self.advance(); let collection = self.expression()?;
1127
1128 self.match_token(&[TokenType::Newline]);
1130
1131 let body = self.parse_block()?;
1132
1133 Ok(Stmt::ForIn {
1134 index_var: None,
1135 item_var: var_name,
1136 collection,
1137 body,
1138 loc,
1139 })
1140 } else {
1141 self.consume(TokenType::Assign, "Expected '=' in for loop")?;
1143 let from = self.expression()?;
1144 self.consume(TokenType::To, "Expected 'to' in for loop")?;
1145 let to = self.expression()?;
1146
1147 let step = if matches!(&self.peek().typ, TokenType::Ident(name) if name == "by") {
1150 self.advance(); Some(self.expression()?)
1152 } else {
1153 None
1154 };
1155
1156 self.match_token(&[TokenType::Newline]);
1158
1159 let body = self.parse_block()?;
1161
1162 Ok(Stmt::For {
1163 var_name,
1164 from,
1165 to,
1166 step,
1167 body,
1168 loc,
1169 })
1170 }
1171 }
1172
1173 fn while_statement(&mut self) -> Result<Stmt, ParserError> {
1174 let condition = self.expression()?;
1176
1177 self.match_token(&[TokenType::Newline]);
1179
1180 let body = self.parse_block()?;
1182
1183 Ok(Stmt::While { condition, body })
1184 }
1185
1186 fn if_statement(&mut self) -> Result<Stmt, ParserError> {
1187 let condition = self.expression()?;
1189
1190 self.match_token(&[TokenType::Newline]);
1192
1193 let then_branch = self.parse_block()?;
1195
1196 let mut else_if_branches = Vec::new();
1198
1199 loop {
1200 self.skip_newlines();
1202
1203 if self.check(&TokenType::Else) {
1205 if let Some((else_if_condition, else_if_body)) = self.try_parse(|p| {
1206 p.advance(); if p.match_token(&[TokenType::If]) {
1209 let else_if_condition = p.expression()?;
1211 p.match_token(&[TokenType::Newline]);
1212 let else_if_body = p.parse_block()?;
1213 Ok((else_if_condition, else_if_body))
1214 } else {
1215 Err(p.unexpected())
1216 }
1217 }) {
1218 else_if_branches.push((else_if_condition, else_if_body));
1219 } else {
1220 break;
1221 }
1222 } else {
1223 break;
1224 }
1225 }
1226
1227 self.skip_newlines();
1229
1230 let else_branch = if self.match_token(&[TokenType::Else]) {
1231 self.match_token(&[TokenType::Newline]);
1233
1234 Some(self.parse_block()?)
1235 } else {
1236 None
1237 };
1238
1239 Ok(Stmt::If {
1240 condition,
1241 then_branch,
1242 else_if_branches,
1243 else_branch,
1244 })
1245 }
1246
1247 fn if_expression(&mut self) -> Result<Expr, ParserError> {
1248 self.consume(TokenType::If, "Expected 'if'")?;
1250
1251 let condition = self.expression()?;
1253
1254 self.match_token(&[TokenType::Newline]);
1256
1257 self.match_token(&[TokenType::Indent]);
1259
1260 let then_expr = self.expression()?;
1262
1263 self.skip_newlines();
1265 self.match_token(&[TokenType::Dedent]);
1266
1267 let mut else_if_branches = Vec::new();
1269
1270 loop {
1271 self.skip_newlines();
1273
1274 if self.check(&TokenType::Else) {
1276 if let Some((else_if_condition, else_if_expr)) = self.try_parse(|p| {
1277 p.advance(); if p.match_token(&[TokenType::If]) {
1280 let else_if_condition = p.expression()?;
1282 p.match_token(&[TokenType::Newline]);
1283 p.match_token(&[TokenType::Indent]);
1284 let else_if_expr = p.expression()?;
1285 p.skip_newlines();
1286 p.match_token(&[TokenType::Dedent]);
1287 Ok((else_if_condition, else_if_expr))
1288 } else {
1289 Err(p.unexpected())
1290 }
1291 }) {
1292 else_if_branches.push((else_if_condition, else_if_expr));
1293 } else {
1294 break;
1295 }
1296 } else {
1297 break;
1298 }
1299 }
1300
1301 self.skip_newlines();
1303 let else_expr = if self.match_token(&[TokenType::Else]) {
1304 self.match_token(&[TokenType::Newline]);
1306
1307 self.match_token(&[TokenType::Indent]);
1309
1310 let expr = self.expression()?;
1312
1313 self.skip_newlines();
1315 self.match_token(&[TokenType::Dedent]);
1316
1317 Some(Box::new(expr))
1318 } else {
1319 None };
1321
1322 Ok(Expr::IfExpr {
1323 condition: Box::new(condition),
1324 then_expr: Box::new(then_expr),
1325 else_if_branches,
1326 else_expr,
1327 })
1328 }
1329
1330 fn parse_block(&mut self) -> Result<Vec<Stmt>, ParserError> {
1331 let mut stmts = vec![];
1332
1333 if !self.match_token(&[TokenType::Indent]) {
1335 if !self.check(&TokenType::Newline)
1338 && !self.check(&TokenType::Else)
1339 && !self.is_at_end()
1340 {
1341 stmts.push(self.declaration()?);
1342 }
1343 return Ok(stmts);
1344 }
1345
1346 loop {
1348 self.skip_newlines();
1350
1351 if self.check(&TokenType::Else) {
1353 break;
1354 }
1355
1356 if self.check(&TokenType::Dedent) {
1358 self.advance(); self.skip_newlines();
1362 if self.check(&TokenType::Else) {
1363 break;
1364 }
1365
1366 break;
1368 }
1369
1370 if self.is_at_end() {
1372 break;
1373 }
1374
1375 stmts.push(self.declaration()?);
1377 }
1378
1379 Ok(stmts)
1380 }
1381
1382 fn expression_statement(&mut self) -> Result<Stmt, ParserError> {
1383 let expr = self.expression()?;
1384
1385 if self.match_token(&[TokenType::ColonAssign]) {
1387 let value = self.parse_indented_expression()?;
1388 return Ok(Stmt::Assignment {
1389 target: expr,
1390 value,
1391 });
1392 }
1393
1394 Ok(Stmt::Expression(expr))
1395 }
1396
1397 fn expression(&mut self) -> Result<Expr, ParserError> {
1399 self.ternary()
1400 }
1401
1402 fn binary_left_assoc(
1404 &mut self,
1405 operators: &[TokenType],
1406 next_precedence: fn(&mut Self) -> Result<Expr, ParserError>,
1407 ) -> Result<Expr, ParserError> {
1408 let mut expr = next_precedence(self)?;
1409
1410 loop {
1411 self.skip_newlines();
1413
1414 if !self.match_token(operators) {
1415 break;
1416 }
1417
1418 let op_tok = &self.tokens[self.current - 1];
1419 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1420 let op = op_tok
1421 .typ
1422 .to_binop()
1423 .expect("matched operator token should convert to binop");
1424
1425 self.skip_newlines_and_indent();
1427
1428 let right = next_precedence(self)?;
1429 expr = Expr::Binary {
1430 left: Box::new(expr),
1431 op,
1432 right: Box::new(right),
1433 loc: op_loc,
1434 };
1435 }
1436
1437 Ok(expr)
1438 }
1439
1440 fn ternary(&mut self) -> Result<Expr, ParserError> {
1441 if self.check(&TokenType::If) {
1443 return self.if_expression();
1444 }
1445
1446 let mut expr = self.logical_or()?;
1447
1448 self.skip_newlines();
1450
1451 self.try_consume_indent_if_followed_by(&TokenType::Question);
1453
1454 if self.match_token(&[TokenType::Question]) {
1455 self.skip_newlines_and_indent();
1457
1458 let then_expr = self.expression()?;
1459
1460 self.skip_newlines();
1462
1463 self.try_consume_indent_if_followed_by(&TokenType::Colon);
1465
1466 self.consume(TokenType::Colon, "Expected ':' in ternary expression")?;
1467
1468 self.skip_newlines_and_indent();
1470
1471 let else_expr = self.expression()?;
1472 expr = Expr::Ternary {
1473 condition: Box::new(expr),
1474 then_expr: Box::new(then_expr),
1475 else_expr: Box::new(else_expr),
1476 };
1477 }
1478
1479 Ok(expr)
1480 }
1481
1482 fn logical_or(&mut self) -> Result<Expr, ParserError> {
1483 self.binary_left_assoc(&[TokenType::Or], Self::logical_and)
1484 }
1485
1486 fn logical_and(&mut self) -> Result<Expr, ParserError> {
1487 self.binary_left_assoc(&[TokenType::And], Self::equality)
1488 }
1489
1490 fn equality(&mut self) -> Result<Expr, ParserError> {
1491 self.binary_left_assoc(&[TokenType::Equal, TokenType::NotEqual], Self::comparison)
1492 }
1493
1494 fn comparison(&mut self) -> Result<Expr, ParserError> {
1495 self.binary_left_assoc(
1496 &[
1497 TokenType::Greater,
1498 TokenType::Less,
1499 TokenType::GreaterEqual,
1500 TokenType::LessEqual,
1501 ],
1502 Self::term,
1503 )
1504 }
1505
1506 fn term(&mut self) -> Result<Expr, ParserError> {
1507 let mut expr = self.factor()?;
1508
1509 loop {
1510 self.skip_newlines();
1512
1513 self.try_consume_layout_token_if_followed_by(&[TokenType::Plus, TokenType::Minus]);
1515
1516 if !self.match_token(&[TokenType::Plus, TokenType::Minus]) {
1517 break;
1518 }
1519
1520 let op_tok = &self.tokens[self.current - 1];
1521 let op_loc = Loc::new(op_tok.line as u32, op_tok.column as u32);
1522 let op = op_tok
1523 .typ
1524 .to_binop()
1525 .expect("term token should convert to binop");
1526 self.skip_newlines_and_indent();
1528 let right = self.factor()?;
1529 expr = Expr::Binary {
1530 left: Box::new(expr),
1531 op,
1532 right: Box::new(right),
1533 loc: op_loc,
1534 };
1535 }
1536
1537 Ok(expr)
1538 }
1539
1540 fn factor(&mut self) -> Result<Expr, ParserError> {
1541 self.binary_left_assoc(
1542 &[TokenType::Star, TokenType::Slash, TokenType::Percent],
1543 Self::unary,
1544 )
1545 }
1546
1547 fn unary(&mut self) -> Result<Expr, ParserError> {
1548 if self.match_token(&[TokenType::Minus]) {
1549 let expr = self.unary()?;
1550 return Ok(Expr::Unary {
1551 op: UnOp::Neg,
1552 expr: Box::new(expr),
1553 });
1554 }
1555
1556 if self.match_token(&[TokenType::Not]) {
1557 let expr = self.unary()?;
1558 return Ok(Expr::Unary {
1559 op: UnOp::Not,
1560 expr: Box::new(expr),
1561 });
1562 }
1563
1564 self.postfix()
1565 }
1566
1567 fn postfix(&mut self) -> Result<Expr, ParserError> {
1568 let mut expr = self.primary()?;
1569
1570 if matches!(expr, Expr::Switch { .. } | Expr::IfExpr { .. }) {
1575 return Ok(expr);
1576 }
1577
1578 loop {
1579 if self.match_token(&[TokenType::Dot]) {
1580 let member = match &self.peek().typ {
1583 TokenType::Ident(name) => {
1584 let name = name.clone();
1585 self.advance();
1586 name
1587 }
1588 TokenType::Int => {
1589 self.advance();
1590 "int".to_string()
1591 }
1592 TokenType::Float => {
1593 self.advance();
1594 "float".to_string()
1595 }
1596 _ => {
1597 let lexeme = self.peek().lexeme.clone();
1599 if !lexeme.is_empty() {
1600 self.advance();
1601 lexeme
1602 } else {
1603 return Err(self.error(ParserErrorKind::ExpectedIdentifierAfterDot));
1604 }
1605 }
1606 };
1607 let member_loc = self.prev_loc();
1608 expr = Expr::MemberAccess {
1609 object: Box::new(expr),
1610 member,
1611 member_loc,
1612 };
1613 } else if self.match_token(&[TokenType::LBracket]) {
1614 let index = self.expression()?;
1616 self.consume(TokenType::RBracket, "Expected ']'")?;
1617 expr = Expr::Index {
1618 expr: Box::new(expr),
1619 index: Box::new(index),
1620 id: self.next_call_id(),
1621 };
1622 } else if self.check(&TokenType::Less) {
1623 let type_args = self.try_parse_type_args().unwrap_or_default();
1627
1628 if self.match_token(&[TokenType::LParen]) {
1630 let lparen = &self.tokens[self.current - 1];
1631 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1632 let id = self.next_call_id();
1633 let args = self.arguments()?;
1634 self.consume(TokenType::RParen, "Expected ')'")?;
1635 expr = Expr::Call {
1636 callee: Box::new(expr),
1637 type_args,
1638 args,
1639 id,
1640 loc: call_loc,
1641 };
1642 } else {
1643 break;
1645 }
1646 } else if self.match_token(&[TokenType::LParen]) {
1647 let lparen = &self.tokens[self.current - 1];
1649 let call_loc = Loc::new(lparen.line as u32, lparen.column as u32);
1650 let id = self.next_call_id();
1651 let args = self.arguments()?;
1652 self.consume(TokenType::RParen, "Expected ')'")?;
1653 expr = Expr::Call {
1654 callee: Box::new(expr),
1655 type_args: vec![],
1656 args,
1657 id,
1658 loc: call_loc,
1659 };
1660 } else {
1661 break;
1662 }
1663 }
1664
1665 Ok(expr)
1666 }
1667
1668 fn arguments(&mut self) -> Result<Vec<Argument>, ParserError> {
1669 let mut args = vec![];
1670
1671 if !self.check(&TokenType::RParen) {
1672 loop {
1673 let key_name = match &self.peek().typ {
1678 TokenType::Ident(name) => Some(name.clone()),
1679 TokenType::Type => Some("type".to_string()),
1680 _ => None,
1681 };
1682 if let Some(name) = key_name {
1683 if let Some((name, value)) = self.try_parse(|p| {
1684 p.advance(); if p.check(&TokenType::Assign) {
1686 p.advance(); let value = p.expression()?;
1689 Ok((name.clone(), value))
1690 } else {
1691 Err(p.unexpected())
1692 }
1693 }) {
1694 args.push(Argument::Named { name, value });
1695 } else {
1696 let expr = self.expression()?;
1698 args.push(Argument::Positional(expr));
1699 }
1700 } else {
1701 let expr = self.expression()?;
1702 args.push(Argument::Positional(expr));
1703 }
1704
1705 if !self.match_token(&[TokenType::Comma]) {
1706 break;
1707 }
1708 }
1709 }
1710
1711 Ok(args)
1712 }
1713
1714 fn primary(&mut self) -> Result<Expr, ParserError> {
1715 if let TokenType::IntLiteral(n) = self.peek().typ {
1716 self.advance();
1717 return Ok(Expr::Literal(Literal::Int(n)));
1718 }
1719
1720 if let TokenType::Number(n) = self.peek().typ {
1721 self.advance();
1722 return Ok(Expr::Literal(Literal::Number(n)));
1723 }
1724
1725 if let TokenType::String(ref s) = self.peek().typ {
1726 let s = s.clone();
1727 self.advance();
1728 return Ok(Expr::Literal(Literal::String(s)));
1729 }
1730
1731 if let TokenType::Bool(b) = self.peek().typ {
1732 self.advance();
1733 return Ok(Expr::Literal(Literal::Bool(b)));
1734 }
1735
1736 if let TokenType::HexColor(ref hex) = self.peek().typ {
1737 let hex = hex.clone();
1738 self.advance();
1739 return Ok(Expr::Literal(Literal::HexColor(hex)));
1740 }
1741
1742 if self.match_token(&[TokenType::Na]) {
1744 return Ok(Expr::Literal(Literal::Na));
1745 }
1746
1747 if self.match_token(&[TokenType::Int, TokenType::Float]) {
1750 let name = self.tokens[self.current - 1].lexeme.clone();
1751 return Ok(Expr::Variable {
1752 name,
1753 loc: self.prev_loc(),
1754 });
1755 }
1756
1757 if let TokenType::Ident(ref name) = self.peek().typ {
1758 let name = name.clone();
1759 let loc = self.cur_loc();
1760 self.advance();
1761 return Ok(Expr::Variable { name, loc });
1762 }
1763
1764 if self.match_token(&[TokenType::LParen]) {
1765 self.skip_newlines();
1767 let had_indent = self.match_token(&[TokenType::Indent]);
1768
1769 let expr = self.expression()?;
1770
1771 self.skip_newlines();
1773 if had_indent {
1774 self.match_token(&[TokenType::Dedent]);
1775 }
1776
1777 self.consume(TokenType::RParen, "Expected ')'")?;
1778 return Ok(expr);
1779 }
1780
1781 if self.match_token(&[TokenType::Switch]) {
1783 let value = if self.check(&TokenType::Newline) {
1787 Box::new(Expr::Literal(Literal::Bool(true)))
1788 } else {
1789 Box::new(self.expression()?)
1790 };
1791
1792 self.match_token(&[TokenType::Newline]);
1794
1795 let has_indent = self.match_token(&[TokenType::Indent]);
1797
1798 let mut cases = vec![];
1799
1800 loop {
1802 self.skip_newlines();
1804
1805 if self.check(&TokenType::Dedent) {
1807 if has_indent {
1808 self.advance(); }
1810 break;
1811 }
1812
1813 if self.is_at_end() {
1815 break;
1816 }
1817
1818 if self.match_token(&[TokenType::Arrow]) {
1820 self.skip_newlines();
1822
1823 let result = self.expression()?;
1825
1826 let default_pattern = Expr::Literal(Literal::Bool(true));
1828 cases.push((default_pattern, result));
1829 continue;
1830 }
1831
1832 if let Some((pattern, result)) = self.try_parse(|p| {
1834 let pattern = p.expression()?;
1836
1837 if !p.match_token(&[TokenType::Arrow]) {
1839 return Err(p.unexpected());
1840 }
1841
1842 p.skip_newlines();
1844
1845 let result = p.expression()?;
1847 Ok((pattern, result))
1848 }) {
1849 cases.push((pattern, result));
1850 } else {
1851 break;
1852 }
1853 }
1854
1855 return Ok(Expr::Switch { value, cases });
1856 }
1857
1858 if self.match_token(&[TokenType::LBracket]) {
1860 self.skip_newlines_and_indent();
1862
1863 let elements = self.parse_comma_separated(&TokenType::RBracket, |p| p.expression())?;
1864
1865 self.skip_newlines_and_dedent();
1867
1868 self.consume(TokenType::RBracket, "Expected ']'")?;
1869 return Ok(Expr::Array(elements));
1870 }
1871
1872 Err(self.unexpected())
1873 }
1874}
1875
1876#[cfg(test)]
1877mod tests {
1878 use super::*;
1879 use pine_lexer::Lexer;
1880
1881 fn parse_expr(input: &str) -> eyre::Result<Expr> {
1882 let mut lexer = Lexer::new(input);
1883 let tokens = lexer.tokenize()?;
1884 let mut parser = Parser::new(tokens);
1885 let stmts = parser.parse()?;
1886
1887 if let Some(Stmt::Expression(expr)) = stmts.first() {
1888 Ok(expr.clone())
1889 } else {
1890 Err(eyre::eyre!("Expected expression statement".to_string()))
1891 }
1892 }
1893
1894 #[test]
1895 fn test_literals() {
1896 let expr = parse_expr("42").unwrap();
1898 assert_eq!(expr, Expr::Literal(Literal::Int(42)));
1899
1900 let expr = parse_expr(r#""hello""#).unwrap();
1902 assert_eq!(expr, Expr::Literal(Literal::String("hello".to_string())));
1903
1904 let expr = parse_expr("true").unwrap();
1906 assert_eq!(expr, Expr::Literal(Literal::Bool(true)));
1907 }
1908
1909 #[test]
1910 fn test_variables() {
1911 let expr = parse_expr("close").unwrap();
1912 assert_eq!(expr, Expr::var("close"));
1913
1914 let expr = parse_expr("my_var").unwrap();
1915 assert_eq!(expr, Expr::var("my_var"));
1916 }
1917
1918 #[test]
1919 fn test_historical_references() {
1920 let expr = parse_expr("close[1]").unwrap();
1922 assert!(matches!(expr, Expr::Index { .. }));
1923 if let Expr::Index {
1924 expr: base, index, ..
1925 } = expr
1926 {
1927 assert_eq!(*base, Expr::var("close"));
1928 assert_eq!(*index, Expr::Literal(Literal::Int(1)));
1929 }
1930
1931 let expr = parse_expr("high[5]").unwrap();
1933 if let Expr::Index {
1934 expr: base, index, ..
1935 } = expr
1936 {
1937 assert_eq!(*base, Expr::var("high"));
1938 assert_eq!(*index, Expr::Literal(Literal::Int(5)));
1939 }
1940 }
1941
1942 #[test]
1943 fn test_function_calls() {
1944 let expr = parse_expr("sma(close, 14)").unwrap();
1946 if let Expr::Call {
1947 callee,
1948 type_args,
1949 args,
1950 ..
1951 } = expr
1952 {
1953 assert_eq!(*callee, Expr::var("sma"));
1954 assert_eq!(type_args.len(), 0);
1955 assert_eq!(args.len(), 2);
1956 assert_eq!(args[0], Argument::Positional(Expr::var("close")));
1957 assert_eq!(
1958 args[1],
1959 Argument::Positional(Expr::Literal(Literal::Int(14)))
1960 );
1961 } else {
1962 panic!("Expected function call");
1963 }
1964
1965 let expr = parse_expr("foo()").unwrap();
1967 if let Expr::Call {
1968 callee,
1969 type_args,
1970 args,
1971 ..
1972 } = expr
1973 {
1974 assert_eq!(*callee, Expr::var("foo"));
1975 assert_eq!(type_args.len(), 0);
1976 assert_eq!(args.len(), 0);
1977 }
1978 }
1979
1980 #[test]
1981 fn test_arithmetic_expressions() {
1982 let expr = parse_expr("2 + 3").unwrap();
1984 if let Expr::Binary {
1985 left, op, right, ..
1986 } = expr
1987 {
1988 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
1989 assert_eq!(op, BinOp::Add);
1990 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
1991 }
1992
1993 let expr = parse_expr("2 + 3 * 4").unwrap();
1995 if let Expr::Binary {
1996 left,
1997 op: op1,
1998 right,
1999 ..
2000 } = expr
2001 {
2002 assert_eq!(*left, Expr::Literal(Literal::Int(2)));
2003 assert_eq!(op1, BinOp::Add);
2004 if let Expr::Binary {
2005 left: l2,
2006 op: op2,
2007 right: r2,
2008 ..
2009 } = *right
2010 {
2011 assert_eq!(*l2, Expr::Literal(Literal::Int(3)));
2012 assert_eq!(op2, BinOp::Mul);
2013 assert_eq!(*r2, Expr::Literal(Literal::Int(4)));
2014 }
2015 }
2016
2017 let expr = parse_expr("10 / 2").unwrap();
2019 if let Expr::Binary {
2020 left, op, right, ..
2021 } = expr
2022 {
2023 assert_eq!(*left, Expr::Literal(Literal::Int(10)));
2024 assert_eq!(op, BinOp::Div);
2025 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2026 }
2027
2028 let expr = parse_expr("5 - 3").unwrap();
2030 if let Expr::Binary {
2031 left, op, right, ..
2032 } = expr
2033 {
2034 assert_eq!(*left, Expr::Literal(Literal::Int(5)));
2035 assert_eq!(op, BinOp::Sub);
2036 assert_eq!(*right, Expr::Literal(Literal::Int(3)));
2037 }
2038 }
2039
2040 #[test]
2041 fn test_comparison_expressions() {
2042 let expr = parse_expr("close > open").unwrap();
2044 if let Expr::Binary {
2045 left, op, right, ..
2046 } = expr
2047 {
2048 assert_eq!(*left, Expr::var("close"));
2049 assert_eq!(op, BinOp::Greater);
2050 assert_eq!(*right, Expr::var("open"));
2051 }
2052
2053 let expr = parse_expr("rsi < 30").unwrap();
2055 if let Expr::Binary {
2056 left, op, right, ..
2057 } = expr
2058 {
2059 assert_eq!(*left, Expr::var("rsi"));
2060 assert_eq!(op, BinOp::Less);
2061 assert_eq!(*right, Expr::Literal(Literal::Int(30)));
2062 }
2063
2064 let expr = parse_expr("x == 5").unwrap();
2066 if let Expr::Binary {
2067 left, op, right, ..
2068 } = expr
2069 {
2070 assert_eq!(*left, Expr::var("x"));
2071 assert_eq!(op, BinOp::Eq);
2072 assert_eq!(*right, Expr::Literal(Literal::Int(5)));
2073 }
2074 }
2075
2076 #[test]
2077 fn test_unary_expressions() {
2078 let expr = parse_expr("-5").unwrap();
2080 if let Expr::Unary { op, expr } = expr {
2081 assert_eq!(op, UnOp::Neg);
2082 assert_eq!(*expr, Expr::Literal(Literal::Int(5)));
2083 }
2084
2085 let expr = parse_expr("--10").unwrap();
2087 if let Expr::Unary { op: op1, expr: e1 } = expr {
2088 assert_eq!(op1, UnOp::Neg);
2089 if let Expr::Unary { op: op2, expr: e2 } = *e1 {
2090 assert_eq!(op2, UnOp::Neg);
2091 assert_eq!(*e2, Expr::Literal(Literal::Int(10)));
2092 }
2093 }
2094 }
2095
2096 #[test]
2097 fn test_var_declarations() {
2098 let mut lexer = Lexer::new("var x = 10");
2099 let tokens = lexer.tokenize().unwrap();
2100 let mut parser = Parser::new(tokens);
2101 let stmts = parser.parse().unwrap();
2102
2103 assert_eq!(stmts.len(), 1);
2104 if let Stmt::VarDecl {
2105 name,
2106 type_qualifier,
2107 type_annotation,
2108 initializer,
2109 var_kind,
2110 ..
2111 } = &stmts[0]
2112 {
2113 assert_eq!(name, "x");
2114 assert_eq!(*type_qualifier, None);
2115 assert_eq!(*type_annotation, None);
2116 assert_eq!(*var_kind, VarKind::Var, "var x = 10 must be Var");
2117 assert_eq!(
2118 initializer.as_ref().unwrap(),
2119 &Expr::Literal(Literal::Int(10))
2120 );
2121 } else {
2122 panic!("Expected VarDecl");
2123 }
2124
2125 let mut lexer = Lexer::new("var y");
2127 let tokens = lexer.tokenize().unwrap();
2128 let mut parser = Parser::new(tokens);
2129 let stmts = parser.parse().unwrap();
2130
2131 if let Stmt::VarDecl {
2132 name, initializer, ..
2133 } = &stmts[0]
2134 {
2135 assert_eq!(name, "y");
2136 assert!(initializer.is_none());
2137 }
2138 }
2139
2140 #[test]
2141 fn test_pinescript_examples() {
2142 let expr = parse_expr("close[1] > close[2]").unwrap();
2144 assert!(matches!(
2145 expr,
2146 Expr::Binary {
2147 op: BinOp::Greater,
2148 ..
2149 }
2150 ));
2151
2152 let expr = parse_expr("sma(close, 14) > sma(close, 28)").unwrap();
2154 if let Expr::Binary {
2155 left, op, right, ..
2156 } = expr
2157 {
2158 assert_eq!(op, BinOp::Greater);
2159 assert!(matches!(*left, Expr::Call { .. }));
2160 assert!(matches!(*right, Expr::Call { .. }));
2161 }
2162
2163 let expr = parse_expr("(high + low) / 2").unwrap();
2165 if let Expr::Binary {
2166 left,
2167 op: div_op,
2168 right,
2169 ..
2170 } = expr
2171 {
2172 assert_eq!(div_op, BinOp::Div);
2173 assert!(matches!(*left, Expr::Binary { op: BinOp::Add, .. }));
2174 assert_eq!(*right, Expr::Literal(Literal::Int(2)));
2175 }
2176 }
2177
2178 fn collect_pine_files_recursive(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
2180 walkdir::WalkDir::new(dir)
2181 .into_iter()
2182 .filter_map(|e| e.ok())
2183 .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("pine"))
2184 .map(|e| e.path().to_path_buf())
2185 .collect()
2186 }
2187
2188 #[test]
2189 fn test_parse_testdata_files() -> eyre::Result<()> {
2190 use std::fs;
2191 use std::path::Path;
2192
2193 let testdata_dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("testdata");
2194
2195 let filter = std::env::var("TEST_FILE").ok();
2196 let debug = std::env::var("DEBUG").is_ok();
2197 let generate_ast = std::env::var("GENERATE_AST").is_ok();
2198
2199 let pine_files = collect_pine_files_recursive(&testdata_dir);
2200
2201 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2202 let content = fs::read_to_string(path)?;
2203
2204 let mut lexer = Lexer::new(&content);
2205 let tokens = lexer.tokenize()?;
2206
2207 if debug {
2208 println!("Tokens: {:#?}", tokens);
2209 }
2210
2211 let mut parser = Parser::new(tokens);
2212 let ast = parser.parse()?;
2213
2214 if debug {
2215 let ast_json = serde_json::to_string(&ast)?;
2216 println!("AST JSON: {:?}", ast_json);
2217 }
2218
2219 let json_path = path.with_file_name(format!(
2221 "{}_ast.json",
2222 path.file_stem().unwrap().to_str().unwrap()
2223 ));
2224
2225 if generate_ast {
2226 let json = serde_json::to_string_pretty(&ast)?;
2228 fs::write(&json_path, &json)?;
2229 } else if json_path.exists() {
2230 let expected_json = fs::read_to_string(&json_path)?;
2232 let expected_ast: Vec<Stmt> = serde_json::from_str(&expected_json)?;
2233
2234 if ast != expected_ast {
2235 return Err(eyre::eyre!(
2236 "AST mismatch, expected AST from {:?}",
2237 json_path
2238 ));
2239 }
2240 }
2241
2242 Ok(())
2243 };
2244
2245 for path in pine_files {
2246 let filename = path.file_name().unwrap().to_str().unwrap();
2247
2248 if let Some(ref filter_name) = filter {
2250 if filename != filter_name {
2251 continue;
2252 }
2253 }
2254
2255 if let Err(e) = process_file(&path) {
2256 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2257 }
2258 }
2259
2260 Ok(())
2261 }
2262
2263 #[test]
2264 #[ignore]
2265 fn test_parse_external_pinescript_indicators() -> eyre::Result<()> {
2266 use std::fs;
2267 use std::path::Path;
2268
2269 let testdata_dir =
2270 Path::new(env!("CARGO_MANIFEST_DIR")).join("tradingview-pinescript-indicators");
2271
2272 let filter = std::env::var("TEST_FILE").ok();
2273 let debug = std::env::var("DEBUG").is_ok();
2274
2275 let pine_files = collect_pine_files_recursive(&testdata_dir);
2276
2277 let process_file = |path: &std::path::PathBuf| -> eyre::Result<()> {
2278 let content = fs::read_to_string(path)?;
2279
2280 let mut lexer = Lexer::new(&content);
2281 let tokens = lexer.tokenize()?;
2282
2283 if debug {
2284 println!("Tokens: {:#?}", tokens);
2285 }
2286
2287 let mut parser = Parser::new(tokens);
2288 let ast = parser.parse()?;
2289
2290 let ast_json = serde_json::to_string(&ast)?;
2291
2292 if debug {
2293 println!("AST JSON: {:?}", ast_json);
2294 }
2295
2296 Ok(())
2297 };
2298
2299 for path in pine_files {
2300 let filename = path.file_name().unwrap().to_str().unwrap();
2301
2302 if let Some(ref filter_name) = filter {
2304 if filename != filter_name {
2305 continue;
2306 }
2307 }
2308
2309 if let Err(e) = process_file(&path) {
2310 return Err(eyre::eyre!("Failed to process {}: {}", filename, e));
2311 }
2312 }
2313
2314 Ok(())
2315 }
2316}