1use crate::ast::*;
2use crate::error::{ErrorKind, PerlError, PerlResult};
3use crate::interpreter::Interpreter;
4use crate::lexer::{Lexer, LITERAL_DOLLAR_IN_DQUOTE};
5use crate::token::Token;
6
7fn postfix_lbracket_is_arrow_container(expr: &Expr) -> bool {
10 matches!(
11 expr.kind,
12 ExprKind::ArrayElement { .. }
13 | ExprKind::HashElement { .. }
14 | ExprKind::ArrowDeref { .. }
15 | ExprKind::Deref {
16 kind: Sigil::Scalar,
17 ..
18 }
19 )
20}
21
22fn destructure_stmt_from_var_decls(keyword: &str, decls: Vec<VarDecl>, line: usize) -> Statement {
23 let kind = match keyword {
24 "my" => StmtKind::My(decls),
25 "mysync" => StmtKind::MySync(decls),
26 "our" => StmtKind::Our(decls),
27 "local" => StmtKind::Local(decls),
28 "state" => StmtKind::State(decls),
29 _ => unreachable!("parse_my_our_local keyword"),
30 };
31 Statement {
32 label: None,
33 kind,
34 line,
35 }
36}
37
38fn destructure_stmt_die_string(line: usize, msg: &str) -> Statement {
39 Statement {
40 label: None,
41 kind: StmtKind::Expression(Expr {
42 kind: ExprKind::Die(vec![Expr {
43 kind: ExprKind::String(msg.to_string()),
44 line,
45 }]),
46 line,
47 }),
48 line,
49 }
50}
51
52fn destructure_stmt_unless_die(line: usize, cond: Expr, msg: &str) -> Statement {
53 Statement {
54 label: None,
55 kind: StmtKind::Unless {
56 condition: cond,
57 body: vec![destructure_stmt_die_string(line, msg)],
58 else_block: None,
59 },
60 line,
61 }
62}
63
64fn destructure_expr_scalar_tmp(name: &str, line: usize) -> Expr {
65 Expr {
66 kind: ExprKind::ScalarVar(name.to_string()),
67 line,
68 }
69}
70
71fn destructure_expr_array_len(tmp: &str, line: usize) -> Expr {
72 Expr {
73 kind: ExprKind::Deref {
74 expr: Box::new(destructure_expr_scalar_tmp(tmp, line)),
75 kind: Sigil::Array,
76 },
77 line,
78 }
79}
80
81pub struct Parser {
82 tokens: Vec<(Token, usize)>,
83 pos: usize,
84 next_rate_limit_slot: u32,
86 suppress_indirect_paren_call: u32,
89 pipe_rhs_depth: u32,
95 no_pipe_forward_depth: u32,
103 suppress_scalar_hash_brace: u32,
106 next_desugar_tmp: u32,
108 error_file: String,
110 declared_subs: std::collections::HashSet<String>,
112 suppress_parenless_call: u32,
116 suppress_slash_as_div: u32,
119 pub suppress_m_regex: u32,
122}
123
124impl Parser {
125 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
126 Self::new_with_file(tokens, "-e")
127 }
128
129 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
130 Self {
131 tokens,
132 pos: 0,
133 next_rate_limit_slot: 0,
134 suppress_indirect_paren_call: 0,
135 pipe_rhs_depth: 0,
136 no_pipe_forward_depth: 0,
137 suppress_scalar_hash_brace: 0,
138 next_desugar_tmp: 0,
139 error_file: file.into(),
140 declared_subs: std::collections::HashSet::new(),
141 suppress_parenless_call: 0,
142 suppress_slash_as_div: 0,
143 suppress_m_regex: 0,
144 }
145 }
146
147 fn alloc_desugar_tmp(&mut self) -> u32 {
148 let n = self.next_desugar_tmp;
149 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
150 n
151 }
152
153 #[inline]
157 fn in_pipe_rhs(&self) -> bool {
158 self.pipe_rhs_depth > 0
159 }
160
161 fn pipe_supplies_slurped_list_operand(&self) -> bool {
163 self.in_pipe_rhs()
164 && matches!(
165 self.peek(),
166 Token::Semicolon
167 | Token::RBrace
168 | Token::RParen
169 | Token::Eof
170 | Token::Comma
171 | Token::PipeForward
172 )
173 }
174
175 #[inline]
180 fn pipe_placeholder_list(&self, line: usize) -> Expr {
181 Expr {
182 kind: ExprKind::List(vec![]),
183 line,
184 }
185 }
186
187 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
198 let line = expr.line;
199 let topic = || Expr {
200 kind: ExprKind::ScalarVar("_".into()),
201 line,
202 };
203 match expr.kind {
204 ExprKind::Bareword(ref name) => Expr {
205 kind: ExprKind::FuncCall {
206 name: name.clone(),
207 args: vec![topic()],
208 },
209 line,
210 },
211 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
213 kind: ExprKind::Unlink(vec![topic()]),
214 line,
215 },
216 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
217 kind: ExprKind::Chmod(vec![topic()]),
218 line,
219 },
220 ExprKind::Stat(_) => expr,
222 ExprKind::Lstat(_) => expr,
223 ExprKind::Readlink(_) => expr,
224 ExprKind::ScalarReverse(ref inner) => {
226 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
227 Expr {
228 kind: ExprKind::ScalarReverse(Box::new(topic())),
229 line,
230 }
231 } else {
232 expr
233 }
234 }
235 _ => expr,
236 }
237 }
238
239 fn parse_assign_expr_stop_at_pipe(&mut self) -> PerlResult<Expr> {
247 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
248 let r = self.parse_assign_expr();
249 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
250 r
251 }
252
253 fn syntax_err(&self, message: impl Into<String>, line: usize) -> PerlError {
254 PerlError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
255 }
256
257 fn alloc_rate_limit_slot(&mut self) -> u32 {
258 let s = self.next_rate_limit_slot;
259 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
260 s
261 }
262
263 fn peek(&self) -> &Token {
264 self.tokens
265 .get(self.pos)
266 .map(|(t, _)| t)
267 .unwrap_or(&Token::Eof)
268 }
269
270 fn peek_line(&self) -> usize {
271 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
272 }
273
274 fn peek_at(&self, offset: usize) -> &Token {
275 self.tokens
276 .get(self.pos + offset)
277 .map(|(t, _)| t)
278 .unwrap_or(&Token::Eof)
279 }
280
281 fn advance(&mut self) -> (Token, usize) {
282 let tok = self
283 .tokens
284 .get(self.pos)
285 .cloned()
286 .unwrap_or((Token::Eof, 0));
287 self.pos += 1;
288 tok
289 }
290
291 fn prev_line(&self) -> usize {
293 if self.pos > 0 {
294 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
295 } else {
296 0
297 }
298 }
299
300 fn expect(&mut self, expected: &Token) -> PerlResult<usize> {
301 let (tok, line) = self.advance();
302 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
303 Ok(line)
304 } else {
305 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
306 }
307 }
308
309 fn eat(&mut self, expected: &Token) -> bool {
310 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
311 self.advance();
312 true
313 } else {
314 false
315 }
316 }
317
318 fn at_eof(&self) -> bool {
319 matches!(self.peek(), Token::Eof)
320 }
321
322 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
324 matches!(
325 tok,
326 Token::RParen
327 | Token::Semicolon
328 | Token::Comma
329 | Token::RBrace
330 | Token::Eof
331 | Token::LogAnd
332 | Token::LogOr
333 | Token::LogAndWord
334 | Token::LogOrWord
335 | Token::PipeForward
336 )
337 }
338
339 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
343 if crate::compat_mode() {
345 return false;
346 }
347 if self.peek_line() == stmt_line {
348 return false;
349 }
350 matches!(
351 self.peek(),
352 Token::Ident(ref kw) if matches!(kw.as_str(),
353 "use" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
354 | "if" | "unless" | "while" | "until" | "for" | "foreach"
355 | "return" | "last" | "next" | "redo" | "package" | "require"
356 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
357 )
358 )
359 }
360
361 pub fn parse_program(&mut self) -> PerlResult<Program> {
364 let statements = self.parse_statements()?;
365 Ok(Program { statements })
366 }
367
368 pub fn parse_statements(&mut self) -> PerlResult<Vec<Statement>> {
370 let mut statements = Vec::new();
371 while !self.at_eof() {
372 if matches!(self.peek(), Token::Semicolon) {
373 let line = self.peek_line();
374 self.advance();
375 statements.push(Statement {
376 label: None,
377 kind: StmtKind::Empty,
378 line,
379 });
380 continue;
381 }
382 statements.push(self.parse_statement()?);
383 }
384 Ok(statements)
385 }
386
387 fn parse_statement(&mut self) -> PerlResult<Statement> {
390 let line = self.peek_line();
391
392 let label = match self.peek().clone() {
395 Token::Ident(_) => {
396 if matches!(self.peek_at(1), Token::Colon)
397 && !matches!(self.peek_at(2), Token::Colon)
398 {
399 let (tok, _) = self.advance();
400 let l = match tok {
401 Token::Ident(l) => l,
402 _ => unreachable!(),
403 };
404 self.advance(); Some(l)
406 } else {
407 None
408 }
409 }
410 _ => None,
411 };
412
413 let mut stmt = match self.peek().clone() {
414 Token::FormatDecl { .. } => {
415 let tok_line = self.peek_line();
416 let (tok, _) = self.advance();
417 match tok {
418 Token::FormatDecl { name, lines } => Statement {
419 label: label.clone(),
420 kind: StmtKind::FormatDecl { name, lines },
421 line: tok_line,
422 },
423 _ => unreachable!(),
424 }
425 }
426 Token::Ident(ref kw) => match kw.as_str() {
427 "if" => self.parse_if()?,
428 "unless" => self.parse_unless()?,
429 "while" => {
430 let mut s = self.parse_while()?;
431 if let StmtKind::While {
432 label: ref mut lbl, ..
433 } = s.kind
434 {
435 *lbl = label.clone();
436 }
437 s
438 }
439 "until" => {
440 let mut s = self.parse_until()?;
441 if let StmtKind::Until {
442 label: ref mut lbl, ..
443 } = s.kind
444 {
445 *lbl = label.clone();
446 }
447 s
448 }
449 "for" => {
450 let mut s = self.parse_for_or_foreach()?;
451 match s.kind {
452 StmtKind::For {
453 label: ref mut lbl, ..
454 }
455 | StmtKind::Foreach {
456 label: ref mut lbl, ..
457 } => *lbl = label.clone(),
458 _ => {}
459 }
460 s
461 }
462 "foreach" => {
463 let mut s = self.parse_foreach()?;
464 if let StmtKind::Foreach {
465 label: ref mut lbl, ..
466 } = s.kind
467 {
468 *lbl = label.clone();
469 }
470 s
471 }
472 "sub" | "fn" => self.parse_sub_decl()?,
473 "struct" => {
474 if crate::compat_mode() {
475 return Err(self.syntax_err(
476 "`struct` is a stryke extension (disabled by --compat)",
477 self.peek_line(),
478 ));
479 }
480 self.parse_struct_decl()?
481 }
482 "enum" => {
483 if crate::compat_mode() {
484 return Err(self.syntax_err(
485 "`enum` is a stryke extension (disabled by --compat)",
486 self.peek_line(),
487 ));
488 }
489 self.parse_enum_decl()?
490 }
491 "class" => {
492 if crate::compat_mode() {
493 return Err(self.syntax_err(
495 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
496 self.peek_line(),
497 ));
498 }
499 self.parse_class_decl(false, false)?
500 }
501 "abstract" => {
502 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
504 return Err(self.syntax_err(
505 "`abstract` must be followed by `class`",
506 self.peek_line(),
507 ));
508 }
509 self.parse_class_decl(true, false)?
510 }
511 "final" => {
512 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
514 return Err(self
515 .syntax_err("`final` must be followed by `class`", self.peek_line()));
516 }
517 self.parse_class_decl(false, true)?
518 }
519 "trait" => {
520 if crate::compat_mode() {
521 return Err(self.syntax_err(
522 "`trait` is a stryke extension (disabled by --compat)",
523 self.peek_line(),
524 ));
525 }
526 self.parse_trait_decl()?
527 }
528 "my" => self.parse_my_our_local("my", false)?,
529 "state" => self.parse_my_our_local("state", false)?,
530 "mysync" => {
531 if crate::compat_mode() {
532 return Err(self.syntax_err(
533 "`mysync` is a stryke extension (disabled by --compat)",
534 self.peek_line(),
535 ));
536 }
537 self.parse_my_our_local("mysync", false)?
538 }
539 "frozen" | "const" => {
540 let leading = kw.as_str().to_string();
541 if crate::compat_mode() {
542 return Err(self.syntax_err(
543 format!("`{leading}` is a stryke extension (disabled by --compat)"),
544 self.peek_line(),
545 ));
546 }
547 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
553 if kw == "my" {
554 let mut stmt = self.parse_my_our_local("my", false)?;
555 if let StmtKind::My(ref mut decls) = stmt.kind {
556 for decl in decls.iter_mut() {
557 decl.frozen = true;
558 }
559 }
560 stmt
561 } else {
562 return Err(self.syntax_err(
563 format!("Expected 'my' after '{leading}'"),
564 self.peek_line(),
565 ));
566 }
567 } else {
568 return Err(self.syntax_err(
569 format!("Expected 'my' after '{leading}'"),
570 self.peek_line(),
571 ));
572 }
573 }
574 "typed" => {
575 if crate::compat_mode() {
576 return Err(self.syntax_err(
577 "`typed` is a stryke extension (disabled by --compat)",
578 self.peek_line(),
579 ));
580 }
581 self.advance();
582 if let Token::Ident(ref kw) = self.peek().clone() {
583 if kw == "my" {
584 self.parse_my_our_local("my", true)?
585 } else {
586 return Err(
587 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
588 );
589 }
590 } else {
591 return Err(
592 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
593 );
594 }
595 }
596 "our" => self.parse_my_our_local("our", false)?,
597 "local" => self.parse_my_our_local("local", false)?,
598 "package" => self.parse_package()?,
599 "use" => self.parse_use()?,
600 "no" => self.parse_no()?,
601 "return" => self.parse_return()?,
602 "last" => {
603 self.advance();
604 let lbl = if let Token::Ident(ref s) = self.peek() {
605 if s.chars().all(|c| c.is_uppercase() || c == '_') {
606 let (Token::Ident(l), _) = self.advance() else {
607 unreachable!()
608 };
609 Some(l)
610 } else {
611 None
612 }
613 } else {
614 None
615 };
616 let stmt = Statement {
617 label: None,
618 kind: StmtKind::Last(lbl.or(label.clone())),
619 line,
620 };
621 self.parse_stmt_postfix_modifier(stmt)?
622 }
623 "next" => {
624 self.advance();
625 let lbl = if let Token::Ident(ref s) = self.peek() {
626 if s.chars().all(|c| c.is_uppercase() || c == '_') {
627 let (Token::Ident(l), _) = self.advance() else {
628 unreachable!()
629 };
630 Some(l)
631 } else {
632 None
633 }
634 } else {
635 None
636 };
637 let stmt = Statement {
638 label: None,
639 kind: StmtKind::Next(lbl.or(label.clone())),
640 line,
641 };
642 self.parse_stmt_postfix_modifier(stmt)?
643 }
644 "redo" => {
645 self.advance();
646 self.eat(&Token::Semicolon);
647 Statement {
648 label: None,
649 kind: StmtKind::Redo(label.clone()),
650 line,
651 }
652 }
653 "BEGIN" => {
654 self.advance();
655 let block = self.parse_block()?;
656 Statement {
657 label: None,
658 kind: StmtKind::Begin(block),
659 line,
660 }
661 }
662 "END" => {
663 self.advance();
664 let block = self.parse_block()?;
665 Statement {
666 label: None,
667 kind: StmtKind::End(block),
668 line,
669 }
670 }
671 "UNITCHECK" => {
672 self.advance();
673 let block = self.parse_block()?;
674 Statement {
675 label: None,
676 kind: StmtKind::UnitCheck(block),
677 line,
678 }
679 }
680 "CHECK" => {
681 self.advance();
682 let block = self.parse_block()?;
683 Statement {
684 label: None,
685 kind: StmtKind::Check(block),
686 line,
687 }
688 }
689 "INIT" => {
690 self.advance();
691 let block = self.parse_block()?;
692 Statement {
693 label: None,
694 kind: StmtKind::Init(block),
695 line,
696 }
697 }
698 "goto" => {
699 self.advance();
700 let target = self.parse_expression()?;
701 let stmt = Statement {
702 label: None,
703 kind: StmtKind::Goto {
704 target: Box::new(target),
705 },
706 line,
707 };
708 self.parse_stmt_postfix_modifier(stmt)?
710 }
711 "continue" => {
712 self.advance();
713 let block = self.parse_block()?;
714 Statement {
715 label: None,
716 kind: StmtKind::Continue(block),
717 line,
718 }
719 }
720 "try" => self.parse_try_catch()?,
721 "defer" => self.parse_defer_stmt()?,
722 "tie" => self.parse_tie_stmt()?,
723 "given" => self.parse_given()?,
724 "when" => self.parse_when_stmt()?,
725 "default" => self.parse_default_stmt()?,
726 "eval_timeout" => self.parse_eval_timeout()?,
727 "do" => {
728 if matches!(self.peek_at(1), Token::LBrace) {
729 self.advance();
730 let body = self.parse_block()?;
731 if let Token::Ident(ref w) = self.peek().clone() {
732 if w == "while" {
733 self.advance();
734 self.expect(&Token::LParen)?;
735 let mut condition = self.parse_expression()?;
736 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
737 self.expect(&Token::RParen)?;
738 self.eat(&Token::Semicolon);
739 Statement {
740 label: label.clone(),
741 kind: StmtKind::DoWhile { body, condition },
742 line,
743 }
744 } else {
745 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
746 let inner = Expr {
747 kind: ExprKind::CodeRef {
748 params: vec![],
749 body,
750 },
751 line: inner_line,
752 };
753 let expr = Expr {
754 kind: ExprKind::Do(Box::new(inner)),
755 line,
756 };
757 let stmt = Statement {
758 label: label.clone(),
759 kind: StmtKind::Expression(expr),
760 line,
761 };
762 self.parse_stmt_postfix_modifier(stmt)?
764 }
765 } else {
766 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
767 let inner = Expr {
768 kind: ExprKind::CodeRef {
769 params: vec![],
770 body,
771 },
772 line: inner_line,
773 };
774 let expr = Expr {
775 kind: ExprKind::Do(Box::new(inner)),
776 line,
777 };
778 let stmt = Statement {
779 label: label.clone(),
780 kind: StmtKind::Expression(expr),
781 line,
782 };
783 self.parse_stmt_postfix_modifier(stmt)?
784 }
785 } else {
786 if let Some(expr) = self.try_parse_bareword_stmt_call() {
787 let stmt = self.maybe_postfix_modifier(expr)?;
788 self.parse_stmt_postfix_modifier(stmt)?
789 } else {
790 let expr = self.parse_expression()?;
791 let stmt = self.maybe_postfix_modifier(expr)?;
792 self.parse_stmt_postfix_modifier(stmt)?
793 }
794 }
795 }
796 _ => {
797 if let Some(expr) = self.try_parse_bareword_stmt_call() {
799 let stmt = self.maybe_postfix_modifier(expr)?;
800 self.parse_stmt_postfix_modifier(stmt)?
801 } else {
802 let expr = self.parse_expression()?;
803 let stmt = self.maybe_postfix_modifier(expr)?;
804 self.parse_stmt_postfix_modifier(stmt)?
805 }
806 }
807 },
808 Token::LBrace => {
809 let block = self.parse_block()?;
810 let stmt = Statement {
811 label: None,
812 kind: StmtKind::Block(block),
813 line,
814 };
815 self.parse_stmt_postfix_modifier(stmt)?
817 }
818 _ => {
819 let expr = self.parse_expression()?;
820 let stmt = self.maybe_postfix_modifier(expr)?;
821 self.parse_stmt_postfix_modifier(stmt)?
822 }
823 };
824
825 stmt.label = label;
826 Ok(stmt)
827 }
828
829 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> PerlResult<Statement> {
831 let line = stmt.line;
832 if self.peek_line() > self.prev_line() {
837 self.eat(&Token::Semicolon);
838 return Ok(stmt);
839 }
840 if let Token::Ident(ref kw) = self.peek().clone() {
841 match kw.as_str() {
842 "if" => {
843 self.advance();
844 let mut cond = self.parse_expression()?;
845 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
846 self.eat(&Token::Semicolon);
847 return Ok(Statement {
848 label: None,
849 kind: StmtKind::If {
850 condition: cond,
851 body: vec![stmt],
852 elsifs: vec![],
853 else_block: None,
854 },
855 line,
856 });
857 }
858 "unless" => {
859 self.advance();
860 let mut cond = self.parse_expression()?;
861 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
862 self.eat(&Token::Semicolon);
863 return Ok(Statement {
864 label: None,
865 kind: StmtKind::Unless {
866 condition: cond,
867 body: vec![stmt],
868 else_block: None,
869 },
870 line,
871 });
872 }
873 "while" | "until" | "for" | "foreach" => {
874 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
877 let out = self.maybe_postfix_modifier(expr)?;
878 self.eat(&Token::Semicolon);
879 return Ok(out);
880 }
881 return Err(self.syntax_err(
882 format!("postfix `{}` is not supported on this statement form", kw),
883 self.peek_line(),
884 ));
885 }
886 "pmap" | "pflat_map" | "pgrep" | "pfor" | "preduce" | "pcache" => {
888 let line = stmt.line;
889 let block = self.stmt_into_parallel_block(stmt)?;
890 let which = kw.as_str();
891 self.advance();
892 self.eat(&Token::Comma);
893 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
894 self.eat(&Token::Semicolon);
895 let list = Box::new(list);
896 let progress = progress.map(Box::new);
897 let kind = match which {
898 "pmap" => ExprKind::PMapExpr {
899 block,
900 list,
901 progress,
902 flat_outputs: false,
903 on_cluster: None,
904 },
905 "pflat_map" => ExprKind::PMapExpr {
906 block,
907 list,
908 progress,
909 flat_outputs: true,
910 on_cluster: None,
911 },
912 "pgrep" => ExprKind::PGrepExpr {
913 block,
914 list,
915 progress,
916 },
917 "pfor" => ExprKind::PForExpr {
918 block,
919 list,
920 progress,
921 },
922 "preduce" => ExprKind::PReduceExpr {
923 block,
924 list,
925 progress,
926 },
927 "pcache" => ExprKind::PcacheExpr {
928 block,
929 list,
930 progress,
931 },
932 _ => unreachable!(),
933 };
934 return Ok(Statement {
935 label: None,
936 kind: StmtKind::Expression(Expr { kind, line }),
937 line,
938 });
939 }
940 _ => {}
941 }
942 }
943 self.eat(&Token::Semicolon);
944 Ok(stmt)
945 }
946
947 fn stmt_into_parallel_block(&self, stmt: Statement) -> PerlResult<Block> {
950 let line = stmt.line;
951 match stmt.kind {
952 StmtKind::Block(block) => Ok(block),
953 StmtKind::Expression(expr) => {
954 if let ExprKind::Do(ref inner) = expr.kind {
955 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
956 return Ok(body.clone());
957 }
958 }
959 Ok(vec![Statement {
960 label: None,
961 kind: StmtKind::Expression(expr),
962 line,
963 }])
964 }
965 _ => Err(self.syntax_err(
966 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
967 line,
968 )),
969 }
970 }
971
972 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
975 match stmt.kind {
976 StmtKind::Expression(expr) => Some(expr),
977 StmtKind::Block(block) => {
978 let line = stmt.line;
979 let inner = Expr {
980 kind: ExprKind::CodeRef {
981 params: vec![],
982 body: block,
983 },
984 line,
985 };
986 Some(Expr {
987 kind: ExprKind::Do(Box::new(inner)),
988 line,
989 })
990 }
991 _ => None,
992 }
993 }
994
995 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
998 matches!(
999 self.peek(),
1000 Token::Ident(ref kw)
1001 if matches!(
1002 kw.as_str(),
1003 "if" | "unless" | "while" | "until" | "for" | "foreach"
1004 )
1005 )
1006 }
1007
1008 fn maybe_postfix_modifier(&mut self, expr: Expr) -> PerlResult<Statement> {
1009 let line = expr.line;
1010 if self.peek_line() > self.prev_line() {
1012 return Ok(Statement {
1013 label: None,
1014 kind: StmtKind::Expression(expr),
1015 line,
1016 });
1017 }
1018 match self.peek() {
1019 Token::Ident(ref kw) => match kw.as_str() {
1020 "if" => {
1021 self.advance();
1022 let cond = self.parse_expression()?;
1023 Ok(Statement {
1024 label: None,
1025 kind: StmtKind::Expression(Expr {
1026 kind: ExprKind::PostfixIf {
1027 expr: Box::new(expr),
1028 condition: Box::new(cond),
1029 },
1030 line,
1031 }),
1032 line,
1033 })
1034 }
1035 "unless" => {
1036 self.advance();
1037 let cond = self.parse_expression()?;
1038 Ok(Statement {
1039 label: None,
1040 kind: StmtKind::Expression(Expr {
1041 kind: ExprKind::PostfixUnless {
1042 expr: Box::new(expr),
1043 condition: Box::new(cond),
1044 },
1045 line,
1046 }),
1047 line,
1048 })
1049 }
1050 "while" => {
1051 self.advance();
1052 let cond = self.parse_expression()?;
1053 Ok(Statement {
1054 label: None,
1055 kind: StmtKind::Expression(Expr {
1056 kind: ExprKind::PostfixWhile {
1057 expr: Box::new(expr),
1058 condition: Box::new(cond),
1059 },
1060 line,
1061 }),
1062 line,
1063 })
1064 }
1065 "until" => {
1066 self.advance();
1067 let cond = self.parse_expression()?;
1068 Ok(Statement {
1069 label: None,
1070 kind: StmtKind::Expression(Expr {
1071 kind: ExprKind::PostfixUntil {
1072 expr: Box::new(expr),
1073 condition: Box::new(cond),
1074 },
1075 line,
1076 }),
1077 line,
1078 })
1079 }
1080 "for" | "foreach" => {
1081 self.advance();
1082 let list = self.parse_expression()?;
1083 Ok(Statement {
1084 label: None,
1085 kind: StmtKind::Expression(Expr {
1086 kind: ExprKind::PostfixForeach {
1087 expr: Box::new(expr),
1088 list: Box::new(list),
1089 },
1090 line,
1091 }),
1092 line,
1093 })
1094 }
1095 _ => Ok(Statement {
1096 label: None,
1097 kind: StmtKind::Expression(expr),
1098 line,
1099 }),
1100 },
1101 _ => Ok(Statement {
1102 label: None,
1103 kind: StmtKind::Expression(expr),
1104 line,
1105 }),
1106 }
1107 }
1108
1109 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1111 let saved = self.pos;
1112 let line = self.peek_line();
1113 let mut name = match self.peek() {
1114 Token::Ident(n) => n.clone(),
1115 _ => return None,
1116 };
1117 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1119 return None;
1120 }
1121 self.advance();
1122 while self.eat(&Token::PackageSep) {
1123 match self.advance() {
1124 (Token::Ident(part), _) => {
1125 name = format!("{}::{}", name, part);
1126 }
1127 _ => {
1128 self.pos = saved;
1129 return None;
1130 }
1131 }
1132 }
1133 match self.peek() {
1134 Token::Semicolon | Token::RBrace => Some(Expr {
1135 kind: ExprKind::FuncCall { name, args: vec![] },
1136 line,
1137 }),
1138 _ => {
1139 self.pos = saved;
1140 None
1141 }
1142 }
1143 }
1144
1145 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1147 !matches!(
1148 name,
1149 "__FILE__"
1150 | "__LINE__"
1151 | "abs"
1152 | "async"
1153 | "spawn"
1154 | "atan2"
1155 | "await"
1156 | "barrier"
1157 | "bless"
1158 | "caller"
1159 | "capture"
1160 | "cat"
1161 | "chdir"
1162 | "chmod"
1163 | "chomp"
1164 | "chop"
1165 | "chr"
1166 | "chown"
1167 | "closedir"
1168 | "close"
1169 | "collect"
1170 | "cos"
1171 | "crypt"
1172 | "defined"
1173 | "dec"
1174 | "delete"
1175 | "die"
1176 | "deque"
1177 | "do"
1178 | "each"
1179 | "eof"
1180 | "fore"
1181 | "eval"
1182 | "exec"
1183 | "exists"
1184 | "exit"
1185 | "exp"
1186 | "fan"
1187 | "fan_cap"
1188 | "fc"
1189 | "fetch_url"
1190 | "d"
1191 | "dirs"
1192 | "dr"
1193 | "f"
1194 | "files"
1195 | "filesf"
1196 | "filter"
1197 | "fr"
1198 | "getcwd"
1199 | "glob_par"
1200 | "par_sed"
1201 | "glob"
1202 | "grep"
1203 | "greps"
1204 | "heap"
1205 | "hex"
1206 | "inc"
1207 | "index"
1208 | "int"
1209 | "join"
1210 | "keys"
1211 | "lcfirst"
1212 | "lc"
1213 | "length"
1214 | "link"
1215 | "log"
1216 | "lstat"
1217 | "map"
1218 | "flat_map"
1219 | "maps"
1220 | "flat_maps"
1221 | "flatten"
1222 | "frequencies"
1223 | "freq"
1224 | "interleave"
1225 | "ddump"
1226 | "stringify"
1227 | "str"
1228 | "s"
1229 | "input"
1230 | "lines"
1231 | "words"
1232 | "chars"
1233 | "digits"
1234 | "sentences"
1235 | "paragraphs"
1236 | "sections"
1237 | "numbers"
1238 | "graphemes"
1239 | "columns"
1240 | "trim"
1241 | "avg"
1242 | "top"
1243 | "pager"
1244 | "pg"
1245 | "less"
1246 | "count_by"
1247 | "to_file"
1248 | "to_json"
1249 | "to_csv"
1250 | "grep_v"
1251 | "select_keys"
1252 | "pluck"
1253 | "clamp"
1254 | "normalize"
1255 | "stddev"
1256 | "squared"
1257 | "square"
1258 | "cubed"
1259 | "cube"
1260 | "expt"
1261 | "pow"
1262 | "pw"
1263 | "snake_case"
1264 | "camel_case"
1265 | "kebab_case"
1266 | "to_toml"
1267 | "to_yaml"
1268 | "to_xml"
1269 | "to_html"
1270 | "to_markdown"
1271 | "xopen"
1272 | "clip"
1273 | "paste"
1274 | "to_table"
1275 | "sparkline"
1276 | "bar_chart"
1277 | "flame"
1278 | "set"
1279 | "list_count"
1280 | "list_size"
1281 | "count"
1282 | "size"
1283 | "cnt"
1284 | "len"
1285 | "all"
1286 | "any"
1287 | "none"
1288 | "take_while"
1289 | "drop_while"
1290 | "skip_while"
1291 | "skip"
1292 | "first_or"
1293 | "tap"
1294 | "peek"
1295 | "partition"
1296 | "min_by"
1297 | "max_by"
1298 | "zip_with"
1299 | "group_by"
1300 | "chunk_by"
1301 | "with_index"
1302 | "puniq"
1303 | "pfirst"
1304 | "pany"
1305 | "uniq"
1306 | "distinct"
1307 | "shuffle"
1308 | "shuffled"
1309 | "chunked"
1310 | "windowed"
1311 | "match"
1312 | "mkdir"
1313 | "every"
1314 | "gen"
1315 | "oct"
1316 | "open"
1317 | "p"
1318 | "opendir"
1319 | "ord"
1320 | "par_lines"
1321 | "par_walk"
1322 | "pipe"
1323 | "pipes"
1324 | "block_devices"
1325 | "char_devices"
1326 | "rate_limit"
1327 | "retry"
1328 | "pcache"
1329 | "pchannel"
1330 | "pfor"
1331 | "pgrep"
1332 | "pipeline"
1333 | "pmap_chunked"
1334 | "pmap_reduce"
1335 | "pmap_on"
1336 | "pflat_map_on"
1337 | "pmap"
1338 | "pflat_map"
1339 | "pop"
1340 | "pos"
1341 | "ppool"
1342 | "preduce_init"
1343 | "preduce"
1344 | "pselect"
1345 | "printf"
1346 | "print"
1347 | "pr"
1348 | "psort"
1349 | "push"
1350 | "pwatch"
1351 | "rand"
1352 | "readdir"
1353 | "readlink"
1354 | "reduce"
1355 | "fold"
1356 | "inject"
1357 | "first"
1358 | "detect"
1359 | "find"
1360 | "find_all"
1361 | "ref"
1362 | "rename"
1363 | "require"
1364 | "rev"
1365 | "reverse"
1366 | "reversed"
1367 | "rewinddir"
1368 | "rindex"
1369 | "rmdir"
1370 | "rm"
1371 | "say"
1372 | "scalar"
1373 | "seekdir"
1374 | "shift"
1375 | "sin"
1376 | "slurp"
1377 | "sockets"
1378 | "sort"
1379 | "splice"
1380 | "split"
1381 | "sprintf"
1382 | "sqrt"
1383 | "srand"
1384 | "stat"
1385 | "study"
1386 | "substr"
1387 | "symlink"
1388 | "sym_links"
1389 | "system"
1390 | "telldir"
1391 | "timer"
1392 | "trace"
1393 | "ucfirst"
1394 | "uc"
1395 | "undef"
1396 | "umask"
1397 | "unlink"
1398 | "unshift"
1399 | "utime"
1400 | "values"
1401 | "wantarray"
1402 | "warn"
1403 | "watch"
1404 | "yield"
1405 | "sub"
1406 )
1407 }
1408
1409 fn parse_block(&mut self) -> PerlResult<Block> {
1410 self.expect(&Token::LBrace)?;
1411 let mut stmts = Vec::new();
1412 if let Some(param_stmts) = self.try_parse_block_params()? {
1416 stmts.extend(param_stmts);
1417 }
1418 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1419 if self.eat(&Token::Semicolon) {
1420 continue;
1421 }
1422 stmts.push(self.parse_statement()?);
1423 }
1424 self.expect(&Token::RBrace)?;
1425 Self::default_topic_for_sole_bareword(&mut stmts);
1426 Ok(stmts)
1427 }
1428
1429 fn try_parse_block_params(&mut self) -> PerlResult<Option<Vec<Statement>>> {
1434 if !matches!(self.peek(), Token::BitOr) {
1435 return Ok(None);
1436 }
1437 let mut i = 1; loop {
1440 match self.peek_at(i) {
1441 Token::ScalarVar(_) => i += 1,
1442 _ => return Ok(None), }
1444 match self.peek_at(i) {
1445 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
1449 }
1450 let line = self.peek_line();
1452 self.advance(); let mut names = Vec::new();
1454 loop {
1455 if let Token::ScalarVar(ref name) = self.peek().clone() {
1456 names.push(name.clone());
1457 self.advance();
1458 }
1459 if self.eat(&Token::BitOr) {
1460 break;
1461 }
1462 self.expect(&Token::Comma)?;
1463 }
1464 let sources: Vec<&str> = match names.len() {
1469 1 => vec!["_"],
1470 2 => vec!["a", "b"],
1471 n => {
1472 let _ = n;
1474 vec![] }
1476 };
1477 let mut stmts = Vec::with_capacity(names.len());
1478 if !sources.is_empty() {
1479 for (name, src) in names.iter().zip(sources.iter()) {
1480 stmts.push(Statement {
1481 label: None,
1482 kind: StmtKind::My(vec![VarDecl {
1483 sigil: Sigil::Scalar,
1484 name: name.clone(),
1485 initializer: Some(Expr {
1486 kind: ExprKind::ScalarVar(src.to_string()),
1487 line,
1488 }),
1489 frozen: false,
1490 type_annotation: None,
1491 }]),
1492 line,
1493 });
1494 }
1495 } else {
1496 for (idx, name) in names.iter().enumerate() {
1498 let src = if idx == 0 {
1499 "_".to_string()
1500 } else {
1501 format!("_{idx}")
1502 };
1503 stmts.push(Statement {
1504 label: None,
1505 kind: StmtKind::My(vec![VarDecl {
1506 sigil: Sigil::Scalar,
1507 name: name.clone(),
1508 initializer: Some(Expr {
1509 kind: ExprKind::ScalarVar(src),
1510 line,
1511 }),
1512 frozen: false,
1513 type_annotation: None,
1514 }]),
1515 line,
1516 });
1517 }
1518 }
1519 Ok(Some(stmts))
1520 }
1521
1522 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
1537 let [only] = stmts else { return };
1538 let StmtKind::Expression(ref mut expr) = only.kind else {
1539 return;
1540 };
1541 let topic_line = expr.line;
1542 let topic_arg = || Expr {
1543 kind: ExprKind::ScalarVar("_".to_string()),
1544 line: topic_line,
1545 };
1546 match expr.kind {
1547 ExprKind::FuncCall {
1549 ref name,
1550 ref mut args,
1551 } if args.is_empty()
1552 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1553 {
1554 args.push(topic_arg());
1555 }
1556 ExprKind::Bareword(ref name)
1560 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1561 {
1562 let n = name.clone();
1563 expr.kind = ExprKind::FuncCall {
1564 name: n,
1565 args: vec![topic_arg()],
1566 };
1567 }
1568 _ => {}
1569 }
1570 }
1571
1572 fn parse_defer_stmt(&mut self) -> PerlResult<Statement> {
1576 let line = self.peek_line();
1577 self.advance(); let body = self.parse_block()?;
1579 self.eat(&Token::Semicolon);
1580 let coderef = Expr {
1582 kind: ExprKind::CodeRef {
1583 params: vec![],
1584 body,
1585 },
1586 line,
1587 };
1588 Ok(Statement {
1589 label: None,
1590 kind: StmtKind::Expression(Expr {
1591 kind: ExprKind::FuncCall {
1592 name: "defer__internal".to_string(),
1593 args: vec![coderef],
1594 },
1595 line,
1596 }),
1597 line,
1598 })
1599 }
1600
1601 fn parse_try_catch(&mut self) -> PerlResult<Statement> {
1603 let line = self.peek_line();
1604 self.advance(); let try_block = self.parse_block()?;
1606 match self.peek() {
1607 Token::Ident(ref k) if k == "catch" => {
1608 self.advance();
1609 }
1610 _ => {
1611 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
1612 }
1613 }
1614 self.expect(&Token::LParen)?;
1615 let catch_var = self.parse_scalar_var_name()?;
1616 self.expect(&Token::RParen)?;
1617 let catch_block = self.parse_block()?;
1618 let finally_block = match self.peek() {
1619 Token::Ident(ref k) if k == "finally" => {
1620 self.advance();
1621 Some(self.parse_block()?)
1622 }
1623 _ => None,
1624 };
1625 self.eat(&Token::Semicolon);
1626 Ok(Statement {
1627 label: None,
1628 kind: StmtKind::TryCatch {
1629 try_block,
1630 catch_var,
1631 catch_block,
1632 finally_block,
1633 },
1634 line,
1635 })
1636 }
1637
1638 fn parse_thread_macro(&mut self, _line: usize) -> PerlResult<Expr> {
1650 let pipe_rhs_wrap = self.in_pipe_rhs();
1651 let mut result = if pipe_rhs_wrap {
1652 Expr {
1653 kind: ExprKind::ArrayElement {
1654 array: "_".to_string(),
1655 index: Box::new(Expr {
1656 kind: ExprKind::Integer(0),
1657 line: _line,
1658 }),
1659 },
1660 line: _line,
1661 }
1662 } else {
1663 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
1666 let expr = self.parse_thread_input();
1667 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
1668 expr?
1669 };
1670
1671 loop {
1673 match self.peek() {
1675 Token::Semicolon
1676 | Token::Newline
1677 | Token::RBrace
1678 | Token::RParen
1679 | Token::RBracket
1680 | Token::PipeForward
1681 | Token::Eof => break,
1682 _ => {}
1683 }
1684
1685 let stage_line = self.peek_line();
1686
1687 match self.peek().clone() {
1689 Token::ArrowBrace => {
1691 self.advance(); let mut stmts = Vec::new();
1693 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1694 if self.eat(&Token::Semicolon) {
1695 continue;
1696 }
1697 stmts.push(self.parse_statement()?);
1698 }
1699 self.expect(&Token::RBrace)?;
1700 let code_ref = Expr {
1701 kind: ExprKind::CodeRef {
1702 params: vec![],
1703 body: stmts,
1704 },
1705 line: stage_line,
1706 };
1707 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
1708 }
1709 Token::Ident(ref name) if name == "sub" || name == "fn" => {
1711 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
1713 let body = self.parse_block()?;
1714 let code_ref = Expr {
1715 kind: ExprKind::CodeRef { params, body },
1716 line: stage_line,
1717 };
1718 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
1719 }
1720 Token::Ident(ref name) => {
1722 let func_name = name.clone();
1723 self.advance();
1724
1725 if func_name.starts_with('\x00') {
1727 let parts: Vec<&str> = func_name.split('\x00').collect();
1728 if parts.len() >= 4 && parts[1] == "s" {
1729 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
1730 let stage = Expr {
1731 kind: ExprKind::Substitution {
1732 expr: Box::new(result.clone()),
1733 pattern: parts[2].to_string(),
1734 replacement: parts[3].to_string(),
1735 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
1736 delim,
1737 },
1738 line: stage_line,
1739 };
1740 result = stage;
1741 continue;
1742 }
1743 if parts.len() >= 4 && parts[1] == "tr" {
1744 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
1745 let stage = Expr {
1746 kind: ExprKind::Transliterate {
1747 expr: Box::new(result.clone()),
1748 from: parts[2].to_string(),
1749 to: parts[3].to_string(),
1750 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
1751 delim,
1752 },
1753 line: stage_line,
1754 };
1755 result = stage;
1756 continue;
1757 }
1758 return Err(
1759 self.syntax_err("Unexpected encoded token in thread", stage_line)
1760 );
1761 }
1762
1763 if matches!(self.peek(), Token::Plus)
1768 && matches!(self.peek_at(1), Token::LBrace)
1769 {
1770 self.advance(); self.expect(&Token::LBrace)?;
1772 let pairs = self.try_parse_hash_ref()?;
1774 let hashref_expr = Expr {
1775 kind: ExprKind::HashRef(pairs),
1776 line: stage_line,
1777 };
1778 let flatten_array_refs =
1779 matches!(func_name.as_str(), "flat_map" | "flat_maps");
1780 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
1781 let placeholder = Expr {
1783 kind: ExprKind::Undef,
1784 line: stage_line,
1785 };
1786 let map_node = Expr {
1787 kind: ExprKind::MapExprComma {
1788 expr: Box::new(hashref_expr),
1789 list: Box::new(placeholder),
1790 flatten_array_refs,
1791 stream,
1792 },
1793 line: stage_line,
1794 };
1795 result = self.pipe_forward_apply(result, map_node, stage_line)?;
1796 } else if matches!(self.peek(), Token::LBrace) {
1798 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
1800 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
1801 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
1802 result = self.pipe_forward_apply(result, stage, stage_line)?;
1803 } else if matches!(self.peek(), Token::LParen) {
1804 self.advance(); let mut call_args = Vec::new();
1815 while !matches!(self.peek(), Token::RParen | Token::Eof) {
1816 call_args.push(self.parse_assign_expr()?);
1817 if !self.eat(&Token::Comma) {
1818 break;
1819 }
1820 }
1821 self.expect(&Token::RParen)?;
1822 if !call_args.iter().any(Self::expr_contains_topic_var) {
1823 return Err(self.syntax_err(
1824 format!(
1825 "thread: `{}(...)` call-stage requires `$_` placeholder somewhere in args (e.g. `{}($_, ...)`); use bare `{}` for sole-arg threading or `>{{ ... }}` for arbitrary expressions",
1826 func_name, func_name, func_name
1827 ),
1828 stage_line,
1829 ));
1830 }
1831 let call_expr = Expr {
1832 kind: ExprKind::FuncCall {
1833 name: func_name.clone(),
1834 args: call_args,
1835 },
1836 line: stage_line,
1837 };
1838 let code_ref = Expr {
1839 kind: ExprKind::CodeRef {
1840 params: vec![],
1841 body: vec![Statement {
1842 label: None,
1843 kind: StmtKind::Expression(call_expr),
1844 line: stage_line,
1845 }],
1846 },
1847 line: stage_line,
1848 };
1849 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
1850 } else {
1851 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
1853 }
1854 }
1855 Token::Regex(ref pattern, ref flags, delim) => {
1857 let pattern = pattern.clone();
1858 let flags = flags.clone();
1859 self.advance();
1860 result =
1861 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
1862 }
1863 Token::Slash => {
1866 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
1872 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
1873 && matches!(self.peek_at(1), Token::Regex(..))
1874 {
1875 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
1882 self.peek().clone()
1883 {
1884 let _ = (misparsed_pattern, misparsed_flags);
1894 }
1895 }
1896 }
1897
1898 let mut pattern = String::new();
1900 loop {
1901 match self.peek().clone() {
1902 Token::Slash => {
1903 self.advance(); break;
1905 }
1906 Token::Eof | Token::Semicolon | Token::Newline => {
1907 return Err(self
1908 .syntax_err("Unterminated regex in thread stage", stage_line));
1909 }
1910 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
1912 if pattern.is_empty()
1920 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
1921 {
1922 let _ = (inner_pattern, inner_flags, delim);
1928 }
1929 return Err(self.syntax_err(
1931 "Complex regex in thread stage - use m/pattern/ syntax instead",
1932 stage_line,
1933 ));
1934 }
1935 Token::Ident(ref s) => {
1936 pattern.push_str(s);
1937 self.advance();
1938 }
1939 Token::Integer(n) => {
1940 pattern.push_str(&n.to_string());
1941 self.advance();
1942 }
1943 Token::ScalarVar(ref v) => {
1944 pattern.push('$');
1945 pattern.push_str(v);
1946 self.advance();
1947 }
1948 Token::Dot => {
1949 pattern.push('.');
1950 self.advance();
1951 }
1952 Token::Star => {
1953 pattern.push('*');
1954 self.advance();
1955 }
1956 Token::Plus => {
1957 pattern.push('+');
1958 self.advance();
1959 }
1960 Token::Question => {
1961 pattern.push('?');
1962 self.advance();
1963 }
1964 Token::LParen => {
1965 pattern.push('(');
1966 self.advance();
1967 }
1968 Token::RParen => {
1969 pattern.push(')');
1970 self.advance();
1971 }
1972 Token::LBracket => {
1973 pattern.push('[');
1974 self.advance();
1975 }
1976 Token::RBracket => {
1977 pattern.push(']');
1978 self.advance();
1979 }
1980 Token::Backslash => {
1981 pattern.push('\\');
1982 self.advance();
1983 }
1984 Token::BitOr => {
1985 pattern.push('|');
1986 self.advance();
1987 }
1988 Token::Power => {
1989 pattern.push_str("**");
1990 self.advance();
1991 }
1992 Token::BitXor => {
1993 pattern.push('^');
1994 self.advance();
1995 }
1996 Token::Minus => {
1997 pattern.push('-');
1998 self.advance();
1999 }
2000 _ => {
2001 return Err(self.syntax_err(
2002 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2003 stage_line,
2004 ));
2005 }
2006 }
2007 }
2008 let mut flags = String::new();
2012 if let Token::Ident(ref s) = self.peek().clone() {
2013 let is_flag_only =
2014 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2015 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2016 if is_flag_only && !followed_by_brace {
2017 flags.push_str(s);
2018 self.advance();
2019 }
2020 }
2021 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2022 }
2023 tok => {
2024 return Err(self.syntax_err(
2025 format!(
2026 "thread: expected stage (ident, sub {{}}, s///, tr///, or /re/), got {:?}",
2027 tok
2028 ),
2029 stage_line,
2030 ));
2031 }
2032 };
2033 }
2034
2035 if pipe_rhs_wrap {
2036 let body_line = result.line;
2039 return Ok(Expr {
2040 kind: ExprKind::CodeRef {
2041 params: vec![],
2042 body: vec![Statement {
2043 label: None,
2044 kind: StmtKind::Expression(result),
2045 line: body_line,
2046 }],
2047 },
2048 line: _line,
2049 });
2050 }
2051 Ok(result)
2052 }
2053
2054 fn thread_regex_grep_stage(
2056 &self,
2057 list: Expr,
2058 pattern: String,
2059 flags: String,
2060 delim: char,
2061 line: usize,
2062 ) -> Expr {
2063 let topic = Expr {
2064 kind: ExprKind::ScalarVar("_".to_string()),
2065 line,
2066 };
2067 let match_expr = Expr {
2068 kind: ExprKind::Match {
2069 expr: Box::new(topic),
2070 pattern,
2071 flags,
2072 scalar_g: false,
2073 delim,
2074 },
2075 line,
2076 };
2077 let block = vec![Statement {
2078 label: None,
2079 kind: StmtKind::Expression(match_expr),
2080 line,
2081 }];
2082 Expr {
2083 kind: ExprKind::GrepExpr {
2084 block,
2085 list: Box::new(list),
2086 keyword: crate::ast::GrepBuiltinKeyword::Grep,
2087 },
2088 line,
2089 }
2090 }
2091
2092 fn expr_contains_topic_var(e: &Expr) -> bool {
2103 format!("{:?}", e).contains("ScalarVar(\"_\")")
2104 }
2105
2106 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> PerlResult<Expr> {
2108 let kind = match name {
2109 "uc" => ExprKind::Uc(Box::new(arg)),
2111 "lc" => ExprKind::Lc(Box::new(arg)),
2112 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
2113 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
2114 "fc" => ExprKind::Fc(Box::new(arg)),
2115 "chomp" => ExprKind::Chomp(Box::new(arg)),
2116 "chop" => ExprKind::Chop(Box::new(arg)),
2117 "length" => ExprKind::Length(Box::new(arg)),
2118 "len" | "cnt" => ExprKind::FuncCall {
2119 name: "count".to_string(),
2120 args: vec![arg],
2121 },
2122 "quotemeta" | "qm" => ExprKind::FuncCall {
2123 name: "quotemeta".to_string(),
2124 args: vec![arg],
2125 },
2126 "abs" => ExprKind::Abs(Box::new(arg)),
2128 "int" => ExprKind::Int(Box::new(arg)),
2129 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
2130 "sin" => ExprKind::Sin(Box::new(arg)),
2131 "cos" => ExprKind::Cos(Box::new(arg)),
2132 "exp" => ExprKind::Exp(Box::new(arg)),
2133 "log" => ExprKind::Log(Box::new(arg)),
2134 "hex" => ExprKind::Hex(Box::new(arg)),
2135 "oct" => ExprKind::Oct(Box::new(arg)),
2136 "chr" => ExprKind::Chr(Box::new(arg)),
2137 "ord" => ExprKind::Ord(Box::new(arg)),
2138 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
2140 "ref" => ExprKind::Ref(Box::new(arg)),
2141 "scalar" => ExprKind::ScalarContext(Box::new(arg)),
2142 "keys" => ExprKind::Keys(Box::new(arg)),
2144 "values" => ExprKind::Values(Box::new(arg)),
2145 "each" => ExprKind::Each(Box::new(arg)),
2146 "pop" => ExprKind::Pop(Box::new(arg)),
2147 "shift" => ExprKind::Shift(Box::new(arg)),
2148 "reverse" | "reversed" | "rv" => ExprKind::ReverseExpr(Box::new(arg)),
2149 "rev" => ExprKind::ScalarReverse(Box::new(arg)),
2150 "sort" | "so" => ExprKind::SortExpr {
2151 cmp: None,
2152 list: Box::new(arg),
2153 },
2154 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
2155 name: "uniq".to_string(),
2156 args: vec![arg],
2157 },
2158 "trim" | "tm" => ExprKind::FuncCall {
2159 name: "trim".to_string(),
2160 args: vec![arg],
2161 },
2162 "flatten" | "fl" => ExprKind::FuncCall {
2163 name: "flatten".to_string(),
2164 args: vec![arg],
2165 },
2166 "compact" | "cpt" => ExprKind::FuncCall {
2167 name: "compact".to_string(),
2168 args: vec![arg],
2169 },
2170 "shuffle" | "shuf" => ExprKind::FuncCall {
2171 name: "shuffle".to_string(),
2172 args: vec![arg],
2173 },
2174 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
2175 name: "frequencies".to_string(),
2176 args: vec![arg],
2177 },
2178 "dedup" | "dup" => ExprKind::FuncCall {
2179 name: "dedup".to_string(),
2180 args: vec![arg],
2181 },
2182 "enumerate" | "en" => ExprKind::FuncCall {
2183 name: "enumerate".to_string(),
2184 args: vec![arg],
2185 },
2186 "lines" | "ln" => ExprKind::FuncCall {
2187 name: "lines".to_string(),
2188 args: vec![arg],
2189 },
2190 "words" | "wd" => ExprKind::FuncCall {
2191 name: "words".to_string(),
2192 args: vec![arg],
2193 },
2194 "chars" | "ch" => ExprKind::FuncCall {
2195 name: "chars".to_string(),
2196 args: vec![arg],
2197 },
2198 "digits" | "dg" => ExprKind::FuncCall {
2199 name: "digits".to_string(),
2200 args: vec![arg],
2201 },
2202 "sentences" | "sents" => ExprKind::FuncCall {
2203 name: "sentences".to_string(),
2204 args: vec![arg],
2205 },
2206 "paragraphs" | "paras" => ExprKind::FuncCall {
2207 name: "paragraphs".to_string(),
2208 args: vec![arg],
2209 },
2210 "sections" | "sects" => ExprKind::FuncCall {
2211 name: "sections".to_string(),
2212 args: vec![arg],
2213 },
2214 "numbers" | "nums" => ExprKind::FuncCall {
2215 name: "numbers".to_string(),
2216 args: vec![arg],
2217 },
2218 "graphemes" | "grs" => ExprKind::FuncCall {
2219 name: "graphemes".to_string(),
2220 args: vec![arg],
2221 },
2222 "columns" | "cols" => ExprKind::FuncCall {
2223 name: "columns".to_string(),
2224 args: vec![arg],
2225 },
2226 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
2228 "chdir" => ExprKind::Chdir(Box::new(arg)),
2229 "stat" => ExprKind::Stat(Box::new(arg)),
2230 "lstat" => ExprKind::Lstat(Box::new(arg)),
2231 "readlink" => ExprKind::Readlink(Box::new(arg)),
2232 "readdir" => ExprKind::Readdir(Box::new(arg)),
2233 "close" => ExprKind::Close(Box::new(arg)),
2234 "basename" | "bn" => ExprKind::FuncCall {
2235 name: "basename".to_string(),
2236 args: vec![arg],
2237 },
2238 "dirname" | "dn" => ExprKind::FuncCall {
2239 name: "dirname".to_string(),
2240 args: vec![arg],
2241 },
2242 "realpath" | "rp" => ExprKind::FuncCall {
2243 name: "realpath".to_string(),
2244 args: vec![arg],
2245 },
2246 "which" | "wh" => ExprKind::FuncCall {
2247 name: "which".to_string(),
2248 args: vec![arg],
2249 },
2250 "eval" => ExprKind::Eval(Box::new(arg)),
2252 "require" => ExprKind::Require(Box::new(arg)),
2253 "study" => ExprKind::Study(Box::new(arg)),
2254 "snake_case" | "sc" => ExprKind::FuncCall {
2256 name: "snake_case".to_string(),
2257 args: vec![arg],
2258 },
2259 "camel_case" | "cc" => ExprKind::FuncCall {
2260 name: "camel_case".to_string(),
2261 args: vec![arg],
2262 },
2263 "kebab_case" | "kc" => ExprKind::FuncCall {
2264 name: "kebab_case".to_string(),
2265 args: vec![arg],
2266 },
2267 "to_json" | "tj" => ExprKind::FuncCall {
2269 name: "to_json".to_string(),
2270 args: vec![arg],
2271 },
2272 "to_yaml" | "ty" => ExprKind::FuncCall {
2273 name: "to_yaml".to_string(),
2274 args: vec![arg],
2275 },
2276 "to_toml" | "tt" => ExprKind::FuncCall {
2277 name: "to_toml".to_string(),
2278 args: vec![arg],
2279 },
2280 "to_csv" | "tc" => ExprKind::FuncCall {
2281 name: "to_csv".to_string(),
2282 args: vec![arg],
2283 },
2284 "to_xml" | "tx" => ExprKind::FuncCall {
2285 name: "to_xml".to_string(),
2286 args: vec![arg],
2287 },
2288 "to_html" | "th" => ExprKind::FuncCall {
2289 name: "to_html".to_string(),
2290 args: vec![arg],
2291 },
2292 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
2293 name: "to_markdown".to_string(),
2294 args: vec![arg],
2295 },
2296 "xopen" | "xo" => ExprKind::FuncCall {
2297 name: "xopen".to_string(),
2298 args: vec![arg],
2299 },
2300 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
2301 name: "clip".to_string(),
2302 args: vec![arg],
2303 },
2304 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
2305 name: "to_table".to_string(),
2306 args: vec![arg],
2307 },
2308 "sparkline" | "spark" => ExprKind::FuncCall {
2309 name: "sparkline".to_string(),
2310 args: vec![arg],
2311 },
2312 "bar_chart" | "bars" => ExprKind::FuncCall {
2313 name: "bar_chart".to_string(),
2314 args: vec![arg],
2315 },
2316 "flame" | "flamechart" => ExprKind::FuncCall {
2317 name: "flame".to_string(),
2318 args: vec![arg],
2319 },
2320 "ddump" | "dd" => ExprKind::FuncCall {
2321 name: "ddump".to_string(),
2322 args: vec![arg],
2323 },
2324 "stringify" | "str" => ExprKind::FuncCall {
2325 name: "stringify".to_string(),
2326 args: vec![arg],
2327 },
2328 "json_decode" | "jd" => ExprKind::FuncCall {
2329 name: "json_decode".to_string(),
2330 args: vec![arg],
2331 },
2332 "yaml_decode" | "yd" => ExprKind::FuncCall {
2333 name: "yaml_decode".to_string(),
2334 args: vec![arg],
2335 },
2336 "toml_decode" | "td" => ExprKind::FuncCall {
2337 name: "toml_decode".to_string(),
2338 args: vec![arg],
2339 },
2340 "xml_decode" | "xd" => ExprKind::FuncCall {
2341 name: "xml_decode".to_string(),
2342 args: vec![arg],
2343 },
2344 "json_encode" | "je" => ExprKind::FuncCall {
2345 name: "json_encode".to_string(),
2346 args: vec![arg],
2347 },
2348 "yaml_encode" | "ye" => ExprKind::FuncCall {
2349 name: "yaml_encode".to_string(),
2350 args: vec![arg],
2351 },
2352 "toml_encode" | "te" => ExprKind::FuncCall {
2353 name: "toml_encode".to_string(),
2354 args: vec![arg],
2355 },
2356 "xml_encode" | "xe" => ExprKind::FuncCall {
2357 name: "xml_encode".to_string(),
2358 args: vec![arg],
2359 },
2360 "base64_encode" | "b64e" => ExprKind::FuncCall {
2362 name: "base64_encode".to_string(),
2363 args: vec![arg],
2364 },
2365 "base64_decode" | "b64d" => ExprKind::FuncCall {
2366 name: "base64_decode".to_string(),
2367 args: vec![arg],
2368 },
2369 "hex_encode" | "hxe" => ExprKind::FuncCall {
2370 name: "hex_encode".to_string(),
2371 args: vec![arg],
2372 },
2373 "hex_decode" | "hxd" => ExprKind::FuncCall {
2374 name: "hex_decode".to_string(),
2375 args: vec![arg],
2376 },
2377 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
2378 name: "url_encode".to_string(),
2379 args: vec![arg],
2380 },
2381 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
2382 name: "url_decode".to_string(),
2383 args: vec![arg],
2384 },
2385 "gzip" | "gz" => ExprKind::FuncCall {
2386 name: "gzip".to_string(),
2387 args: vec![arg],
2388 },
2389 "gunzip" | "ugz" => ExprKind::FuncCall {
2390 name: "gunzip".to_string(),
2391 args: vec![arg],
2392 },
2393 "zstd" | "zst" => ExprKind::FuncCall {
2394 name: "zstd".to_string(),
2395 args: vec![arg],
2396 },
2397 "zstd_decode" | "uzst" => ExprKind::FuncCall {
2398 name: "zstd_decode".to_string(),
2399 args: vec![arg],
2400 },
2401 "sha256" | "s256" => ExprKind::FuncCall {
2403 name: "sha256".to_string(),
2404 args: vec![arg],
2405 },
2406 "sha1" | "s1" => ExprKind::FuncCall {
2407 name: "sha1".to_string(),
2408 args: vec![arg],
2409 },
2410 "md5" | "m5" => ExprKind::FuncCall {
2411 name: "md5".to_string(),
2412 args: vec![arg],
2413 },
2414 "uuid" | "uid" => ExprKind::FuncCall {
2415 name: "uuid".to_string(),
2416 args: vec![arg],
2417 },
2418 "datetime_utc" | "utc" => ExprKind::FuncCall {
2420 name: "datetime_utc".to_string(),
2421 args: vec![arg],
2422 },
2423 "p" | "say" => ExprKind::Say {
2425 handle: None,
2426 args: vec![arg],
2427 },
2428 "print" | "pr" => ExprKind::Print {
2429 handle: None,
2430 args: vec![arg],
2431 },
2432 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
2435 block: vec![Statement {
2436 label: None,
2437 kind: StmtKind::Expression(Expr {
2438 kind: ExprKind::Say {
2439 handle: None,
2440 args: vec![Expr {
2441 kind: ExprKind::ScalarVar("_".into()),
2442 line,
2443 }],
2444 },
2445 line,
2446 }),
2447 line,
2448 }],
2449 list: Box::new(arg),
2450 },
2451 _ => ExprKind::FuncCall {
2453 name: name.to_string(),
2454 args: vec![arg],
2455 },
2456 };
2457 Ok(Expr { kind, line })
2458 }
2459
2460 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> PerlResult<Expr> {
2463 let block = self.parse_block()?;
2464 let placeholder = self.pipe_placeholder_list(line);
2466
2467 match name {
2468 "map" | "flat_map" | "maps" | "flat_maps" => {
2469 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
2470 let stream = matches!(name, "maps" | "flat_maps");
2471 Ok(Expr {
2472 kind: ExprKind::MapExpr {
2473 block,
2474 list: Box::new(placeholder),
2475 flatten_array_refs,
2476 stream,
2477 },
2478 line,
2479 })
2480 }
2481 "grep" | "greps" | "filter" | "f" | "find_all" | "gr" => {
2482 let keyword = match name {
2483 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
2484 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
2485 "filter" | "f" => crate::ast::GrepBuiltinKeyword::Filter,
2486 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
2487 _ => unreachable!(),
2488 };
2489 Ok(Expr {
2490 kind: ExprKind::GrepExpr {
2491 block,
2492 list: Box::new(placeholder),
2493 keyword,
2494 },
2495 line,
2496 })
2497 }
2498 "sort" | "so" => Ok(Expr {
2499 kind: ExprKind::SortExpr {
2500 cmp: Some(SortComparator::Block(block)),
2501 list: Box::new(placeholder),
2502 },
2503 line,
2504 }),
2505 "reduce" | "rd" => Ok(Expr {
2506 kind: ExprKind::ReduceExpr {
2507 block,
2508 list: Box::new(placeholder),
2509 },
2510 line,
2511 }),
2512 "fore" | "e" | "ep" => Ok(Expr {
2513 kind: ExprKind::ForEachExpr {
2514 block,
2515 list: Box::new(placeholder),
2516 },
2517 line,
2518 }),
2519 _ => {
2520 let code_ref = Expr {
2522 kind: ExprKind::CodeRef {
2523 params: vec![],
2524 body: block,
2525 },
2526 line,
2527 };
2528 Ok(Expr {
2529 kind: ExprKind::FuncCall {
2530 name: name.to_string(),
2531 args: vec![code_ref],
2532 },
2533 line,
2534 })
2535 }
2536 }
2537 }
2538
2539 fn parse_tie_stmt(&mut self) -> PerlResult<Statement> {
2541 let line = self.peek_line();
2542 self.advance(); let target = match self.peek().clone() {
2544 Token::HashVar(h) => {
2545 self.advance();
2546 TieTarget::Hash(h)
2547 }
2548 Token::ArrayVar(a) => {
2549 self.advance();
2550 TieTarget::Array(a)
2551 }
2552 Token::ScalarVar(s) => {
2553 self.advance();
2554 TieTarget::Scalar(s)
2555 }
2556 tok => {
2557 return Err(self.syntax_err(
2558 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
2559 self.peek_line(),
2560 ));
2561 }
2562 };
2563 self.expect(&Token::Comma)?;
2564 let class = self.parse_assign_expr()?;
2565 let mut args = Vec::new();
2566 while self.eat(&Token::Comma) {
2567 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
2568 break;
2569 }
2570 args.push(self.parse_assign_expr()?);
2571 }
2572 self.eat(&Token::Semicolon);
2573 Ok(Statement {
2574 label: None,
2575 kind: StmtKind::Tie {
2576 target,
2577 class,
2578 args,
2579 },
2580 line,
2581 })
2582 }
2583
2584 fn parse_given(&mut self) -> PerlResult<Statement> {
2586 let line = self.peek_line();
2587 self.advance();
2588 self.expect(&Token::LParen)?;
2589 let topic = self.parse_expression()?;
2590 self.expect(&Token::RParen)?;
2591 let body = self.parse_block()?;
2592 self.eat(&Token::Semicolon);
2593 Ok(Statement {
2594 label: None,
2595 kind: StmtKind::Given { topic, body },
2596 line,
2597 })
2598 }
2599
2600 fn parse_when_stmt(&mut self) -> PerlResult<Statement> {
2602 let line = self.peek_line();
2603 self.advance();
2604 self.expect(&Token::LParen)?;
2605 let cond = self.parse_expression()?;
2606 self.expect(&Token::RParen)?;
2607 let body = self.parse_block()?;
2608 self.eat(&Token::Semicolon);
2609 Ok(Statement {
2610 label: None,
2611 kind: StmtKind::When { cond, body },
2612 line,
2613 })
2614 }
2615
2616 fn parse_default_stmt(&mut self) -> PerlResult<Statement> {
2618 let line = self.peek_line();
2619 self.advance();
2620 let body = self.parse_block()?;
2621 self.eat(&Token::Semicolon);
2622 Ok(Statement {
2623 label: None,
2624 kind: StmtKind::DefaultCase { body },
2625 line,
2626 })
2627 }
2628
2629 fn parse_algebraic_match_expr(&mut self, line: usize) -> PerlResult<Expr> {
2631 self.expect(&Token::LParen)?;
2632 let subject = self.parse_expression()?;
2633 self.expect(&Token::RParen)?;
2634 self.expect(&Token::LBrace)?;
2635 let mut arms = Vec::new();
2636 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2637 if self.eat(&Token::Semicolon) {
2638 continue;
2639 }
2640 let pattern = self.parse_match_pattern()?;
2641 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
2642 self.advance();
2643 Some(Box::new(self.parse_assign_expr()?))
2646 } else {
2647 None
2648 };
2649 self.expect(&Token::FatArrow)?;
2650 let body = self.parse_assign_expr()?;
2652 arms.push(MatchArm {
2653 pattern,
2654 guard,
2655 body,
2656 });
2657 self.eat(&Token::Comma);
2658 }
2659 self.expect(&Token::RBrace)?;
2660 Ok(Expr {
2661 kind: ExprKind::AlgebraicMatch {
2662 subject: Box::new(subject),
2663 arms,
2664 },
2665 line,
2666 })
2667 }
2668
2669 fn parse_match_pattern(&mut self) -> PerlResult<MatchPattern> {
2670 match self.peek().clone() {
2671 Token::Regex(pattern, flags, _delim) => {
2672 self.advance();
2673 Ok(MatchPattern::Regex { pattern, flags })
2674 }
2675 Token::Ident(ref s) if s == "_" => {
2676 self.advance();
2677 Ok(MatchPattern::Any)
2678 }
2679 Token::Ident(ref s) if s == "Some" => {
2680 self.advance();
2681 self.expect(&Token::LParen)?;
2682 let name = self.parse_scalar_var_name()?;
2683 self.expect(&Token::RParen)?;
2684 Ok(MatchPattern::OptionSome(name))
2685 }
2686 Token::LBracket => self.parse_match_array_pattern(),
2687 Token::LBrace => self.parse_match_hash_pattern(),
2688 Token::LParen => {
2689 self.advance();
2690 let e = self.parse_expression()?;
2691 self.expect(&Token::RParen)?;
2692 Ok(MatchPattern::Value(Box::new(e)))
2693 }
2694 _ => {
2695 let e = self.parse_assign_expr()?;
2696 Ok(MatchPattern::Value(Box::new(e)))
2697 }
2698 }
2699 }
2700
2701 fn parse_match_array_elems_until_rbracket(&mut self) -> PerlResult<Vec<MatchArrayElem>> {
2703 let mut elems = Vec::new();
2704 if self.eat(&Token::RBracket) {
2705 return Ok(vec![]);
2706 }
2707 loop {
2708 if matches!(self.peek(), Token::Star) {
2709 self.advance();
2710 elems.push(MatchArrayElem::Rest);
2711 self.eat(&Token::Comma);
2712 if !matches!(self.peek(), Token::RBracket) {
2713 return Err(self.syntax_err(
2714 "`*` must be the last element in an array match pattern",
2715 self.peek_line(),
2716 ));
2717 }
2718 self.expect(&Token::RBracket)?;
2719 return Ok(elems);
2720 }
2721 if let Token::ArrayVar(name) = self.peek().clone() {
2722 self.advance();
2723 elems.push(MatchArrayElem::RestBind(name));
2724 self.eat(&Token::Comma);
2725 if !matches!(self.peek(), Token::RBracket) {
2726 return Err(self.syntax_err(
2727 "`@name` rest bind must be the last element in an array match pattern",
2728 self.peek_line(),
2729 ));
2730 }
2731 self.expect(&Token::RBracket)?;
2732 return Ok(elems);
2733 }
2734 if let Token::ScalarVar(name) = self.peek().clone() {
2735 self.advance();
2736 elems.push(MatchArrayElem::CaptureScalar(name));
2737 if self.eat(&Token::Comma) {
2738 if matches!(self.peek(), Token::RBracket) {
2739 break;
2740 }
2741 continue;
2742 }
2743 break;
2744 }
2745 let e = self.parse_assign_expr()?;
2746 elems.push(MatchArrayElem::Expr(e));
2747 if self.eat(&Token::Comma) {
2748 if matches!(self.peek(), Token::RBracket) {
2749 break;
2750 }
2751 continue;
2752 }
2753 break;
2754 }
2755 self.expect(&Token::RBracket)?;
2756 Ok(elems)
2757 }
2758
2759 fn parse_match_array_pattern(&mut self) -> PerlResult<MatchPattern> {
2760 self.expect(&Token::LBracket)?;
2761 let elems = self.parse_match_array_elems_until_rbracket()?;
2762 Ok(MatchPattern::Array(elems))
2763 }
2764
2765 fn parse_match_hash_pattern(&mut self) -> PerlResult<MatchPattern> {
2766 self.expect(&Token::LBrace)?;
2767 let mut pairs = Vec::new();
2768 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2769 if self.eat(&Token::Semicolon) {
2770 continue;
2771 }
2772 let key = self.parse_assign_expr()?;
2773 self.expect(&Token::FatArrow)?;
2774 match self.advance().0 {
2775 Token::Ident(ref s) if s == "_" => {
2776 pairs.push(MatchHashPair::KeyOnly { key });
2777 }
2778 Token::ScalarVar(name) => {
2779 pairs.push(MatchHashPair::Capture { key, name });
2780 }
2781 tok => {
2782 return Err(self.syntax_err(
2783 format!(
2784 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
2785 tok
2786 ),
2787 self.peek_line(),
2788 ));
2789 }
2790 }
2791 self.eat(&Token::Comma);
2792 }
2793 self.expect(&Token::RBrace)?;
2794 Ok(MatchPattern::Hash(pairs))
2795 }
2796
2797 fn parse_eval_timeout(&mut self) -> PerlResult<Statement> {
2799 let line = self.peek_line();
2800 self.advance();
2801 let timeout = self.parse_postfix()?;
2802 let body = self.parse_block_or_bareword_block_no_args()?;
2803 self.eat(&Token::Semicolon);
2804 Ok(Statement {
2805 label: None,
2806 kind: StmtKind::EvalTimeout { timeout, body },
2807 line,
2808 })
2809 }
2810
2811 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
2812 match &mut cond.kind {
2813 ExprKind::Match {
2814 flags, scalar_g, ..
2815 } if flags.contains('g') => {
2816 *scalar_g = true;
2817 }
2818 ExprKind::UnaryOp {
2819 op: UnaryOp::LogNot,
2820 expr,
2821 } => {
2822 if let ExprKind::Match {
2823 flags, scalar_g, ..
2824 } = &mut expr.kind
2825 {
2826 if flags.contains('g') {
2827 *scalar_g = true;
2828 }
2829 }
2830 }
2831 _ => {}
2832 }
2833 }
2834
2835 fn parse_if(&mut self) -> PerlResult<Statement> {
2836 let line = self.peek_line();
2837 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
2839 if crate::compat_mode() {
2840 return Err(self.syntax_err(
2841 "`if let` is a stryke extension (disabled by --compat)",
2842 line,
2843 ));
2844 }
2845 return self.parse_if_let(line);
2846 }
2847 self.expect(&Token::LParen)?;
2848 let mut cond = self.parse_expression()?;
2849 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
2850 self.expect(&Token::RParen)?;
2851 let body = self.parse_block()?;
2852
2853 let mut elsifs = Vec::new();
2854 let mut else_block = None;
2855
2856 loop {
2857 if let Token::Ident(ref kw) = self.peek().clone() {
2858 if kw == "elsif" {
2859 self.advance();
2860 self.expect(&Token::LParen)?;
2861 let mut c = self.parse_expression()?;
2862 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
2863 self.expect(&Token::RParen)?;
2864 let b = self.parse_block()?;
2865 elsifs.push((c, b));
2866 continue;
2867 }
2868 if kw == "else" {
2869 self.advance();
2870 else_block = Some(self.parse_block()?);
2871 }
2872 }
2873 break;
2874 }
2875
2876 Ok(Statement {
2877 label: None,
2878 kind: StmtKind::If {
2879 condition: cond,
2880 body,
2881 elsifs,
2882 else_block,
2883 },
2884 line,
2885 })
2886 }
2887
2888 fn parse_if_let(&mut self, line: usize) -> PerlResult<Statement> {
2890 self.advance(); let pattern = self.parse_match_pattern()?;
2892 self.expect(&Token::Assign)?;
2893 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
2895 let rhs = self.parse_assign_expr();
2896 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
2897 let rhs = rhs?;
2898 let then_block = self.parse_block()?;
2899 let else_block_opt = match self.peek().clone() {
2900 Token::Ident(ref kw) if kw == "else" => {
2901 self.advance();
2902 Some(self.parse_block()?)
2903 }
2904 Token::Ident(ref kw) if kw == "elsif" => {
2905 return Err(self.syntax_err(
2906 "`if let` does not support `elsif`; use `else { }` or a full `match`",
2907 self.peek_line(),
2908 ));
2909 }
2910 _ => None,
2911 };
2912 let then_expr = Self::expr_do_anon_block(then_block, line);
2913 let else_expr = if let Some(eb) = else_block_opt {
2914 Self::expr_do_anon_block(eb, line)
2915 } else {
2916 Expr {
2917 kind: ExprKind::Undef,
2918 line,
2919 }
2920 };
2921 let arms = vec![
2922 MatchArm {
2923 pattern,
2924 guard: None,
2925 body: then_expr,
2926 },
2927 MatchArm {
2928 pattern: MatchPattern::Any,
2929 guard: None,
2930 body: else_expr,
2931 },
2932 ];
2933 Ok(Statement {
2934 label: None,
2935 kind: StmtKind::Expression(Expr {
2936 kind: ExprKind::AlgebraicMatch {
2937 subject: Box::new(rhs),
2938 arms,
2939 },
2940 line,
2941 }),
2942 line,
2943 })
2944 }
2945
2946 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
2947 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
2948 Expr {
2949 kind: ExprKind::Do(Box::new(Expr {
2950 kind: ExprKind::CodeRef {
2951 params: vec![],
2952 body: block,
2953 },
2954 line: inner_line,
2955 })),
2956 line: outer_line,
2957 }
2958 }
2959
2960 fn parse_unless(&mut self) -> PerlResult<Statement> {
2961 let line = self.peek_line();
2962 self.advance(); self.expect(&Token::LParen)?;
2964 let mut cond = self.parse_expression()?;
2965 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
2966 self.expect(&Token::RParen)?;
2967 let body = self.parse_block()?;
2968 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
2969 if kw == "else" {
2970 self.advance();
2971 Some(self.parse_block()?)
2972 } else {
2973 None
2974 }
2975 } else {
2976 None
2977 };
2978 Ok(Statement {
2979 label: None,
2980 kind: StmtKind::Unless {
2981 condition: cond,
2982 body,
2983 else_block,
2984 },
2985 line,
2986 })
2987 }
2988
2989 fn parse_while(&mut self) -> PerlResult<Statement> {
2990 let line = self.peek_line();
2991 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
2993 if crate::compat_mode() {
2994 return Err(self.syntax_err(
2995 "`while let` is a stryke extension (disabled by --compat)",
2996 line,
2997 ));
2998 }
2999 return self.parse_while_let(line);
3000 }
3001 self.expect(&Token::LParen)?;
3002 let mut cond = self.parse_expression()?;
3003 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3004 self.expect(&Token::RParen)?;
3005 let body = self.parse_block()?;
3006 let continue_block = self.parse_optional_continue_block()?;
3007 Ok(Statement {
3008 label: None,
3009 kind: StmtKind::While {
3010 condition: cond,
3011 body,
3012 label: None,
3013 continue_block,
3014 },
3015 line,
3016 })
3017 }
3018
3019 fn parse_while_let(&mut self, line: usize) -> PerlResult<Statement> {
3022 self.advance(); let pattern = self.parse_match_pattern()?;
3024 self.expect(&Token::Assign)?;
3025 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
3026 let rhs = self.parse_assign_expr();
3027 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
3028 let rhs = rhs?;
3029 let mut user_body = self.parse_block()?;
3030 let continue_block = self.parse_optional_continue_block()?;
3031 user_body.push(Statement::new(
3032 StmtKind::Expression(Expr {
3033 kind: ExprKind::Integer(1),
3034 line,
3035 }),
3036 line,
3037 ));
3038 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
3039 let match_expr = Expr {
3040 kind: ExprKind::AlgebraicMatch {
3041 subject: Box::new(rhs),
3042 arms: vec![
3043 MatchArm {
3044 pattern,
3045 guard: None,
3046 body: Self::expr_do_anon_block(user_body, line),
3047 },
3048 MatchArm {
3049 pattern: MatchPattern::Any,
3050 guard: None,
3051 body: Expr {
3052 kind: ExprKind::Integer(0),
3053 line,
3054 },
3055 },
3056 ],
3057 },
3058 line,
3059 };
3060 let my_stmt = Statement::new(
3061 StmtKind::My(vec![VarDecl {
3062 sigil: Sigil::Scalar,
3063 name: tmp.clone(),
3064 initializer: Some(match_expr),
3065 frozen: false,
3066 type_annotation: None,
3067 }]),
3068 line,
3069 );
3070 let unless_last = Statement::new(
3071 StmtKind::Unless {
3072 condition: Expr {
3073 kind: ExprKind::ScalarVar(tmp),
3074 line,
3075 },
3076 body: vec![Statement::new(StmtKind::Last(None), line)],
3077 else_block: None,
3078 },
3079 line,
3080 );
3081 Ok(Statement::new(
3082 StmtKind::While {
3083 condition: Expr {
3084 kind: ExprKind::Integer(1),
3085 line,
3086 },
3087 body: vec![my_stmt, unless_last],
3088 label: None,
3089 continue_block,
3090 },
3091 line,
3092 ))
3093 }
3094
3095 fn parse_until(&mut self) -> PerlResult<Statement> {
3096 let line = self.peek_line();
3097 self.advance(); self.expect(&Token::LParen)?;
3099 let mut cond = self.parse_expression()?;
3100 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3101 self.expect(&Token::RParen)?;
3102 let body = self.parse_block()?;
3103 let continue_block = self.parse_optional_continue_block()?;
3104 Ok(Statement {
3105 label: None,
3106 kind: StmtKind::Until {
3107 condition: cond,
3108 body,
3109 label: None,
3110 continue_block,
3111 },
3112 line,
3113 })
3114 }
3115
3116 fn parse_optional_continue_block(&mut self) -> PerlResult<Option<Block>> {
3118 if let Token::Ident(ref kw) = self.peek().clone() {
3119 if kw == "continue" {
3120 self.advance();
3121 return Ok(Some(self.parse_block()?));
3122 }
3123 }
3124 Ok(None)
3125 }
3126
3127 fn parse_for_or_foreach(&mut self) -> PerlResult<Statement> {
3128 let line = self.peek_line();
3129 self.advance(); match self.peek() {
3135 Token::LParen => {
3136 let saved = self.pos;
3141 self.advance(); let mut depth = 1;
3144 let mut has_semi = false;
3145 let mut scan = self.pos;
3146 while scan < self.tokens.len() {
3147 match &self.tokens[scan].0 {
3148 Token::LParen => depth += 1,
3149 Token::RParen => {
3150 depth -= 1;
3151 if depth == 0 {
3152 break;
3153 }
3154 }
3155 Token::Semicolon if depth == 1 => {
3156 has_semi = true;
3157 break;
3158 }
3159 _ => {}
3160 }
3161 scan += 1;
3162 }
3163 self.pos = saved;
3164
3165 if has_semi {
3166 self.parse_c_style_for(line)
3167 } else {
3168 self.expect(&Token::LParen)?;
3170 let list = self.parse_expression()?;
3171 self.expect(&Token::RParen)?;
3172 let body = self.parse_block()?;
3173 let continue_block = self.parse_optional_continue_block()?;
3174 Ok(Statement {
3175 label: None,
3176 kind: StmtKind::Foreach {
3177 var: "_".to_string(),
3178 list,
3179 body,
3180 label: None,
3181 continue_block,
3182 },
3183 line,
3184 })
3185 }
3186 }
3187 Token::Ident(ref kw) if kw == "my" => {
3188 self.advance(); let var = self.parse_scalar_var_name()?;
3190 self.expect(&Token::LParen)?;
3191 let list = self.parse_expression()?;
3192 self.expect(&Token::RParen)?;
3193 let body = self.parse_block()?;
3194 let continue_block = self.parse_optional_continue_block()?;
3195 Ok(Statement {
3196 label: None,
3197 kind: StmtKind::Foreach {
3198 var,
3199 list,
3200 body,
3201 label: None,
3202 continue_block,
3203 },
3204 line,
3205 })
3206 }
3207 Token::ScalarVar(_) => {
3208 let var = self.parse_scalar_var_name()?;
3209 self.expect(&Token::LParen)?;
3210 let list = self.parse_expression()?;
3211 self.expect(&Token::RParen)?;
3212 let body = self.parse_block()?;
3213 let continue_block = self.parse_optional_continue_block()?;
3214 Ok(Statement {
3215 label: None,
3216 kind: StmtKind::Foreach {
3217 var,
3218 list,
3219 body,
3220 label: None,
3221 continue_block,
3222 },
3223 line,
3224 })
3225 }
3226 _ => self.parse_c_style_for(line),
3227 }
3228 }
3229
3230 fn parse_c_style_for(&mut self, line: usize) -> PerlResult<Statement> {
3231 self.expect(&Token::LParen)?;
3232 let init = if self.eat(&Token::Semicolon) {
3233 None
3234 } else {
3235 let s = self.parse_statement()?;
3236 self.eat(&Token::Semicolon);
3237 Some(Box::new(s))
3238 };
3239 let mut condition = if matches!(self.peek(), Token::Semicolon) {
3240 None
3241 } else {
3242 Some(self.parse_expression()?)
3243 };
3244 if let Some(ref mut c) = condition {
3245 Self::mark_match_scalar_g_for_boolean_condition(c);
3246 }
3247 self.expect(&Token::Semicolon)?;
3248 let step = if matches!(self.peek(), Token::RParen) {
3249 None
3250 } else {
3251 Some(self.parse_expression()?)
3252 };
3253 self.expect(&Token::RParen)?;
3254 let body = self.parse_block()?;
3255 let continue_block = self.parse_optional_continue_block()?;
3256 Ok(Statement {
3257 label: None,
3258 kind: StmtKind::For {
3259 init,
3260 condition,
3261 step,
3262 body,
3263 label: None,
3264 continue_block,
3265 },
3266 line,
3267 })
3268 }
3269
3270 fn parse_foreach(&mut self) -> PerlResult<Statement> {
3271 let line = self.peek_line();
3272 self.advance(); let var = match self.peek() {
3274 Token::Ident(ref kw) if kw == "my" => {
3275 self.advance();
3276 self.parse_scalar_var_name()?
3277 }
3278 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
3279 _ => "_".to_string(),
3280 };
3281 self.expect(&Token::LParen)?;
3282 let list = self.parse_expression()?;
3283 self.expect(&Token::RParen)?;
3284 let body = self.parse_block()?;
3285 let continue_block = self.parse_optional_continue_block()?;
3286 Ok(Statement {
3287 label: None,
3288 kind: StmtKind::Foreach {
3289 var,
3290 list,
3291 body,
3292 label: None,
3293 continue_block,
3294 },
3295 line,
3296 })
3297 }
3298
3299 fn parse_scalar_var_name(&mut self) -> PerlResult<String> {
3300 match self.advance() {
3301 (Token::ScalarVar(name), _) => Ok(name),
3302 (tok, line) => {
3303 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
3304 }
3305 }
3306 }
3307
3308 fn parse_legacy_sub_prototype_tail(&mut self) -> PerlResult<String> {
3310 let mut s = String::new();
3311 loop {
3312 match self.peek().clone() {
3313 Token::RParen => {
3314 self.advance();
3315 break;
3316 }
3317 Token::Eof => {
3318 return Err(self.syntax_err(
3319 "Unterminated sub prototype (expected ')' before end of input)",
3320 self.peek_line(),
3321 ));
3322 }
3323 Token::ScalarVar(v) if v == ")" => {
3324 self.advance();
3327 s.push('$');
3328 if matches!(self.peek(), Token::LBrace) {
3329 break;
3330 }
3331 }
3332 Token::Ident(i) => {
3333 let i = i.clone();
3334 self.advance();
3335 s.push_str(&i);
3336 }
3337 Token::Semicolon => {
3338 self.advance();
3339 s.push(';');
3340 }
3341 Token::LParen => {
3342 self.advance();
3343 s.push('(');
3344 }
3345 Token::LBracket => {
3346 self.advance();
3347 s.push('[');
3348 }
3349 Token::RBracket => {
3350 self.advance();
3351 s.push(']');
3352 }
3353 Token::Backslash => {
3354 self.advance();
3355 s.push('\\');
3356 }
3357 Token::Comma => {
3358 self.advance();
3359 s.push(',');
3360 }
3361 Token::ScalarVar(v) => {
3362 let v = v.clone();
3363 self.advance();
3364 s.push('$');
3365 s.push_str(&v);
3366 }
3367 Token::ArrayVar(v) => {
3368 let v = v.clone();
3369 self.advance();
3370 s.push('@');
3371 s.push_str(&v);
3372 }
3373 Token::ArrayAt => {
3375 self.advance();
3376 s.push('@');
3377 }
3378 Token::HashVar(v) => {
3379 let v = v.clone();
3380 self.advance();
3381 s.push('%');
3382 s.push_str(&v);
3383 }
3384 Token::HashPercent => {
3385 self.advance();
3386 s.push('%');
3387 }
3388 Token::Plus => {
3389 self.advance();
3390 s.push('+');
3391 }
3392 Token::Minus => {
3393 self.advance();
3394 s.push('-');
3395 }
3396 Token::BitAnd => {
3397 self.advance();
3398 s.push('&');
3399 }
3400 tok => {
3401 return Err(self.syntax_err(
3402 format!("Unexpected token in sub prototype: {:?}", tok),
3403 self.peek_line(),
3404 ));
3405 }
3406 }
3407 }
3408 Ok(s)
3409 }
3410
3411 fn sub_signature_list_starts_here(&self) -> bool {
3412 match self.peek() {
3413 Token::LBrace | Token::LBracket => true,
3414 Token::ScalarVar(name) if name != "$$" && name != ")" => true,
3415 _ => false,
3416 }
3417 }
3418
3419 fn parse_sub_signature_hash_key(&mut self) -> PerlResult<String> {
3420 let (tok, line) = self.advance();
3421 match tok {
3422 Token::Ident(i) => Ok(i),
3423 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
3424 tok => Err(self.syntax_err(
3425 format!(
3426 "sub signature: expected hash key (identifier or string), got {:?}",
3427 tok
3428 ),
3429 line,
3430 )),
3431 }
3432 }
3433
3434 fn parse_sub_signature_param_list(&mut self) -> PerlResult<Vec<SubSigParam>> {
3435 let mut params = Vec::new();
3436 loop {
3437 if matches!(self.peek(), Token::RParen) {
3438 break;
3439 }
3440 match self.peek().clone() {
3441 Token::ScalarVar(name) => {
3442 if name == "$$" || name == ")" {
3443 return Err(self.syntax_err(
3444 format!(
3445 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
3446 ),
3447 self.peek_line(),
3448 ));
3449 }
3450 self.advance();
3451 let ty = if self.eat(&Token::Colon) {
3452 match self.peek() {
3453 Token::Ident(ref tname) => {
3454 let tname = tname.clone();
3455 self.advance();
3456 Some(match tname.as_str() {
3457 "Int" => PerlTypeName::Int,
3458 "Str" => PerlTypeName::Str,
3459 "Float" => PerlTypeName::Float,
3460 "Bool" => PerlTypeName::Bool,
3461 "Array" => PerlTypeName::Array,
3462 "Hash" => PerlTypeName::Hash,
3463 "Ref" => PerlTypeName::Ref,
3464 "Any" => PerlTypeName::Any,
3465 _ => PerlTypeName::Struct(tname),
3466 })
3467 }
3468 _ => {
3469 return Err(self.syntax_err(
3470 "expected type name after `:` in sub signature",
3471 self.peek_line(),
3472 ));
3473 }
3474 }
3475 } else {
3476 None
3477 };
3478 params.push(SubSigParam::Scalar(name, ty));
3479 }
3480 Token::LBracket => {
3481 self.advance();
3482 let elems = self.parse_match_array_elems_until_rbracket()?;
3483 params.push(SubSigParam::ArrayDestruct(elems));
3484 }
3485 Token::LBrace => {
3486 self.advance();
3487 let mut pairs = Vec::new();
3488 loop {
3489 if matches!(self.peek(), Token::RBrace | Token::Eof) {
3490 break;
3491 }
3492 if self.eat(&Token::Comma) {
3493 continue;
3494 }
3495 let key = self.parse_sub_signature_hash_key()?;
3496 self.expect(&Token::FatArrow)?;
3497 let bind = self.parse_scalar_var_name()?;
3498 pairs.push((key, bind));
3499 self.eat(&Token::Comma);
3500 }
3501 self.expect(&Token::RBrace)?;
3502 params.push(SubSigParam::HashDestruct(pairs));
3503 }
3504 tok => {
3505 return Err(self.syntax_err(
3506 format!(
3507 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
3508 tok
3509 ),
3510 self.peek_line(),
3511 ));
3512 }
3513 }
3514 match self.peek() {
3515 Token::Comma => {
3516 self.advance();
3517 if matches!(self.peek(), Token::RParen) {
3518 return Err(self.syntax_err(
3519 "trailing `,` before `)` in sub signature",
3520 self.peek_line(),
3521 ));
3522 }
3523 }
3524 Token::RParen => break,
3525 _ => {
3526 return Err(self.syntax_err(
3527 format!(
3528 "expected `,` or `)` after sub signature parameter, got {:?}",
3529 self.peek()
3530 ),
3531 self.peek_line(),
3532 ));
3533 }
3534 }
3535 }
3536 Ok(params)
3537 }
3538
3539 fn parse_sub_sig_or_prototype_opt(&mut self) -> PerlResult<(Vec<SubSigParam>, Option<String>)> {
3541 if !matches!(self.peek(), Token::LParen) {
3542 return Ok((vec![], None));
3543 }
3544 self.advance();
3545 if matches!(self.peek(), Token::RParen) {
3546 self.advance();
3547 return Ok((vec![], Some(String::new())));
3548 }
3549 if self.sub_signature_list_starts_here() {
3550 let params = self.parse_sub_signature_param_list()?;
3551 self.expect(&Token::RParen)?;
3552 return Ok((params, None));
3553 }
3554 let proto = self.parse_legacy_sub_prototype_tail()?;
3555 Ok((vec![], Some(proto)))
3556 }
3557
3558 fn parse_sub_attributes(&mut self) -> PerlResult<()> {
3560 while self.eat(&Token::Colon) {
3561 match self.advance() {
3562 (Token::Ident(_), _) => {}
3563 (tok, line) => {
3564 return Err(self.syntax_err(
3565 format!("Expected attribute name after `:`, got {:?}", tok),
3566 line,
3567 ));
3568 }
3569 }
3570 if self.eat(&Token::LParen) {
3571 let mut depth = 1usize;
3572 while depth > 0 {
3573 match self.advance().0 {
3574 Token::LParen => depth += 1,
3575 Token::RParen => {
3576 depth -= 1;
3577 }
3578 Token::Eof => {
3579 return Err(self.syntax_err(
3580 "Unterminated sub attribute argument list",
3581 self.peek_line(),
3582 ));
3583 }
3584 _ => {}
3585 }
3586 }
3587 }
3588 }
3589 Ok(())
3590 }
3591
3592 fn parse_sub_decl(&mut self) -> PerlResult<Statement> {
3593 let line = self.peek_line();
3594 self.advance(); match self.peek().clone() {
3596 Token::Ident(_) => {
3597 let name = self.parse_package_qualified_identifier()?;
3598 self.declared_subs.insert(name.clone());
3599 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
3600 self.parse_sub_attributes()?;
3601 let body = self.parse_block()?;
3602 Ok(Statement {
3603 label: None,
3604 kind: StmtKind::SubDecl {
3605 name,
3606 params,
3607 body,
3608 prototype,
3609 },
3610 line,
3611 })
3612 }
3613 Token::LParen | Token::LBrace | Token::Colon => {
3614 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
3616 self.parse_sub_attributes()?;
3617 let body = self.parse_block()?;
3618 Ok(Statement {
3619 label: None,
3620 kind: StmtKind::Expression(Expr {
3621 kind: ExprKind::CodeRef { params, body },
3622 line,
3623 }),
3624 line,
3625 })
3626 }
3627 tok => Err(self.syntax_err(
3628 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
3629 self.peek_line(),
3630 )),
3631 }
3632 }
3633
3634 fn parse_struct_decl(&mut self) -> PerlResult<Statement> {
3636 let line = self.peek_line();
3637 self.advance(); let name = match self.advance() {
3639 (Token::Ident(n), _) => n,
3640 (tok, err_line) => {
3641 return Err(
3642 self.syntax_err(format!("Expected struct name, got {:?}", tok), err_line)
3643 )
3644 }
3645 };
3646 self.expect(&Token::LBrace)?;
3647 let mut fields = Vec::new();
3648 let mut methods = Vec::new();
3649 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3650 let is_method = match self.peek() {
3652 Token::Ident(s) => s == "fn" || s == "sub",
3653 _ => false,
3654 };
3655 if is_method {
3656 self.advance(); let method_name = match self.advance() {
3658 (Token::Ident(n), _) => n,
3659 (tok, err_line) => {
3660 return Err(self
3661 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
3662 }
3663 };
3664 let params = if self.eat(&Token::LParen) {
3666 let p = self.parse_sub_signature_param_list()?;
3667 self.expect(&Token::RParen)?;
3668 p
3669 } else {
3670 Vec::new()
3671 };
3672 let body = self.parse_block()?;
3674 methods.push(crate::ast::StructMethod {
3675 name: method_name,
3676 params,
3677 body,
3678 });
3679 self.eat(&Token::Comma);
3681 self.eat(&Token::Semicolon);
3682 continue;
3683 }
3684
3685 let field_name = match self.advance() {
3686 (Token::Ident(n), _) => n,
3687 (tok, err_line) => {
3688 return Err(
3689 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
3690 )
3691 }
3692 };
3693 let ty = if self.eat(&Token::FatArrow) {
3695 self.parse_type_name()?
3696 } else {
3697 crate::ast::PerlTypeName::Any
3698 };
3699 let default = if self.eat(&Token::Assign) {
3700 Some(self.parse_ternary()?)
3702 } else {
3703 None
3704 };
3705 fields.push(StructField {
3706 name: field_name,
3707 ty,
3708 default,
3709 });
3710 if !self.eat(&Token::Comma) {
3711 self.eat(&Token::Semicolon);
3713 }
3714 }
3715 self.expect(&Token::RBrace)?;
3716 self.eat(&Token::Semicolon);
3717 Ok(Statement {
3718 label: None,
3719 kind: StmtKind::StructDecl {
3720 def: StructDef {
3721 name,
3722 fields,
3723 methods,
3724 },
3725 },
3726 line,
3727 })
3728 }
3729
3730 fn parse_enum_decl(&mut self) -> PerlResult<Statement> {
3732 let line = self.peek_line();
3733 self.advance(); let name = match self.advance() {
3735 (Token::Ident(n), _) => n,
3736 (tok, err_line) => {
3737 return Err(self.syntax_err(format!("Expected enum name, got {:?}", tok), err_line))
3738 }
3739 };
3740 self.expect(&Token::LBrace)?;
3741 let mut variants = Vec::new();
3742 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3743 let variant_name = match self.advance() {
3744 (Token::Ident(n), _) => n,
3745 (tok, err_line) => {
3746 return Err(
3747 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
3748 )
3749 }
3750 };
3751 let ty = if self.eat(&Token::FatArrow) {
3752 Some(self.parse_type_name()?)
3753 } else {
3754 None
3755 };
3756 variants.push(EnumVariant {
3757 name: variant_name,
3758 ty,
3759 });
3760 if !self.eat(&Token::Comma) {
3761 self.eat(&Token::Semicolon);
3762 }
3763 }
3764 self.expect(&Token::RBrace)?;
3765 self.eat(&Token::Semicolon);
3766 Ok(Statement {
3767 label: None,
3768 kind: StmtKind::EnumDecl {
3769 def: EnumDef { name, variants },
3770 },
3771 line,
3772 })
3773 }
3774
3775 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> PerlResult<Statement> {
3777 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
3778 let line = self.peek_line();
3779 self.advance(); let name = match self.advance() {
3781 (Token::Ident(n), _) => n,
3782 (tok, err_line) => {
3783 return Err(self.syntax_err(format!("Expected class name, got {:?}", tok), err_line))
3784 }
3785 };
3786
3787 let mut extends = Vec::new();
3789 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
3790 self.advance(); loop {
3792 match self.advance() {
3793 (Token::Ident(parent), _) => extends.push(parent),
3794 (tok, err_line) => {
3795 return Err(self.syntax_err(
3796 format!("Expected parent class name after `extends`, got {:?}", tok),
3797 err_line,
3798 ))
3799 }
3800 }
3801 if !self.eat(&Token::Comma) {
3802 break;
3803 }
3804 }
3805 }
3806
3807 let mut implements = Vec::new();
3809 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
3810 self.advance(); loop {
3812 match self.advance() {
3813 (Token::Ident(trait_name), _) => implements.push(trait_name),
3814 (tok, err_line) => {
3815 return Err(self.syntax_err(
3816 format!("Expected trait name after `impl`, got {:?}", tok),
3817 err_line,
3818 ))
3819 }
3820 }
3821 if !self.eat(&Token::Comma) {
3822 break;
3823 }
3824 }
3825 }
3826
3827 self.expect(&Token::LBrace)?;
3828 let mut fields = Vec::new();
3829 let mut methods = Vec::new();
3830 let mut static_fields = Vec::new();
3831
3832 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3833 let visibility = match self.peek() {
3835 Token::Ident(ref s) if s == "pub" => {
3836 self.advance();
3837 Visibility::Public
3838 }
3839 Token::Ident(ref s) if s == "priv" => {
3840 self.advance();
3841 Visibility::Private
3842 }
3843 Token::Ident(ref s) if s == "prot" => {
3844 self.advance();
3845 Visibility::Protected
3846 }
3847 _ => Visibility::Public, };
3849
3850 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
3852 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
3856 return Err(self.syntax_err(
3858 "use `fn Self.name` for static methods, not `static fn`",
3859 self.peek_line(),
3860 ));
3861 }
3862
3863 let field_name = match self.advance() {
3864 (Token::Ident(n), _) => n,
3865 (tok, err_line) => {
3866 return Err(self.syntax_err(
3867 format!("Expected static field name, got {:?}", tok),
3868 err_line,
3869 ))
3870 }
3871 };
3872
3873 let ty = if self.eat(&Token::Colon) {
3874 self.parse_type_name()?
3875 } else {
3876 crate::ast::PerlTypeName::Any
3877 };
3878
3879 let default = if self.eat(&Token::Assign) {
3880 Some(self.parse_ternary()?)
3881 } else {
3882 None
3883 };
3884
3885 static_fields.push(ClassStaticField {
3886 name: field_name,
3887 ty,
3888 visibility,
3889 default,
3890 });
3891
3892 if !self.eat(&Token::Comma) {
3893 self.eat(&Token::Semicolon);
3894 }
3895 continue;
3896 }
3897
3898 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
3900 if method_is_final {
3901 self.advance(); }
3903
3904 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
3906 if is_method {
3907 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
3911 if is_static {
3912 self.advance(); self.expect(&Token::Dot)?;
3914 }
3915
3916 let method_name = match self.advance() {
3917 (Token::Ident(n), _) => n,
3918 (tok, err_line) => {
3919 return Err(self
3920 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
3921 }
3922 };
3923
3924 let params = if self.eat(&Token::LParen) {
3926 let p = self.parse_sub_signature_param_list()?;
3927 self.expect(&Token::RParen)?;
3928 p
3929 } else {
3930 Vec::new()
3931 };
3932
3933 let body = if matches!(self.peek(), Token::LBrace) {
3935 Some(self.parse_block()?)
3936 } else {
3937 None
3938 };
3939
3940 methods.push(ClassMethod {
3941 name: method_name,
3942 params,
3943 body,
3944 visibility,
3945 is_static,
3946 is_final: method_is_final,
3947 });
3948 self.eat(&Token::Comma);
3949 self.eat(&Token::Semicolon);
3950 continue;
3951 } else if method_is_final {
3952 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
3953 }
3954
3955 let field_name = match self.advance() {
3957 (Token::Ident(n), _) => n,
3958 (tok, err_line) => {
3959 return Err(
3960 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
3961 )
3962 }
3963 };
3964
3965 let ty = if self.eat(&Token::Colon) {
3967 self.parse_type_name()?
3968 } else {
3969 crate::ast::PerlTypeName::Any
3970 };
3971
3972 let default = if self.eat(&Token::Assign) {
3974 Some(self.parse_ternary()?)
3975 } else {
3976 None
3977 };
3978
3979 fields.push(ClassField {
3980 name: field_name,
3981 ty,
3982 visibility,
3983 default,
3984 });
3985
3986 if !self.eat(&Token::Comma) {
3987 self.eat(&Token::Semicolon);
3988 }
3989 }
3990
3991 self.expect(&Token::RBrace)?;
3992 self.eat(&Token::Semicolon);
3993
3994 Ok(Statement {
3995 label: None,
3996 kind: StmtKind::ClassDecl {
3997 def: ClassDef {
3998 name,
3999 is_abstract,
4000 is_final,
4001 extends,
4002 implements,
4003 fields,
4004 methods,
4005 static_fields,
4006 },
4007 },
4008 line,
4009 })
4010 }
4011
4012 fn parse_trait_decl(&mut self) -> PerlResult<Statement> {
4014 use crate::ast::{ClassMethod, TraitDef, Visibility};
4015 let line = self.peek_line();
4016 self.advance(); let name = match self.advance() {
4018 (Token::Ident(n), _) => n,
4019 (tok, err_line) => {
4020 return Err(self.syntax_err(format!("Expected trait name, got {:?}", tok), err_line))
4021 }
4022 };
4023
4024 self.expect(&Token::LBrace)?;
4025 let mut methods = Vec::new();
4026
4027 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4028 let visibility = match self.peek() {
4030 Token::Ident(ref s) if s == "pub" => {
4031 self.advance();
4032 Visibility::Public
4033 }
4034 Token::Ident(ref s) if s == "priv" => {
4035 self.advance();
4036 Visibility::Private
4037 }
4038 Token::Ident(ref s) if s == "prot" => {
4039 self.advance();
4040 Visibility::Protected
4041 }
4042 _ => Visibility::Public,
4043 };
4044
4045 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
4047 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
4048 }
4049 self.advance(); let method_name = match self.advance() {
4052 (Token::Ident(n), _) => n,
4053 (tok, err_line) => {
4054 return Err(
4055 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
4056 )
4057 }
4058 };
4059
4060 let params = if self.eat(&Token::LParen) {
4062 let p = self.parse_sub_signature_param_list()?;
4063 self.expect(&Token::RParen)?;
4064 p
4065 } else {
4066 Vec::new()
4067 };
4068
4069 let body = if matches!(self.peek(), Token::LBrace) {
4071 Some(self.parse_block()?)
4072 } else {
4073 None
4074 };
4075
4076 methods.push(ClassMethod {
4077 name: method_name,
4078 params,
4079 body,
4080 visibility,
4081 is_static: false,
4082 is_final: false,
4083 });
4084
4085 self.eat(&Token::Comma);
4086 self.eat(&Token::Semicolon);
4087 }
4088
4089 self.expect(&Token::RBrace)?;
4090 self.eat(&Token::Semicolon);
4091
4092 Ok(Statement {
4093 label: None,
4094 kind: StmtKind::TraitDecl {
4095 def: TraitDef { name, methods },
4096 },
4097 line,
4098 })
4099 }
4100
4101 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
4102 match &target.kind {
4103 ExprKind::ScalarVar(name) => Some(VarDecl {
4104 sigil: Sigil::Scalar,
4105 name: name.clone(),
4106 initializer: None,
4107 frozen: false,
4108 type_annotation: None,
4109 }),
4110 ExprKind::ArrayVar(name) => Some(VarDecl {
4111 sigil: Sigil::Array,
4112 name: name.clone(),
4113 initializer: None,
4114 frozen: false,
4115 type_annotation: None,
4116 }),
4117 ExprKind::HashVar(name) => Some(VarDecl {
4118 sigil: Sigil::Hash,
4119 name: name.clone(),
4120 initializer: None,
4121 frozen: false,
4122 type_annotation: None,
4123 }),
4124 ExprKind::Typeglob(name) => Some(VarDecl {
4125 sigil: Sigil::Typeglob,
4126 name: name.clone(),
4127 initializer: None,
4128 frozen: false,
4129 type_annotation: None,
4130 }),
4131 _ => None,
4132 }
4133 }
4134
4135 fn parse_decl_array_destructure(
4136 &mut self,
4137 keyword: &str,
4138 line: usize,
4139 ) -> PerlResult<Statement> {
4140 self.expect(&Token::LBracket)?;
4141 let elems = self.parse_match_array_elems_until_rbracket()?;
4142 self.expect(&Token::Assign)?;
4143 self.suppress_scalar_hash_brace += 1;
4144 let rhs = self.parse_expression()?;
4145 self.suppress_scalar_hash_brace -= 1;
4146 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
4147 self.parse_stmt_postfix_modifier(stmt)
4148 }
4149
4150 fn parse_decl_hash_destructure(&mut self, keyword: &str, line: usize) -> PerlResult<Statement> {
4151 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
4152 unreachable!("parse_match_hash_pattern returns Hash");
4153 };
4154 self.expect(&Token::Assign)?;
4155 self.suppress_scalar_hash_brace += 1;
4156 let rhs = self.parse_expression()?;
4157 self.suppress_scalar_hash_brace -= 1;
4158 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
4159 self.parse_stmt_postfix_modifier(stmt)
4160 }
4161
4162 fn desugar_array_destructure(
4163 &mut self,
4164 keyword: &str,
4165 line: usize,
4166 elems: Vec<MatchArrayElem>,
4167 rhs: Expr,
4168 ) -> PerlResult<Statement> {
4169 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
4170 let mut stmts: Vec<Statement> = Vec::new();
4171 stmts.push(destructure_stmt_from_var_decls(
4172 keyword,
4173 vec![VarDecl {
4174 sigil: Sigil::Scalar,
4175 name: tmp.clone(),
4176 initializer: Some(rhs),
4177 frozen: false,
4178 type_annotation: None,
4179 }],
4180 line,
4181 ));
4182
4183 let has_rest = elems
4184 .iter()
4185 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
4186 let fixed_slots = elems
4187 .iter()
4188 .filter(|e| {
4189 matches!(
4190 e,
4191 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
4192 )
4193 })
4194 .count();
4195 if !has_rest {
4196 let cond = Expr {
4197 kind: ExprKind::BinOp {
4198 left: Box::new(destructure_expr_array_len(&tmp, line)),
4199 op: BinOp::NumEq,
4200 right: Box::new(Expr {
4201 kind: ExprKind::Integer(fixed_slots as i64),
4202 line,
4203 }),
4204 },
4205 line,
4206 };
4207 stmts.push(destructure_stmt_unless_die(
4208 line,
4209 cond,
4210 "array destructure: length mismatch",
4211 ));
4212 }
4213
4214 let mut idx: i64 = 0;
4215 for elem in elems {
4216 match elem {
4217 MatchArrayElem::Rest => break,
4218 MatchArrayElem::RestBind(name) => {
4219 let list_source = Expr {
4220 kind: ExprKind::Deref {
4221 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
4222 kind: Sigil::Array,
4223 },
4224 line,
4225 };
4226 let last_ix = Expr {
4227 kind: ExprKind::BinOp {
4228 left: Box::new(destructure_expr_array_len(&tmp, line)),
4229 op: BinOp::Sub,
4230 right: Box::new(Expr {
4231 kind: ExprKind::Integer(1),
4232 line,
4233 }),
4234 },
4235 line,
4236 };
4237 let range = Expr {
4238 kind: ExprKind::Range {
4239 from: Box::new(Expr {
4240 kind: ExprKind::Integer(idx),
4241 line,
4242 }),
4243 to: Box::new(last_ix),
4244 exclusive: false,
4245 },
4246 line,
4247 };
4248 let slice = Expr {
4249 kind: ExprKind::AnonymousListSlice {
4250 source: Box::new(list_source),
4251 indices: vec![range],
4252 },
4253 line,
4254 };
4255 stmts.push(destructure_stmt_from_var_decls(
4256 keyword,
4257 vec![VarDecl {
4258 sigil: Sigil::Array,
4259 name,
4260 initializer: Some(slice),
4261 frozen: false,
4262 type_annotation: None,
4263 }],
4264 line,
4265 ));
4266 break;
4267 }
4268 MatchArrayElem::CaptureScalar(name) => {
4269 let arrow = Expr {
4270 kind: ExprKind::ArrowDeref {
4271 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
4272 index: Box::new(Expr {
4273 kind: ExprKind::Integer(idx),
4274 line,
4275 }),
4276 kind: DerefKind::Array,
4277 },
4278 line,
4279 };
4280 stmts.push(destructure_stmt_from_var_decls(
4281 keyword,
4282 vec![VarDecl {
4283 sigil: Sigil::Scalar,
4284 name,
4285 initializer: Some(arrow),
4286 frozen: false,
4287 type_annotation: None,
4288 }],
4289 line,
4290 ));
4291 idx += 1;
4292 }
4293 MatchArrayElem::Expr(e) => {
4294 let elem_subj = Expr {
4295 kind: ExprKind::ArrowDeref {
4296 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
4297 index: Box::new(Expr {
4298 kind: ExprKind::Integer(idx),
4299 line,
4300 }),
4301 kind: DerefKind::Array,
4302 },
4303 line,
4304 };
4305 let match_expr = Expr {
4306 kind: ExprKind::AlgebraicMatch {
4307 subject: Box::new(elem_subj),
4308 arms: vec![
4309 MatchArm {
4310 pattern: MatchPattern::Value(Box::new(e.clone())),
4311 guard: None,
4312 body: Expr {
4313 kind: ExprKind::Integer(0),
4314 line,
4315 },
4316 },
4317 MatchArm {
4318 pattern: MatchPattern::Any,
4319 guard: None,
4320 body: Expr {
4321 kind: ExprKind::Die(vec![Expr {
4322 kind: ExprKind::String(
4323 "array destructure: element pattern mismatch"
4324 .to_string(),
4325 ),
4326 line,
4327 }]),
4328 line,
4329 },
4330 },
4331 ],
4332 },
4333 line,
4334 };
4335 stmts.push(Statement {
4336 label: None,
4337 kind: StmtKind::Expression(match_expr),
4338 line,
4339 });
4340 idx += 1;
4341 }
4342 }
4343 }
4344
4345 Ok(Statement {
4346 label: None,
4347 kind: StmtKind::StmtGroup(stmts),
4348 line,
4349 })
4350 }
4351
4352 fn desugar_hash_destructure(
4353 &mut self,
4354 keyword: &str,
4355 line: usize,
4356 pairs: Vec<MatchHashPair>,
4357 rhs: Expr,
4358 ) -> PerlResult<Statement> {
4359 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
4360 let mut stmts: Vec<Statement> = Vec::new();
4361 stmts.push(destructure_stmt_from_var_decls(
4362 keyword,
4363 vec![VarDecl {
4364 sigil: Sigil::Scalar,
4365 name: tmp.clone(),
4366 initializer: Some(rhs),
4367 frozen: false,
4368 type_annotation: None,
4369 }],
4370 line,
4371 ));
4372
4373 for pair in pairs {
4374 match pair {
4375 MatchHashPair::KeyOnly { key } => {
4376 let exists_op = Expr {
4377 kind: ExprKind::Exists(Box::new(Expr {
4378 kind: ExprKind::ArrowDeref {
4379 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
4380 index: Box::new(key),
4381 kind: DerefKind::Hash,
4382 },
4383 line,
4384 })),
4385 line,
4386 };
4387 stmts.push(destructure_stmt_unless_die(
4388 line,
4389 exists_op,
4390 "hash destructure: missing required key",
4391 ));
4392 }
4393 MatchHashPair::Capture { key, name } => {
4394 let init = Expr {
4395 kind: ExprKind::ArrowDeref {
4396 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
4397 index: Box::new(key),
4398 kind: DerefKind::Hash,
4399 },
4400 line,
4401 };
4402 stmts.push(destructure_stmt_from_var_decls(
4403 keyword,
4404 vec![VarDecl {
4405 sigil: Sigil::Scalar,
4406 name,
4407 initializer: Some(init),
4408 frozen: false,
4409 type_annotation: None,
4410 }],
4411 line,
4412 ));
4413 }
4414 }
4415 }
4416
4417 Ok(Statement {
4418 label: None,
4419 kind: StmtKind::StmtGroup(stmts),
4420 line,
4421 })
4422 }
4423
4424 fn parse_my_our_local(
4425 &mut self,
4426 keyword: &str,
4427 allow_type_annotation: bool,
4428 ) -> PerlResult<Statement> {
4429 let line = self.peek_line();
4430 self.advance(); if keyword == "local"
4433 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
4434 {
4435 let target = self.parse_postfix()?;
4436 let mut initializer: Option<Expr> = None;
4437 if self.eat(&Token::Assign) {
4438 initializer = Some(self.parse_expression()?);
4439 } else if matches!(
4440 self.peek(),
4441 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
4442 ) {
4443 if matches!(&target.kind, ExprKind::Typeglob(_)) {
4444 return Err(self.syntax_err(
4445 "compound assignment on typeglob declaration is not supported",
4446 self.peek_line(),
4447 ));
4448 }
4449 let op = match self.peek().clone() {
4450 Token::OrAssign => BinOp::LogOr,
4451 Token::DefinedOrAssign => BinOp::DefinedOr,
4452 Token::AndAssign => BinOp::LogAnd,
4453 _ => unreachable!(),
4454 };
4455 self.advance();
4456 let rhs = self.parse_assign_expr()?;
4457 let tgt_line = target.line;
4458 initializer = Some(Expr {
4459 kind: ExprKind::CompoundAssign {
4460 target: Box::new(target.clone()),
4461 op,
4462 value: Box::new(rhs),
4463 },
4464 line: tgt_line,
4465 });
4466 }
4467
4468 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
4469 decl.initializer = initializer;
4470 StmtKind::Local(vec![decl])
4471 } else {
4472 StmtKind::LocalExpr {
4473 target,
4474 initializer,
4475 }
4476 };
4477 let stmt = Statement {
4478 label: None,
4479 kind,
4480 line,
4481 };
4482 return self.parse_stmt_postfix_modifier(stmt);
4483 }
4484
4485 if matches!(self.peek(), Token::LBracket) {
4486 return self.parse_decl_array_destructure(keyword, line);
4487 }
4488 if matches!(self.peek(), Token::LBrace) {
4489 return self.parse_decl_hash_destructure(keyword, line);
4490 }
4491
4492 let mut decls = Vec::new();
4493
4494 if self.eat(&Token::LParen) {
4495 while !matches!(self.peek(), Token::RParen | Token::Eof) {
4497 let decl = self.parse_var_decl(allow_type_annotation)?;
4498 decls.push(decl);
4499 if !self.eat(&Token::Comma) {
4500 break;
4501 }
4502 }
4503 self.expect(&Token::RParen)?;
4504 } else {
4505 decls.push(self.parse_var_decl(allow_type_annotation)?);
4506 }
4507
4508 if self.eat(&Token::Assign) {
4510 if keyword == "our" && decls.len() == 1 {
4511 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
4512 self.advance();
4513 decls.push(self.parse_var_decl(allow_type_annotation)?);
4514 if !self.eat(&Token::Assign) {
4515 return Err(self.syntax_err(
4516 "expected `=` after `our` in chained our-declaration",
4517 self.peek_line(),
4518 ));
4519 }
4520 }
4521 }
4522 let val = self.parse_expression()?;
4523 if decls.len() == 1 {
4524 decls[0].initializer = Some(val);
4525 } else {
4526 for decl in &mut decls {
4527 decl.initializer = Some(val.clone());
4528 }
4529 }
4530 } else if decls.len() == 1 {
4531 let op = match self.peek().clone() {
4533 Token::OrAssign => Some(BinOp::LogOr),
4534 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
4535 Token::AndAssign => Some(BinOp::LogAnd),
4536 _ => None,
4537 };
4538 if let Some(op) = op {
4539 let d = &decls[0];
4540 if matches!(d.sigil, Sigil::Typeglob) {
4541 return Err(self.syntax_err(
4542 "compound assignment on typeglob declaration is not supported",
4543 self.peek_line(),
4544 ));
4545 }
4546 self.advance();
4547 let rhs = self.parse_assign_expr()?;
4548 let target = Expr {
4549 kind: match d.sigil {
4550 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
4551 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
4552 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
4553 Sigil::Typeglob => unreachable!(),
4554 },
4555 line,
4556 };
4557 decls[0].initializer = Some(Expr {
4558 kind: ExprKind::CompoundAssign {
4559 target: Box::new(target),
4560 op,
4561 value: Box::new(rhs),
4562 },
4563 line,
4564 });
4565 }
4566 }
4567
4568 let kind = match keyword {
4569 "my" => StmtKind::My(decls),
4570 "mysync" => StmtKind::MySync(decls),
4571 "our" => StmtKind::Our(decls),
4572 "local" => StmtKind::Local(decls),
4573 "state" => StmtKind::State(decls),
4574 _ => unreachable!(),
4575 };
4576 let stmt = Statement {
4577 label: None,
4578 kind,
4579 line,
4580 };
4581 self.parse_stmt_postfix_modifier(stmt)
4583 }
4584
4585 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> PerlResult<VarDecl> {
4586 let mut decl = match self.advance() {
4587 (Token::ScalarVar(name), _) => VarDecl {
4588 sigil: Sigil::Scalar,
4589 name,
4590 initializer: None,
4591 frozen: false,
4592 type_annotation: None,
4593 },
4594 (Token::ArrayVar(name), _) => VarDecl {
4595 sigil: Sigil::Array,
4596 name,
4597 initializer: None,
4598 frozen: false,
4599 type_annotation: None,
4600 },
4601 (Token::HashVar(name), _) => VarDecl {
4602 sigil: Sigil::Hash,
4603 name,
4604 initializer: None,
4605 frozen: false,
4606 type_annotation: None,
4607 },
4608 (Token::Star, _line) => {
4609 let name = match self.advance() {
4610 (Token::Ident(n), _) => n,
4611 (tok, l) => {
4612 return Err(self
4613 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
4614 }
4615 };
4616 VarDecl {
4617 sigil: Sigil::Typeglob,
4618 name,
4619 initializer: None,
4620 frozen: false,
4621 type_annotation: None,
4622 }
4623 }
4624 (Token::Ident(ref kw), _) if kw == "undef" => VarDecl {
4629 sigil: Sigil::Scalar,
4630 name: format!("__undef_sink_{}", self.pos),
4634 initializer: None,
4635 frozen: false,
4636 type_annotation: None,
4637 },
4638 (tok, line) => {
4639 return Err(self.syntax_err(
4640 format!("Expected variable in declaration, got {:?}", tok),
4641 line,
4642 ));
4643 }
4644 };
4645 if allow_type_annotation && self.eat(&Token::Colon) {
4646 let ty = self.parse_type_name()?;
4647 if decl.sigil != Sigil::Scalar {
4648 return Err(self.syntax_err(
4649 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
4650 self.peek_line(),
4651 ));
4652 }
4653 decl.type_annotation = Some(ty);
4654 }
4655 Ok(decl)
4656 }
4657
4658 fn parse_type_name(&mut self) -> PerlResult<PerlTypeName> {
4659 match self.advance() {
4660 (Token::Ident(name), _) => match name.as_str() {
4661 "Int" => Ok(PerlTypeName::Int),
4662 "Str" => Ok(PerlTypeName::Str),
4663 "Float" => Ok(PerlTypeName::Float),
4664 "Bool" => Ok(PerlTypeName::Bool),
4665 "Array" => Ok(PerlTypeName::Array),
4666 "Hash" => Ok(PerlTypeName::Hash),
4667 "Ref" => Ok(PerlTypeName::Ref),
4668 "Any" => Ok(PerlTypeName::Any),
4669 _ => Ok(PerlTypeName::Struct(name)),
4670 },
4671 (tok, err_line) => Err(self.syntax_err(
4672 format!("Expected type name after `:`, got {:?}", tok),
4673 err_line,
4674 )),
4675 }
4676 }
4677
4678 fn parse_package(&mut self) -> PerlResult<Statement> {
4679 let line = self.peek_line();
4680 self.advance(); let name = match self.advance() {
4682 (Token::Ident(n), _) => n,
4683 (tok, line) => {
4684 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
4685 }
4686 };
4687 let mut full_name = name;
4689 while self.eat(&Token::PackageSep) {
4690 if let (Token::Ident(part), _) = self.advance() {
4691 full_name = format!("{}::{}", full_name, part);
4692 }
4693 }
4694 self.eat(&Token::Semicolon);
4695 Ok(Statement {
4696 label: None,
4697 kind: StmtKind::Package { name: full_name },
4698 line,
4699 })
4700 }
4701
4702 fn parse_use(&mut self) -> PerlResult<Statement> {
4703 let line = self.peek_line();
4704 self.advance(); let (tok, tok_line) = self.advance();
4706 match tok {
4707 Token::Float(v) => {
4708 self.eat(&Token::Semicolon);
4709 Ok(Statement {
4710 label: None,
4711 kind: StmtKind::UsePerlVersion { version: v },
4712 line,
4713 })
4714 }
4715 Token::Integer(n) => {
4716 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
4717 self.eat(&Token::Semicolon);
4718 Ok(Statement {
4719 label: None,
4720 kind: StmtKind::UsePerlVersion { version: n as f64 },
4721 line,
4722 })
4723 } else {
4724 Err(self.syntax_err(
4725 format!("Expected ';' after use VERSION (got {:?})", self.peek()),
4726 line,
4727 ))
4728 }
4729 }
4730 Token::Ident(n) => {
4731 let mut full_name = n;
4732 while self.eat(&Token::PackageSep) {
4733 if let (Token::Ident(part), _) = self.advance() {
4734 full_name = format!("{}::{}", full_name, part);
4735 }
4736 }
4737 if full_name == "overload" {
4738 let mut pairs = Vec::new();
4739 let mut parse_overload_pairs = |this: &mut Self| -> PerlResult<()> {
4740 loop {
4741 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
4742 {
4743 break;
4744 }
4745 let key_e = this.parse_assign_expr()?;
4746 this.expect(&Token::FatArrow)?;
4747 let val_e = this.parse_assign_expr()?;
4748 let key = this.expr_to_overload_key(&key_e)?;
4749 let val = this.expr_to_overload_sub(&val_e)?;
4750 pairs.push((key, val));
4751 if !this.eat(&Token::Comma) {
4752 break;
4753 }
4754 }
4755 Ok(())
4756 };
4757 if self.eat(&Token::LParen) {
4758 parse_overload_pairs(self)?;
4760 self.expect(&Token::RParen)?;
4761 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
4762 parse_overload_pairs(self)?;
4763 }
4764 self.eat(&Token::Semicolon);
4765 return Ok(Statement {
4766 label: None,
4767 kind: StmtKind::UseOverload { pairs },
4768 line,
4769 });
4770 }
4771 let mut imports = Vec::new();
4772 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
4773 && !self.next_is_new_stmt_keyword(tok_line)
4774 {
4775 loop {
4776 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
4777 break;
4778 }
4779 imports.push(self.parse_expression()?);
4780 if !self.eat(&Token::Comma) {
4781 break;
4782 }
4783 }
4784 }
4785 self.eat(&Token::Semicolon);
4786 Ok(Statement {
4787 label: None,
4788 kind: StmtKind::Use {
4789 module: full_name,
4790 imports,
4791 },
4792 line,
4793 })
4794 }
4795 other => Err(self.syntax_err(
4796 format!("Expected module name or version after use, got {:?}", other),
4797 tok_line,
4798 )),
4799 }
4800 }
4801
4802 fn parse_no(&mut self) -> PerlResult<Statement> {
4803 let line = self.peek_line();
4804 self.advance(); let module = match self.advance() {
4806 (Token::Ident(n), tok_line) => (n, tok_line),
4807 (tok, line) => {
4808 return Err(self.syntax_err(
4809 format!("Expected module name after no, got {:?}", tok),
4810 line,
4811 ))
4812 }
4813 };
4814 let (module_name, tok_line) = module;
4815 let mut full_name = module_name;
4816 while self.eat(&Token::PackageSep) {
4817 if let (Token::Ident(part), _) = self.advance() {
4818 full_name = format!("{}::{}", full_name, part);
4819 }
4820 }
4821 let mut imports = Vec::new();
4822 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
4823 && !self.next_is_new_stmt_keyword(tok_line)
4824 {
4825 loop {
4826 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
4827 break;
4828 }
4829 imports.push(self.parse_expression()?);
4830 if !self.eat(&Token::Comma) {
4831 break;
4832 }
4833 }
4834 }
4835 self.eat(&Token::Semicolon);
4836 Ok(Statement {
4837 label: None,
4838 kind: StmtKind::No {
4839 module: full_name,
4840 imports,
4841 },
4842 line,
4843 })
4844 }
4845
4846 fn parse_return(&mut self) -> PerlResult<Statement> {
4847 let line = self.peek_line();
4848 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
4850 None
4851 } else {
4852 Some(self.parse_assign_expr()?)
4854 };
4855 let stmt = Statement {
4857 label: None,
4858 kind: StmtKind::Return(val),
4859 line,
4860 };
4861 if let Token::Ident(ref kw) = self.peek().clone() {
4862 match kw.as_str() {
4863 "if" => {
4864 self.advance();
4865 let cond = self.parse_expression()?;
4866 self.eat(&Token::Semicolon);
4867 return Ok(Statement {
4868 label: None,
4869 kind: StmtKind::If {
4870 condition: cond,
4871 body: vec![stmt],
4872 elsifs: vec![],
4873 else_block: None,
4874 },
4875 line,
4876 });
4877 }
4878 "unless" => {
4879 self.advance();
4880 let cond = self.parse_expression()?;
4881 self.eat(&Token::Semicolon);
4882 return Ok(Statement {
4883 label: None,
4884 kind: StmtKind::Unless {
4885 condition: cond,
4886 body: vec![stmt],
4887 else_block: None,
4888 },
4889 line,
4890 });
4891 }
4892 _ => {}
4893 }
4894 }
4895 self.eat(&Token::Semicolon);
4896 Ok(stmt)
4897 }
4898
4899 fn parse_expression(&mut self) -> PerlResult<Expr> {
4902 self.parse_comma_expr()
4903 }
4904
4905 fn parse_comma_expr(&mut self) -> PerlResult<Expr> {
4906 let expr = self.parse_assign_expr()?;
4907 let mut exprs = vec![expr];
4908 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
4909 if matches!(
4910 self.peek(),
4911 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
4912 ) {
4913 break; }
4915 exprs.push(self.parse_assign_expr()?);
4916 }
4917 if exprs.len() == 1 {
4918 return Ok(exprs.pop().unwrap());
4919 }
4920 let line = exprs[0].line;
4921 Ok(Expr {
4922 kind: ExprKind::List(exprs),
4923 line,
4924 })
4925 }
4926
4927 fn parse_assign_expr(&mut self) -> PerlResult<Expr> {
4928 let expr = self.parse_ternary()?;
4929 let line = expr.line;
4930
4931 match self.peek().clone() {
4932 Token::Assign => {
4933 self.advance();
4934 let right = self.parse_assign_expr()?;
4935 Ok(Expr {
4936 kind: ExprKind::Assign {
4937 target: Box::new(expr),
4938 value: Box::new(right),
4939 },
4940 line,
4941 })
4942 }
4943 Token::PlusAssign => {
4944 self.advance();
4945 let r = self.parse_assign_expr()?;
4946 Ok(Expr {
4947 kind: ExprKind::CompoundAssign {
4948 target: Box::new(expr),
4949 op: BinOp::Add,
4950 value: Box::new(r),
4951 },
4952 line,
4953 })
4954 }
4955 Token::MinusAssign => {
4956 self.advance();
4957 let r = self.parse_assign_expr()?;
4958 Ok(Expr {
4959 kind: ExprKind::CompoundAssign {
4960 target: Box::new(expr),
4961 op: BinOp::Sub,
4962 value: Box::new(r),
4963 },
4964 line,
4965 })
4966 }
4967 Token::MulAssign => {
4968 self.advance();
4969 let r = self.parse_assign_expr()?;
4970 Ok(Expr {
4971 kind: ExprKind::CompoundAssign {
4972 target: Box::new(expr),
4973 op: BinOp::Mul,
4974 value: Box::new(r),
4975 },
4976 line,
4977 })
4978 }
4979 Token::DivAssign => {
4980 self.advance();
4981 let r = self.parse_assign_expr()?;
4982 Ok(Expr {
4983 kind: ExprKind::CompoundAssign {
4984 target: Box::new(expr),
4985 op: BinOp::Div,
4986 value: Box::new(r),
4987 },
4988 line,
4989 })
4990 }
4991 Token::ModAssign => {
4992 self.advance();
4993 let r = self.parse_assign_expr()?;
4994 Ok(Expr {
4995 kind: ExprKind::CompoundAssign {
4996 target: Box::new(expr),
4997 op: BinOp::Mod,
4998 value: Box::new(r),
4999 },
5000 line,
5001 })
5002 }
5003 Token::PowAssign => {
5004 self.advance();
5005 let r = self.parse_assign_expr()?;
5006 Ok(Expr {
5007 kind: ExprKind::CompoundAssign {
5008 target: Box::new(expr),
5009 op: BinOp::Pow,
5010 value: Box::new(r),
5011 },
5012 line,
5013 })
5014 }
5015 Token::DotAssign => {
5016 self.advance();
5017 let r = self.parse_assign_expr()?;
5018 Ok(Expr {
5019 kind: ExprKind::CompoundAssign {
5020 target: Box::new(expr),
5021 op: BinOp::Concat,
5022 value: Box::new(r),
5023 },
5024 line,
5025 })
5026 }
5027 Token::BitAndAssign => {
5028 self.advance();
5029 let r = self.parse_assign_expr()?;
5030 Ok(Expr {
5031 kind: ExprKind::CompoundAssign {
5032 target: Box::new(expr),
5033 op: BinOp::BitAnd,
5034 value: Box::new(r),
5035 },
5036 line,
5037 })
5038 }
5039 Token::BitOrAssign => {
5040 self.advance();
5041 let r = self.parse_assign_expr()?;
5042 Ok(Expr {
5043 kind: ExprKind::CompoundAssign {
5044 target: Box::new(expr),
5045 op: BinOp::BitOr,
5046 value: Box::new(r),
5047 },
5048 line,
5049 })
5050 }
5051 Token::XorAssign => {
5052 self.advance();
5053 let r = self.parse_assign_expr()?;
5054 Ok(Expr {
5055 kind: ExprKind::CompoundAssign {
5056 target: Box::new(expr),
5057 op: BinOp::BitXor,
5058 value: Box::new(r),
5059 },
5060 line,
5061 })
5062 }
5063 Token::ShiftLeftAssign => {
5064 self.advance();
5065 let r = self.parse_assign_expr()?;
5066 Ok(Expr {
5067 kind: ExprKind::CompoundAssign {
5068 target: Box::new(expr),
5069 op: BinOp::ShiftLeft,
5070 value: Box::new(r),
5071 },
5072 line,
5073 })
5074 }
5075 Token::ShiftRightAssign => {
5076 self.advance();
5077 let r = self.parse_assign_expr()?;
5078 Ok(Expr {
5079 kind: ExprKind::CompoundAssign {
5080 target: Box::new(expr),
5081 op: BinOp::ShiftRight,
5082 value: Box::new(r),
5083 },
5084 line,
5085 })
5086 }
5087 Token::OrAssign => {
5088 self.advance();
5089 let r = self.parse_assign_expr()?;
5090 Ok(Expr {
5091 kind: ExprKind::CompoundAssign {
5092 target: Box::new(expr),
5093 op: BinOp::LogOr,
5094 value: Box::new(r),
5095 },
5096 line,
5097 })
5098 }
5099 Token::DefinedOrAssign => {
5100 self.advance();
5101 let r = self.parse_assign_expr()?;
5102 Ok(Expr {
5103 kind: ExprKind::CompoundAssign {
5104 target: Box::new(expr),
5105 op: BinOp::DefinedOr,
5106 value: Box::new(r),
5107 },
5108 line,
5109 })
5110 }
5111 Token::AndAssign => {
5112 self.advance();
5113 let r = self.parse_assign_expr()?;
5114 Ok(Expr {
5115 kind: ExprKind::CompoundAssign {
5116 target: Box::new(expr),
5117 op: BinOp::LogAnd,
5118 value: Box::new(r),
5119 },
5120 line,
5121 })
5122 }
5123 _ => Ok(expr),
5124 }
5125 }
5126
5127 fn parse_ternary(&mut self) -> PerlResult<Expr> {
5128 let expr = self.parse_pipe_forward()?;
5129 if self.eat(&Token::Question) {
5130 let line = expr.line;
5131 let then_expr = self.parse_assign_expr()?;
5132 self.expect(&Token::Colon)?;
5133 let else_expr = self.parse_assign_expr()?;
5134 return Ok(Expr {
5135 kind: ExprKind::Ternary {
5136 condition: Box::new(expr),
5137 then_expr: Box::new(then_expr),
5138 else_expr: Box::new(else_expr),
5139 },
5140 line,
5141 });
5142 }
5143 Ok(expr)
5144 }
5145
5146 fn parse_pipe_forward(&mut self) -> PerlResult<Expr> {
5152 let mut left = self.parse_or_word()?;
5153 if self.no_pipe_forward_depth > 0 {
5159 return Ok(left);
5160 }
5161 while matches!(self.peek(), Token::PipeForward) {
5162 if crate::compat_mode() {
5163 return Err(self.syntax_err(
5164 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
5165 left.line,
5166 ));
5167 }
5168 let line = left.line;
5169 self.advance();
5170 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
5173 let right_result = self.parse_or_word();
5174 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
5175 let right = right_result?;
5176 left = self.pipe_forward_apply(left, right, line)?;
5177 }
5178 Ok(left)
5179 }
5180
5181 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> PerlResult<Expr> {
5203 let Expr { kind, line: rline } = rhs;
5204 let new_kind = match kind {
5205 ExprKind::FuncCall { name, mut args } => {
5207 match name.as_str() {
5208 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
5209 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
5210 | "shuffled" | "frequencies" | "freq" | "interleave" | "ddump"
5211 | "stringify" | "str" | "lines" | "words" | "chars" | "digits" | "numbers"
5212 | "graphemes" | "columns" | "sentences" | "paragraphs" | "sections"
5213 | "trim" | "avg" | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
5214 | "to_html" | "to_markdown" | "to_table" | "xopen" | "clip" | "sparkline"
5215 | "bar_chart" | "flame" | "stddev" | "squared" | "sq" | "square" | "cubed"
5216 | "cb" | "cube" | "normalize" | "snake_case" | "camel_case" | "kebab_case" => {
5217 if args.is_empty() {
5218 args.push(lhs);
5219 } else {
5220 args[0] = lhs;
5221 }
5222 }
5223 "chunked" | "windowed" => {
5224 if args.is_empty() {
5225 return Err(self.syntax_err(
5226 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
5227 line,
5228 ));
5229 }
5230 args.insert(0, lhs);
5231 }
5232 "List::Util::reduce" | "List::Util::fold" => {
5233 args.push(lhs);
5234 }
5235 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
5236 args.push(lhs);
5242 }
5243 "enumerate" | "dedup" => {
5244 args.insert(0, lhs);
5247 }
5248 "clamp" => {
5249 args.push(lhs);
5251 }
5252 "pfirst" | "pany" | "any" | "all" | "none" | "first" | "take_while"
5253 | "drop_while" | "skip_while" | "reject" | "tap" | "peek" | "group_by"
5254 | "chunk_by" | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
5255 if args.len() < 2 {
5256 return Err(self.syntax_err(
5257 format!(
5258 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
5259 ),
5260 line,
5261 ));
5262 }
5263 args[1] = lhs;
5264 }
5265 "take" | "head" | "tail" | "drop" | "List::Util::head" | "List::Util::tail" => {
5266 if args.is_empty() {
5267 return Err(self.syntax_err(
5268 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
5269 line,
5270 ));
5271 }
5272 args.insert(0, lhs);
5274 }
5275 _ => {
5276 args.insert(0, lhs);
5277 }
5278 }
5279 ExprKind::FuncCall { name, args }
5280 }
5281 ExprKind::MethodCall {
5282 object,
5283 method,
5284 mut args,
5285 super_call,
5286 } => {
5287 args.insert(0, lhs);
5288 ExprKind::MethodCall {
5289 object,
5290 method,
5291 args,
5292 super_call,
5293 }
5294 }
5295 ExprKind::IndirectCall {
5296 target,
5297 mut args,
5298 ampersand,
5299 pass_caller_arglist: _,
5300 } => {
5301 args.insert(0, lhs);
5302 ExprKind::IndirectCall {
5303 target,
5304 args,
5305 ampersand,
5306 pass_caller_arglist: false,
5309 }
5310 }
5311
5312 ExprKind::Print { handle, mut args } => {
5314 args.insert(0, lhs);
5315 ExprKind::Print { handle, args }
5316 }
5317 ExprKind::Say { handle, mut args } => {
5318 args.insert(0, lhs);
5319 ExprKind::Say { handle, args }
5320 }
5321 ExprKind::Printf { handle, mut args } => {
5322 args.insert(0, lhs);
5323 ExprKind::Printf { handle, args }
5324 }
5325 ExprKind::Die(mut args) => {
5326 args.insert(0, lhs);
5327 ExprKind::Die(args)
5328 }
5329 ExprKind::Warn(mut args) => {
5330 args.insert(0, lhs);
5331 ExprKind::Warn(args)
5332 }
5333
5334 ExprKind::Sprintf { format, mut args } => {
5340 args.insert(0, lhs);
5341 ExprKind::Sprintf { format, args }
5342 }
5343
5344 ExprKind::System(mut args) => {
5346 args.insert(0, lhs);
5347 ExprKind::System(args)
5348 }
5349 ExprKind::Exec(mut args) => {
5350 args.insert(0, lhs);
5351 ExprKind::Exec(args)
5352 }
5353 ExprKind::Unlink(mut args) => {
5354 args.insert(0, lhs);
5355 ExprKind::Unlink(args)
5356 }
5357 ExprKind::Chmod(mut args) => {
5358 args.insert(0, lhs);
5359 ExprKind::Chmod(args)
5360 }
5361 ExprKind::Chown(mut args) => {
5362 args.insert(0, lhs);
5363 ExprKind::Chown(args)
5364 }
5365 ExprKind::Glob(mut args) => {
5366 args.insert(0, lhs);
5367 ExprKind::Glob(args)
5368 }
5369 ExprKind::Files(mut args) => {
5370 args.insert(0, lhs);
5371 ExprKind::Files(args)
5372 }
5373 ExprKind::Filesf(mut args) => {
5374 args.insert(0, lhs);
5375 ExprKind::Filesf(args)
5376 }
5377 ExprKind::FilesfRecursive(mut args) => {
5378 args.insert(0, lhs);
5379 ExprKind::FilesfRecursive(args)
5380 }
5381 ExprKind::Dirs(mut args) => {
5382 args.insert(0, lhs);
5383 ExprKind::Dirs(args)
5384 }
5385 ExprKind::DirsRecursive(mut args) => {
5386 args.insert(0, lhs);
5387 ExprKind::DirsRecursive(args)
5388 }
5389 ExprKind::SymLinks(mut args) => {
5390 args.insert(0, lhs);
5391 ExprKind::SymLinks(args)
5392 }
5393 ExprKind::Sockets(mut args) => {
5394 args.insert(0, lhs);
5395 ExprKind::Sockets(args)
5396 }
5397 ExprKind::Pipes(mut args) => {
5398 args.insert(0, lhs);
5399 ExprKind::Pipes(args)
5400 }
5401 ExprKind::BlockDevices(mut args) => {
5402 args.insert(0, lhs);
5403 ExprKind::BlockDevices(args)
5404 }
5405 ExprKind::CharDevices(mut args) => {
5406 args.insert(0, lhs);
5407 ExprKind::CharDevices(args)
5408 }
5409 ExprKind::GlobPar { mut args, progress } => {
5410 args.insert(0, lhs);
5411 ExprKind::GlobPar { args, progress }
5412 }
5413 ExprKind::ParSed { mut args, progress } => {
5414 args.insert(0, lhs);
5415 ExprKind::ParSed { args, progress }
5416 }
5417
5418 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
5420 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
5421 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
5422 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
5423 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
5424 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
5425 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
5426 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
5427 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
5428 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
5429 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
5430 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
5431 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
5432 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
5433 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
5434 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
5435 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
5436 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
5437 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
5438 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
5439 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
5440 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
5441 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
5442 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
5443 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
5444 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
5445 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
5446 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
5447 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
5448 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
5449 ExprKind::ScalarReverse(_) => ExprKind::ScalarReverse(Box::new(lhs)),
5450 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
5451 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
5452 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
5453 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
5454 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
5455 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
5456 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
5457 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
5458 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
5459 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
5460 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
5461 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
5462 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
5463 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
5464 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
5465 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
5466 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
5467 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
5468 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
5469 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
5470
5471 ExprKind::MapExpr {
5473 block,
5474 list: _,
5475 flatten_array_refs,
5476 stream,
5477 } => ExprKind::MapExpr {
5478 block,
5479 list: Box::new(lhs),
5480 flatten_array_refs,
5481 stream,
5482 },
5483 ExprKind::MapExprComma {
5484 expr,
5485 list: _,
5486 flatten_array_refs,
5487 stream,
5488 } => ExprKind::MapExprComma {
5489 expr,
5490 list: Box::new(lhs),
5491 flatten_array_refs,
5492 stream,
5493 },
5494 ExprKind::GrepExpr {
5495 block,
5496 list: _,
5497 keyword,
5498 } => ExprKind::GrepExpr {
5499 block,
5500 list: Box::new(lhs),
5501 keyword,
5502 },
5503 ExprKind::GrepExprComma {
5504 expr,
5505 list: _,
5506 keyword,
5507 } => ExprKind::GrepExprComma {
5508 expr,
5509 list: Box::new(lhs),
5510 keyword,
5511 },
5512 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
5513 block,
5514 list: Box::new(lhs),
5515 },
5516 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
5517 cmp,
5518 list: Box::new(lhs),
5519 },
5520 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
5521 separator,
5522 list: Box::new(lhs),
5523 },
5524 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
5525 block,
5526 list: Box::new(lhs),
5527 },
5528 ExprKind::PMapExpr {
5529 block,
5530 list: _,
5531 progress,
5532 flat_outputs,
5533 on_cluster,
5534 } => ExprKind::PMapExpr {
5535 block,
5536 list: Box::new(lhs),
5537 progress,
5538 flat_outputs,
5539 on_cluster,
5540 },
5541 ExprKind::PMapChunkedExpr {
5542 chunk_size,
5543 block,
5544 list: _,
5545 progress,
5546 } => ExprKind::PMapChunkedExpr {
5547 chunk_size,
5548 block,
5549 list: Box::new(lhs),
5550 progress,
5551 },
5552 ExprKind::PGrepExpr {
5553 block,
5554 list: _,
5555 progress,
5556 } => ExprKind::PGrepExpr {
5557 block,
5558 list: Box::new(lhs),
5559 progress,
5560 },
5561 ExprKind::PForExpr {
5562 block,
5563 list: _,
5564 progress,
5565 } => ExprKind::PForExpr {
5566 block,
5567 list: Box::new(lhs),
5568 progress,
5569 },
5570 ExprKind::PSortExpr {
5571 cmp,
5572 list: _,
5573 progress,
5574 } => ExprKind::PSortExpr {
5575 cmp,
5576 list: Box::new(lhs),
5577 progress,
5578 },
5579 ExprKind::PReduceExpr {
5580 block,
5581 list: _,
5582 progress,
5583 } => ExprKind::PReduceExpr {
5584 block,
5585 list: Box::new(lhs),
5586 progress,
5587 },
5588 ExprKind::PcacheExpr {
5589 block,
5590 list: _,
5591 progress,
5592 } => ExprKind::PcacheExpr {
5593 block,
5594 list: Box::new(lhs),
5595 progress,
5596 },
5597 ExprKind::PReduceInitExpr {
5598 init,
5599 block,
5600 list: _,
5601 progress,
5602 } => ExprKind::PReduceInitExpr {
5603 init,
5604 block,
5605 list: Box::new(lhs),
5606 progress,
5607 },
5608 ExprKind::PMapReduceExpr {
5609 map_block,
5610 reduce_block,
5611 list: _,
5612 progress,
5613 } => ExprKind::PMapReduceExpr {
5614 map_block,
5615 reduce_block,
5616 list: Box::new(lhs),
5617 progress,
5618 },
5619
5620 ExprKind::Push { array, mut values } => {
5625 values.insert(0, lhs);
5626 ExprKind::Push { array, values }
5627 }
5628 ExprKind::Unshift { array, mut values } => {
5629 values.insert(0, lhs);
5630 ExprKind::Unshift { array, values }
5631 }
5632
5633 ExprKind::SplitExpr {
5635 pattern,
5636 string: _,
5637 limit,
5638 } => ExprKind::SplitExpr {
5639 pattern,
5640 string: Box::new(lhs),
5641 limit,
5642 },
5643
5644 ExprKind::Substitution {
5648 pattern,
5649 replacement,
5650 mut flags,
5651 expr: _,
5652 delim,
5653 } => {
5654 if !flags.contains('r') {
5655 flags.push('r');
5656 }
5657 ExprKind::Substitution {
5658 expr: Box::new(lhs),
5659 pattern,
5660 replacement,
5661 flags,
5662 delim,
5663 }
5664 }
5665 ExprKind::Transliterate {
5666 from,
5667 to,
5668 mut flags,
5669 expr: _,
5670 delim,
5671 } => {
5672 if !flags.contains('r') {
5673 flags.push('r');
5674 }
5675 ExprKind::Transliterate {
5676 expr: Box::new(lhs),
5677 from,
5678 to,
5679 flags,
5680 delim,
5681 }
5682 }
5683 ExprKind::Match {
5684 pattern,
5685 flags,
5686 scalar_g,
5687 expr: _,
5688 delim,
5689 } => ExprKind::Match {
5690 expr: Box::new(lhs),
5691 pattern,
5692 flags,
5693 scalar_g,
5694 delim,
5695 },
5696 ExprKind::Regex(pattern, flags) => ExprKind::Match {
5698 expr: Box::new(lhs),
5699 pattern,
5700 flags,
5701 scalar_g: false,
5702 delim: '/',
5703 },
5704
5705 ExprKind::Bareword(name) => match name.as_str() {
5707 "rv" | "reverse" | "reversed" => ExprKind::ReverseExpr(Box::new(lhs)),
5708 "rev" => ExprKind::ScalarReverse(Box::new(lhs)),
5709 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
5710 name: "uniq".to_string(),
5711 args: vec![lhs],
5712 },
5713 "fl" | "flatten" => ExprKind::FuncCall {
5714 name: "flatten".to_string(),
5715 args: vec![lhs],
5716 },
5717 _ => ExprKind::FuncCall {
5718 name,
5719 args: vec![lhs],
5720 },
5721 },
5722
5723 kind @ (ExprKind::ScalarVar(_)
5725 | ExprKind::ArrayElement { .. }
5726 | ExprKind::HashElement { .. }
5727 | ExprKind::Deref { .. }
5728 | ExprKind::ArrowDeref { .. }
5729 | ExprKind::CodeRef { .. }
5730 | ExprKind::SubroutineRef(_)
5731 | ExprKind::SubroutineCodeRef(_)
5732 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
5733 target: Box::new(Expr { kind, line: rline }),
5734 args: vec![lhs],
5735 ampersand: false,
5736 pass_caller_arglist: false,
5737 },
5738
5739 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
5743 ExprKind::IndirectCall {
5744 target: inner,
5745 args: vec![lhs],
5746 ampersand: false,
5747 pass_caller_arglist: false,
5748 }
5749 }
5750
5751 other => {
5752 return Err(self.syntax_err(
5753 format!(
5754 "right-hand side of `|>` must be a call, builtin, or coderef \
5755 expression (got {})",
5756 Self::expr_kind_name(&other)
5757 ),
5758 line,
5759 ));
5760 }
5761 };
5762 Ok(Expr {
5763 kind: new_kind,
5764 line,
5765 })
5766 }
5767
5768 fn expr_kind_name(kind: &ExprKind) -> &'static str {
5770 match kind {
5771 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
5772 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
5773 ExprKind::BinOp { .. } => "binary expression",
5774 ExprKind::UnaryOp { .. } => "unary expression",
5775 ExprKind::Ternary { .. } => "ternary expression",
5776 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
5777 ExprKind::List(_) => "list expression",
5778 ExprKind::Range { .. } => "range expression",
5779 _ => "expression",
5780 }
5781 }
5782
5783 fn parse_or_word(&mut self) -> PerlResult<Expr> {
5785 let mut left = self.parse_and_word()?;
5786 while matches!(self.peek(), Token::LogOrWord) {
5787 let line = left.line;
5788 self.advance();
5789 let right = self.parse_and_word()?;
5790 left = Expr {
5791 kind: ExprKind::BinOp {
5792 left: Box::new(left),
5793 op: BinOp::LogOrWord,
5794 right: Box::new(right),
5795 },
5796 line,
5797 };
5798 }
5799 Ok(left)
5800 }
5801
5802 fn parse_and_word(&mut self) -> PerlResult<Expr> {
5803 let mut left = self.parse_not_word()?;
5804 while matches!(self.peek(), Token::LogAndWord) {
5805 let line = left.line;
5806 self.advance();
5807 let right = self.parse_not_word()?;
5808 left = Expr {
5809 kind: ExprKind::BinOp {
5810 left: Box::new(left),
5811 op: BinOp::LogAndWord,
5812 right: Box::new(right),
5813 },
5814 line,
5815 };
5816 }
5817 Ok(left)
5818 }
5819
5820 fn parse_not_word(&mut self) -> PerlResult<Expr> {
5821 if matches!(self.peek(), Token::LogNotWord) {
5822 let line = self.peek_line();
5823 self.advance();
5824 let expr = self.parse_not_word()?;
5825 return Ok(Expr {
5826 kind: ExprKind::UnaryOp {
5827 op: UnaryOp::LogNotWord,
5828 expr: Box::new(expr),
5829 },
5830 line,
5831 });
5832 }
5833 self.parse_range()
5834 }
5835
5836 fn parse_log_or(&mut self) -> PerlResult<Expr> {
5837 let mut left = self.parse_log_and()?;
5838 loop {
5839 let op = match self.peek() {
5840 Token::LogOr => BinOp::LogOr,
5841 Token::DefinedOr => BinOp::DefinedOr,
5842 _ => break,
5843 };
5844 let line = left.line;
5845 self.advance();
5846 let right = self.parse_log_and()?;
5847 left = Expr {
5848 kind: ExprKind::BinOp {
5849 left: Box::new(left),
5850 op,
5851 right: Box::new(right),
5852 },
5853 line,
5854 };
5855 }
5856 Ok(left)
5857 }
5858
5859 fn parse_log_and(&mut self) -> PerlResult<Expr> {
5860 let mut left = self.parse_bit_or()?;
5861 while matches!(self.peek(), Token::LogAnd) {
5862 let line = left.line;
5863 self.advance();
5864 let right = self.parse_bit_or()?;
5865 left = Expr {
5866 kind: ExprKind::BinOp {
5867 left: Box::new(left),
5868 op: BinOp::LogAnd,
5869 right: Box::new(right),
5870 },
5871 line,
5872 };
5873 }
5874 Ok(left)
5875 }
5876
5877 fn parse_bit_or(&mut self) -> PerlResult<Expr> {
5878 let mut left = self.parse_bit_xor()?;
5879 while matches!(self.peek(), Token::BitOr) {
5880 let line = left.line;
5881 self.advance();
5882 let right = self.parse_bit_xor()?;
5883 left = Expr {
5884 kind: ExprKind::BinOp {
5885 left: Box::new(left),
5886 op: BinOp::BitOr,
5887 right: Box::new(right),
5888 },
5889 line,
5890 };
5891 }
5892 Ok(left)
5893 }
5894
5895 fn parse_bit_xor(&mut self) -> PerlResult<Expr> {
5896 let mut left = self.parse_bit_and()?;
5897 while matches!(self.peek(), Token::BitXor) {
5898 let line = left.line;
5899 self.advance();
5900 let right = self.parse_bit_and()?;
5901 left = Expr {
5902 kind: ExprKind::BinOp {
5903 left: Box::new(left),
5904 op: BinOp::BitXor,
5905 right: Box::new(right),
5906 },
5907 line,
5908 };
5909 }
5910 Ok(left)
5911 }
5912
5913 fn parse_bit_and(&mut self) -> PerlResult<Expr> {
5914 let mut left = self.parse_equality()?;
5915 while matches!(self.peek(), Token::BitAnd) {
5916 let line = left.line;
5917 self.advance();
5918 let right = self.parse_equality()?;
5919 left = Expr {
5920 kind: ExprKind::BinOp {
5921 left: Box::new(left),
5922 op: BinOp::BitAnd,
5923 right: Box::new(right),
5924 },
5925 line,
5926 };
5927 }
5928 Ok(left)
5929 }
5930
5931 fn parse_equality(&mut self) -> PerlResult<Expr> {
5932 let mut left = self.parse_comparison()?;
5933 loop {
5934 let op = match self.peek() {
5935 Token::NumEq => BinOp::NumEq,
5936 Token::NumNe => BinOp::NumNe,
5937 Token::StrEq => BinOp::StrEq,
5938 Token::StrNe => BinOp::StrNe,
5939 Token::Spaceship => BinOp::Spaceship,
5940 Token::StrCmp => BinOp::StrCmp,
5941 _ => break,
5942 };
5943 let line = left.line;
5944 self.advance();
5945 let right = self.parse_comparison()?;
5946 left = Expr {
5947 kind: ExprKind::BinOp {
5948 left: Box::new(left),
5949 op,
5950 right: Box::new(right),
5951 },
5952 line,
5953 };
5954 }
5955 Ok(left)
5956 }
5957
5958 fn parse_comparison(&mut self) -> PerlResult<Expr> {
5959 let mut left = self.parse_shift()?;
5960 loop {
5961 let op = match self.peek() {
5962 Token::NumLt => BinOp::NumLt,
5963 Token::NumGt => BinOp::NumGt,
5964 Token::NumLe => BinOp::NumLe,
5965 Token::NumGe => BinOp::NumGe,
5966 Token::StrLt => BinOp::StrLt,
5967 Token::StrGt => BinOp::StrGt,
5968 Token::StrLe => BinOp::StrLe,
5969 Token::StrGe => BinOp::StrGe,
5970 _ => break,
5971 };
5972 let line = left.line;
5973 self.advance();
5974 let right = self.parse_shift()?;
5975 left = Expr {
5976 kind: ExprKind::BinOp {
5977 left: Box::new(left),
5978 op,
5979 right: Box::new(right),
5980 },
5981 line,
5982 };
5983 }
5984 Ok(left)
5985 }
5986
5987 fn parse_shift(&mut self) -> PerlResult<Expr> {
5988 let mut left = self.parse_addition()?;
5989 loop {
5990 let op = match self.peek() {
5991 Token::ShiftLeft => BinOp::ShiftLeft,
5992 Token::ShiftRight => BinOp::ShiftRight,
5993 _ => break,
5994 };
5995 let line = left.line;
5996 self.advance();
5997 let right = self.parse_addition()?;
5998 left = Expr {
5999 kind: ExprKind::BinOp {
6000 left: Box::new(left),
6001 op,
6002 right: Box::new(right),
6003 },
6004 line,
6005 };
6006 }
6007 Ok(left)
6008 }
6009
6010 fn parse_addition(&mut self) -> PerlResult<Expr> {
6011 let mut left = self.parse_multiplication()?;
6012 loop {
6013 let op = match self.peek() {
6014 Token::Plus => BinOp::Add,
6015 Token::Minus => BinOp::Sub,
6016 Token::Dot => BinOp::Concat,
6017 _ => break,
6018 };
6019 let line = left.line;
6020 self.advance();
6021 let right = self.parse_multiplication()?;
6022 left = Expr {
6023 kind: ExprKind::BinOp {
6024 left: Box::new(left),
6025 op,
6026 right: Box::new(right),
6027 },
6028 line,
6029 };
6030 }
6031 Ok(left)
6032 }
6033
6034 fn parse_multiplication(&mut self) -> PerlResult<Expr> {
6035 let mut left = self.parse_regex_bind()?;
6036 loop {
6037 let op = match self.peek() {
6038 Token::Star => BinOp::Mul,
6039 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
6040 Token::Percent => BinOp::Mod,
6041 Token::X => {
6042 let line = left.line;
6043 self.advance();
6044 let right = self.parse_regex_bind()?;
6045 left = Expr {
6046 kind: ExprKind::Repeat {
6047 expr: Box::new(left),
6048 count: Box::new(right),
6049 },
6050 line,
6051 };
6052 continue;
6053 }
6054 _ => break,
6055 };
6056 let line = left.line;
6057 self.advance();
6058 let right = self.parse_regex_bind()?;
6059 left = Expr {
6060 kind: ExprKind::BinOp {
6061 left: Box::new(left),
6062 op,
6063 right: Box::new(right),
6064 },
6065 line,
6066 };
6067 }
6068 Ok(left)
6069 }
6070
6071 fn parse_regex_bind(&mut self) -> PerlResult<Expr> {
6072 let left = self.parse_unary()?;
6073 match self.peek() {
6074 Token::BindMatch => {
6075 let line = left.line;
6076 self.advance();
6077 match self.peek().clone() {
6078 Token::Regex(pattern, flags, delim) => {
6079 self.advance();
6080 Ok(Expr {
6081 kind: ExprKind::Match {
6082 expr: Box::new(left),
6083 pattern,
6084 flags,
6085 scalar_g: false,
6086 delim,
6087 },
6088 line,
6089 })
6090 }
6091 Token::Ident(ref s) if s.starts_with('\x00') => {
6092 let (Token::Ident(encoded), _) = self.advance() else {
6093 unreachable!()
6094 };
6095 let parts: Vec<&str> = encoded.split('\x00').collect();
6096 if parts.len() >= 4 && parts[1] == "s" {
6097 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
6098 Ok(Expr {
6099 kind: ExprKind::Substitution {
6100 expr: Box::new(left),
6101 pattern: parts[2].to_string(),
6102 replacement: parts[3].to_string(),
6103 flags: parts.get(4).unwrap_or(&"").to_string(),
6104 delim,
6105 },
6106 line,
6107 })
6108 } else if parts.len() >= 4 && parts[1] == "tr" {
6109 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
6110 Ok(Expr {
6111 kind: ExprKind::Transliterate {
6112 expr: Box::new(left),
6113 from: parts[2].to_string(),
6114 to: parts[3].to_string(),
6115 flags: parts.get(4).unwrap_or(&"").to_string(),
6116 delim,
6117 },
6118 line,
6119 })
6120 } else {
6121 Err(self.syntax_err("Invalid regex binding", line))
6122 }
6123 }
6124 _ => {
6125 let rhs = self.parse_unary()?;
6126 Ok(Expr {
6127 kind: ExprKind::BinOp {
6128 left: Box::new(left),
6129 op: BinOp::BindMatch,
6130 right: Box::new(rhs),
6131 },
6132 line,
6133 })
6134 }
6135 }
6136 }
6137 Token::BindNotMatch => {
6138 let line = left.line;
6139 self.advance();
6140 match self.peek().clone() {
6141 Token::Regex(pattern, flags, delim) => {
6142 self.advance();
6143 Ok(Expr {
6144 kind: ExprKind::UnaryOp {
6145 op: UnaryOp::LogNot,
6146 expr: Box::new(Expr {
6147 kind: ExprKind::Match {
6148 expr: Box::new(left),
6149 pattern,
6150 flags,
6151 scalar_g: false,
6152 delim,
6153 },
6154 line,
6155 }),
6156 },
6157 line,
6158 })
6159 }
6160 Token::Ident(ref s) if s.starts_with('\x00') => {
6161 let (Token::Ident(encoded), _) = self.advance() else {
6162 unreachable!()
6163 };
6164 let parts: Vec<&str> = encoded.split('\x00').collect();
6165 if parts.len() >= 4 && parts[1] == "s" {
6166 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
6167 Ok(Expr {
6168 kind: ExprKind::UnaryOp {
6169 op: UnaryOp::LogNot,
6170 expr: Box::new(Expr {
6171 kind: ExprKind::Substitution {
6172 expr: Box::new(left),
6173 pattern: parts[2].to_string(),
6174 replacement: parts[3].to_string(),
6175 flags: parts.get(4).unwrap_or(&"").to_string(),
6176 delim,
6177 },
6178 line,
6179 }),
6180 },
6181 line,
6182 })
6183 } else if parts.len() >= 4 && parts[1] == "tr" {
6184 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
6185 Ok(Expr {
6186 kind: ExprKind::UnaryOp {
6187 op: UnaryOp::LogNot,
6188 expr: Box::new(Expr {
6189 kind: ExprKind::Transliterate {
6190 expr: Box::new(left),
6191 from: parts[2].to_string(),
6192 to: parts[3].to_string(),
6193 flags: parts.get(4).unwrap_or(&"").to_string(),
6194 delim,
6195 },
6196 line,
6197 }),
6198 },
6199 line,
6200 })
6201 } else {
6202 Err(self.syntax_err("Invalid regex binding after !~", line))
6203 }
6204 }
6205 _ => {
6206 let rhs = self.parse_unary()?;
6207 Ok(Expr {
6208 kind: ExprKind::BinOp {
6209 left: Box::new(left),
6210 op: BinOp::BindNotMatch,
6211 right: Box::new(rhs),
6212 },
6213 line,
6214 })
6215 }
6216 }
6217 }
6218 _ => Ok(left),
6219 }
6220 }
6221
6222 fn parse_thread_input(&mut self) -> PerlResult<Expr> {
6225 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
6226 let result = self.parse_range();
6227 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
6228 result
6229 }
6230
6231 fn parse_range(&mut self) -> PerlResult<Expr> {
6238 let left = self.parse_log_or()?;
6239 let line = left.line;
6240 let exclusive = if self.eat(&Token::RangeExclusive) {
6241 true
6242 } else if self.eat(&Token::Range) {
6243 false
6244 } else {
6245 return Ok(left);
6246 };
6247 let right = self.parse_log_or()?;
6248 Ok(Expr {
6249 kind: ExprKind::Range {
6250 from: Box::new(left),
6251 to: Box::new(right),
6252 exclusive,
6253 },
6254 line,
6255 })
6256 }
6257
6258 fn parse_package_qualified_identifier(&mut self) -> PerlResult<String> {
6260 let mut name = match self.advance() {
6261 (Token::Ident(n), _) => n,
6262 (tok, l) => {
6263 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
6264 }
6265 };
6266 while self.eat(&Token::PackageSep) {
6267 match self.advance() {
6268 (Token::Ident(part), _) => {
6269 name.push_str("::");
6270 name.push_str(&part);
6271 }
6272 (tok, l) => {
6273 return Err(self
6274 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
6275 }
6276 }
6277 }
6278 Ok(name)
6279 }
6280
6281 fn parse_qualified_subroutine_name(&mut self) -> PerlResult<String> {
6283 self.parse_package_qualified_identifier()
6284 }
6285
6286 fn parse_unary(&mut self) -> PerlResult<Expr> {
6287 let line = self.peek_line();
6288 match self.peek().clone() {
6289 Token::Minus => {
6290 self.advance();
6291 let expr = self.parse_power()?;
6292 Ok(Expr {
6293 kind: ExprKind::UnaryOp {
6294 op: UnaryOp::Negate,
6295 expr: Box::new(expr),
6296 },
6297 line,
6298 })
6299 }
6300 Token::Plus => {
6303 self.advance();
6304 self.parse_unary()
6305 }
6306 Token::LogNot => {
6307 self.advance();
6308 let expr = self.parse_unary()?;
6309 Ok(Expr {
6310 kind: ExprKind::UnaryOp {
6311 op: UnaryOp::LogNot,
6312 expr: Box::new(expr),
6313 },
6314 line,
6315 })
6316 }
6317 Token::BitNot => {
6318 self.advance();
6319 let expr = self.parse_unary()?;
6320 Ok(Expr {
6321 kind: ExprKind::UnaryOp {
6322 op: UnaryOp::BitNot,
6323 expr: Box::new(expr),
6324 },
6325 line,
6326 })
6327 }
6328 Token::Increment => {
6329 self.advance();
6330 let expr = self.parse_postfix()?;
6331 Ok(Expr {
6332 kind: ExprKind::UnaryOp {
6333 op: UnaryOp::PreIncrement,
6334 expr: Box::new(expr),
6335 },
6336 line,
6337 })
6338 }
6339 Token::Decrement => {
6340 self.advance();
6341 let expr = self.parse_postfix()?;
6342 Ok(Expr {
6343 kind: ExprKind::UnaryOp {
6344 op: UnaryOp::PreDecrement,
6345 expr: Box::new(expr),
6346 },
6347 line,
6348 })
6349 }
6350 Token::BitAnd => {
6351 self.advance();
6354 if matches!(self.peek(), Token::LBrace) {
6355 self.advance();
6356 let inner = self.parse_expression()?;
6357 self.expect(&Token::RBrace)?;
6358 return Ok(Expr {
6359 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
6360 line,
6361 });
6362 }
6363 if matches!(self.peek(), Token::Ident(_)) {
6364 let name = self.parse_qualified_subroutine_name()?;
6365 return Ok(Expr {
6366 kind: ExprKind::SubroutineRef(name),
6367 line,
6368 });
6369 }
6370 let target = self.parse_primary()?;
6371 if matches!(self.peek(), Token::LParen) {
6372 self.advance();
6373 let args = self.parse_arg_list()?;
6374 self.expect(&Token::RParen)?;
6375 return Ok(Expr {
6376 kind: ExprKind::IndirectCall {
6377 target: Box::new(target),
6378 args,
6379 ampersand: true,
6380 pass_caller_arglist: false,
6381 },
6382 line,
6383 });
6384 }
6385 Ok(Expr {
6387 kind: ExprKind::IndirectCall {
6388 target: Box::new(target),
6389 args: vec![],
6390 ampersand: true,
6391 pass_caller_arglist: true,
6392 },
6393 line,
6394 })
6395 }
6396 Token::Backslash => {
6397 self.advance();
6398 let expr = self.parse_unary()?;
6399 if let ExprKind::SubroutineRef(name) = expr.kind {
6400 return Ok(Expr {
6401 kind: ExprKind::SubroutineCodeRef(name),
6402 line,
6403 });
6404 }
6405 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
6406 return Ok(expr);
6407 }
6408 Ok(Expr {
6410 kind: ExprKind::ScalarRef(Box::new(expr)),
6411 line,
6412 })
6413 }
6414 Token::FileTest(op) => {
6415 self.advance();
6416 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
6418 Expr {
6419 kind: ExprKind::ScalarVar("_".into()),
6420 line: self.peek_line(),
6421 }
6422 } else {
6423 self.parse_unary()?
6424 };
6425 Ok(Expr {
6426 kind: ExprKind::FileTest {
6427 op,
6428 expr: Box::new(expr),
6429 },
6430 line,
6431 })
6432 }
6433 _ => self.parse_power(),
6434 }
6435 }
6436
6437 fn parse_power(&mut self) -> PerlResult<Expr> {
6438 let left = self.parse_postfix()?;
6439 if matches!(self.peek(), Token::Power) {
6440 let line = left.line;
6441 self.advance();
6442 let right = self.parse_unary()?; return Ok(Expr {
6444 kind: ExprKind::BinOp {
6445 left: Box::new(left),
6446 op: BinOp::Pow,
6447 right: Box::new(right),
6448 },
6449 line,
6450 });
6451 }
6452 Ok(left)
6453 }
6454
6455 fn parse_postfix(&mut self) -> PerlResult<Expr> {
6456 let mut expr = self.parse_primary()?;
6457 loop {
6458 match self.peek().clone() {
6459 Token::Increment => {
6460 let line = expr.line;
6461 self.advance();
6462 expr = Expr {
6463 kind: ExprKind::PostfixOp {
6464 expr: Box::new(expr),
6465 op: PostfixOp::Increment,
6466 },
6467 line,
6468 };
6469 }
6470 Token::Decrement => {
6471 let line = expr.line;
6472 self.advance();
6473 expr = Expr {
6474 kind: ExprKind::PostfixOp {
6475 expr: Box::new(expr),
6476 op: PostfixOp::Decrement,
6477 },
6478 line,
6479 };
6480 }
6481 Token::LParen => {
6482 if self.suppress_indirect_paren_call > 0 {
6483 break;
6484 }
6485 if self.peek_line() > self.prev_line() {
6489 break;
6490 }
6491 let line = expr.line;
6492 self.advance();
6493 let args = self.parse_arg_list()?;
6494 self.expect(&Token::RParen)?;
6495 expr = Expr {
6496 kind: ExprKind::IndirectCall {
6497 target: Box::new(expr),
6498 args,
6499 ampersand: false,
6500 pass_caller_arglist: false,
6501 },
6502 line,
6503 };
6504 }
6505 Token::Arrow => {
6506 let line = expr.line;
6507 self.advance();
6508 match self.peek().clone() {
6509 Token::LBracket => {
6510 self.advance();
6511 let index = self.parse_expression()?;
6512 self.expect(&Token::RBracket)?;
6513 expr = Expr {
6514 kind: ExprKind::ArrowDeref {
6515 expr: Box::new(expr),
6516 index: Box::new(index),
6517 kind: DerefKind::Array,
6518 },
6519 line,
6520 };
6521 }
6522 Token::LBrace => {
6523 self.advance();
6524 let key = self.parse_hash_subscript_key()?;
6525 self.expect(&Token::RBrace)?;
6526 expr = Expr {
6527 kind: ExprKind::ArrowDeref {
6528 expr: Box::new(expr),
6529 index: Box::new(key),
6530 kind: DerefKind::Hash,
6531 },
6532 line,
6533 };
6534 }
6535 Token::LParen => {
6536 self.advance();
6537 let args = self.parse_arg_list()?;
6538 self.expect(&Token::RParen)?;
6539 expr = Expr {
6540 kind: ExprKind::ArrowDeref {
6541 expr: Box::new(expr),
6542 index: Box::new(Expr {
6543 kind: ExprKind::List(args),
6544 line,
6545 }),
6546 kind: DerefKind::Call,
6547 },
6548 line,
6549 };
6550 }
6551 Token::Ident(method) => {
6552 self.advance();
6553 if method == "SUPER" {
6554 self.expect(&Token::PackageSep)?;
6555 let real_method = match self.advance() {
6556 (Token::Ident(n), _) => n,
6557 (tok, l) => {
6558 return Err(self.syntax_err(
6559 format!(
6560 "Expected method name after SUPER::, got {:?}",
6561 tok
6562 ),
6563 l,
6564 ));
6565 }
6566 };
6567 let args = if self.eat(&Token::LParen) {
6568 let a = self.parse_arg_list()?;
6569 self.expect(&Token::RParen)?;
6570 a
6571 } else {
6572 self.parse_method_arg_list_no_paren()?
6573 };
6574 expr = Expr {
6575 kind: ExprKind::MethodCall {
6576 object: Box::new(expr),
6577 method: real_method,
6578 args,
6579 super_call: true,
6580 },
6581 line,
6582 };
6583 } else {
6584 let mut method_name = method;
6585 while self.eat(&Token::PackageSep) {
6586 match self.advance() {
6587 (Token::Ident(part), _) => {
6588 method_name.push_str("::");
6589 method_name.push_str(&part);
6590 }
6591 (tok, l) => {
6592 return Err(self.syntax_err(
6593 format!(
6594 "Expected identifier after :: in method name, got {:?}",
6595 tok
6596 ),
6597 l,
6598 ));
6599 }
6600 }
6601 }
6602 let args = if self.eat(&Token::LParen) {
6603 let a = self.parse_arg_list()?;
6604 self.expect(&Token::RParen)?;
6605 a
6606 } else {
6607 self.parse_method_arg_list_no_paren()?
6608 };
6609 expr = Expr {
6610 kind: ExprKind::MethodCall {
6611 object: Box::new(expr),
6612 method: method_name,
6613 args,
6614 super_call: false,
6615 },
6616 line,
6617 };
6618 }
6619 }
6620 Token::ArrayAt => {
6626 self.advance(); match self.peek().clone() {
6628 Token::Star => {
6629 self.advance();
6630 expr = Expr {
6631 kind: ExprKind::Deref {
6632 expr: Box::new(expr),
6633 kind: Sigil::Array,
6634 },
6635 line,
6636 };
6637 }
6638 Token::LBracket => {
6639 self.advance();
6640 let mut indices = Vec::new();
6641 while !matches!(self.peek(), Token::RBracket | Token::Eof) {
6642 indices.push(self.parse_assign_expr()?);
6643 if !self.eat(&Token::Comma) {
6644 break;
6645 }
6646 }
6647 self.expect(&Token::RBracket)?;
6648 let source = Expr {
6649 kind: ExprKind::Deref {
6650 expr: Box::new(expr),
6651 kind: Sigil::Array,
6652 },
6653 line,
6654 };
6655 expr = Expr {
6656 kind: ExprKind::AnonymousListSlice {
6657 source: Box::new(source),
6658 indices,
6659 },
6660 line,
6661 };
6662 }
6663 Token::LBrace => {
6664 self.advance();
6665 let mut keys = Vec::new();
6666 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
6667 keys.push(self.parse_assign_expr()?);
6668 if !self.eat(&Token::Comma) {
6669 break;
6670 }
6671 }
6672 self.expect(&Token::RBrace)?;
6673 expr = Expr {
6674 kind: ExprKind::HashSliceDeref {
6675 container: Box::new(expr),
6676 keys,
6677 },
6678 line,
6679 };
6680 }
6681 tok => {
6682 return Err(self.syntax_err(
6683 format!(
6684 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
6685 tok
6686 ),
6687 line,
6688 ));
6689 }
6690 }
6691 }
6692 Token::HashPercent => {
6693 self.advance(); match self.peek().clone() {
6695 Token::Star => {
6696 self.advance();
6697 expr = Expr {
6698 kind: ExprKind::Deref {
6699 expr: Box::new(expr),
6700 kind: Sigil::Hash,
6701 },
6702 line,
6703 };
6704 }
6705 tok => {
6706 return Err(self.syntax_err(
6707 format!("Expected `*` after `->%`, got {:?}", tok),
6708 line,
6709 ));
6710 }
6711 }
6712 }
6713 Token::X => {
6715 self.advance();
6716 let args = if self.eat(&Token::LParen) {
6717 let a = self.parse_arg_list()?;
6718 self.expect(&Token::RParen)?;
6719 a
6720 } else {
6721 self.parse_method_arg_list_no_paren()?
6722 };
6723 expr = Expr {
6724 kind: ExprKind::MethodCall {
6725 object: Box::new(expr),
6726 method: "x".to_string(),
6727 args,
6728 super_call: false,
6729 },
6730 line,
6731 };
6732 }
6733 _ => break,
6734 }
6735 }
6736 Token::LBracket => {
6737 let line = expr.line;
6739 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
6740 if let ExprKind::ScalarVar(ref name) = expr.kind {
6741 let name = name.clone();
6742 self.advance();
6743 let index = self.parse_expression()?;
6744 self.expect(&Token::RBracket)?;
6745 expr = Expr {
6746 kind: ExprKind::ArrayElement {
6747 array: name,
6748 index: Box::new(index),
6749 },
6750 line,
6751 };
6752 }
6753 } else if postfix_lbracket_is_arrow_container(&expr) {
6754 self.advance();
6755 let indices = self.parse_arg_list()?;
6756 self.expect(&Token::RBracket)?;
6757 expr = Expr {
6758 kind: ExprKind::ArrowDeref {
6759 expr: Box::new(expr),
6760 index: Box::new(Expr {
6761 kind: ExprKind::List(indices),
6762 line,
6763 }),
6764 kind: DerefKind::Array,
6765 },
6766 line,
6767 };
6768 } else {
6769 self.advance();
6770 let indices = self.parse_arg_list()?;
6771 self.expect(&Token::RBracket)?;
6772 expr = Expr {
6773 kind: ExprKind::AnonymousListSlice {
6774 source: Box::new(expr),
6775 indices,
6776 },
6777 line,
6778 };
6779 }
6780 }
6781 Token::LBrace => {
6782 if self.suppress_scalar_hash_brace > 0 {
6783 break;
6784 }
6785 let line = expr.line;
6788 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
6789 let is_chainable_hash_subscript = is_scalar_named_hash
6790 || matches!(
6791 expr.kind,
6792 ExprKind::HashElement { .. }
6793 | ExprKind::ArrayElement { .. }
6794 | ExprKind::ArrowDeref { .. }
6795 | ExprKind::Deref {
6796 kind: Sigil::Scalar,
6797 ..
6798 }
6799 );
6800 if !is_chainable_hash_subscript {
6801 break;
6802 }
6803 self.advance();
6804 let key = self.parse_hash_subscript_key()?;
6805 self.expect(&Token::RBrace)?;
6806 expr = if is_scalar_named_hash {
6807 if let ExprKind::ScalarVar(ref name) = expr.kind {
6808 let name = name.clone();
6809 if name == "_" {
6811 Expr {
6812 kind: ExprKind::ArrowDeref {
6813 expr: Box::new(Expr {
6814 kind: ExprKind::ScalarVar("_".into()),
6815 line,
6816 }),
6817 index: Box::new(key),
6818 kind: DerefKind::Hash,
6819 },
6820 line,
6821 }
6822 } else {
6823 Expr {
6824 kind: ExprKind::HashElement {
6825 hash: name,
6826 key: Box::new(key),
6827 },
6828 line,
6829 }
6830 }
6831 } else {
6832 unreachable!("is_scalar_named_hash implies ScalarVar");
6833 }
6834 } else {
6835 Expr {
6836 kind: ExprKind::ArrowDeref {
6837 expr: Box::new(expr),
6838 index: Box::new(key),
6839 kind: DerefKind::Hash,
6840 },
6841 line,
6842 }
6843 };
6844 }
6845 _ => break,
6846 }
6847 }
6848 Ok(expr)
6849 }
6850
6851 fn parse_primary(&mut self) -> PerlResult<Expr> {
6852 let line = self.peek_line();
6853 if let Token::Ident(ref kw) = self.peek().clone() {
6858 if matches!(kw.as_str(), "my" | "our" | "state" | "local") {
6859 let kw_owned = kw.clone();
6860 let saved_pos = self.pos;
6865 let stmt = self.parse_my_our_local(&kw_owned, false)?;
6866 let decls = match stmt.kind {
6867 StmtKind::My(d)
6868 | StmtKind::Our(d)
6869 | StmtKind::State(d)
6870 | StmtKind::Local(d) => d,
6871 _ => {
6872 self.pos = saved_pos;
6877 return Err(self.syntax_err(
6878 "`my`/`our`/`local` in expression must declare variables",
6879 line,
6880 ));
6881 }
6882 };
6883 return Ok(Expr {
6884 kind: ExprKind::MyExpr {
6885 keyword: kw_owned,
6886 decls,
6887 },
6888 line,
6889 });
6890 }
6891 }
6892 match self.peek().clone() {
6893 Token::Integer(n) => {
6894 self.advance();
6895 Ok(Expr {
6896 kind: ExprKind::Integer(n),
6897 line,
6898 })
6899 }
6900 Token::Float(f) => {
6901 self.advance();
6902 Ok(Expr {
6903 kind: ExprKind::Float(f),
6904 line,
6905 })
6906 }
6907 Token::ArrowBrace => {
6913 self.advance();
6914 let mut stmts = Vec::new();
6915 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
6916 if self.eat(&Token::Semicolon) {
6917 continue;
6918 }
6919 stmts.push(self.parse_statement()?);
6920 }
6921 self.expect(&Token::RBrace)?;
6922 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
6923 let inner = Expr {
6924 kind: ExprKind::CodeRef {
6925 params: vec![],
6926 body: stmts,
6927 },
6928 line: inner_line,
6929 };
6930 Ok(Expr {
6931 kind: ExprKind::Do(Box::new(inner)),
6932 line,
6933 })
6934 }
6935 Token::Star => {
6936 self.advance();
6937 if matches!(self.peek(), Token::LBrace) {
6938 self.advance();
6939 let inner = self.parse_expression()?;
6940 self.expect(&Token::RBrace)?;
6941 return Ok(Expr {
6942 kind: ExprKind::Deref {
6943 expr: Box::new(inner),
6944 kind: Sigil::Typeglob,
6945 },
6946 line,
6947 });
6948 }
6949 if matches!(
6951 self.peek(),
6952 Token::ScalarVar(_)
6953 | Token::ArrayVar(_)
6954 | Token::HashVar(_)
6955 | Token::DerefScalarVar(_)
6956 | Token::HashPercent
6957 ) {
6958 let inner = self.parse_postfix()?;
6959 return Ok(Expr {
6960 kind: ExprKind::TypeglobExpr(Box::new(inner)),
6961 line,
6962 });
6963 }
6964 let mut full_name = match self.advance() {
6966 (Token::Ident(n), _) => n,
6967 (Token::X, _) => "x".to_string(),
6968 (tok, l) => {
6969 return Err(self
6970 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
6971 }
6972 };
6973 while self.eat(&Token::PackageSep) {
6974 match self.advance() {
6975 (Token::Ident(part), _) => {
6976 full_name = format!("{}::{}", full_name, part);
6977 }
6978 (Token::X, _) => {
6979 full_name = format!("{}::x", full_name);
6980 }
6981 (tok, l) => {
6982 return Err(self.syntax_err(
6983 format!("Expected identifier after :: in typeglob, got {:?}", tok),
6984 l,
6985 ));
6986 }
6987 }
6988 }
6989 Ok(Expr {
6990 kind: ExprKind::Typeglob(full_name),
6991 line,
6992 })
6993 }
6994 Token::SingleString(s) => {
6995 self.advance();
6996 Ok(Expr {
6997 kind: ExprKind::String(s),
6998 line,
6999 })
7000 }
7001 Token::DoubleString(s) => {
7002 self.advance();
7003 self.parse_interpolated_string(&s, line)
7004 }
7005 Token::BacktickString(s) => {
7006 self.advance();
7007 let inner = self.parse_interpolated_string(&s, line)?;
7008 Ok(Expr {
7009 kind: ExprKind::Qx(Box::new(inner)),
7010 line,
7011 })
7012 }
7013 Token::HereDoc(_, body, interpolate) => {
7014 self.advance();
7015 if interpolate {
7016 self.parse_interpolated_string(&body, line)
7017 } else {
7018 Ok(Expr {
7019 kind: ExprKind::String(body),
7020 line,
7021 })
7022 }
7023 }
7024 Token::Regex(pattern, flags, _delim) => {
7025 self.advance();
7026 Ok(Expr {
7027 kind: ExprKind::Regex(pattern, flags),
7028 line,
7029 })
7030 }
7031 Token::QW(words) => {
7032 self.advance();
7033 Ok(Expr {
7034 kind: ExprKind::QW(words),
7035 line,
7036 })
7037 }
7038 Token::DerefScalarVar(name) => {
7039 self.advance();
7040 Ok(Expr {
7041 kind: ExprKind::Deref {
7042 expr: Box::new(Expr {
7043 kind: ExprKind::ScalarVar(name),
7044 line,
7045 }),
7046 kind: Sigil::Scalar,
7047 },
7048 line,
7049 })
7050 }
7051 Token::ScalarVar(name) => {
7052 self.advance();
7053 Ok(Expr {
7054 kind: ExprKind::ScalarVar(name),
7055 line,
7056 })
7057 }
7058 Token::ArrayVar(name) => {
7059 self.advance();
7060 match self.peek() {
7062 Token::LBracket => {
7063 self.advance();
7064 let indices = self.parse_arg_list()?;
7065 self.expect(&Token::RBracket)?;
7066 Ok(Expr {
7067 kind: ExprKind::ArraySlice {
7068 array: name,
7069 indices,
7070 },
7071 line,
7072 })
7073 }
7074 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
7075 self.advance();
7076 let keys = self.parse_arg_list()?;
7077 self.expect(&Token::RBrace)?;
7078 Ok(Expr {
7079 kind: ExprKind::HashSlice { hash: name, keys },
7080 line,
7081 })
7082 }
7083 _ => Ok(Expr {
7084 kind: ExprKind::ArrayVar(name),
7085 line,
7086 }),
7087 }
7088 }
7089 Token::HashVar(name) => {
7090 self.advance();
7091 Ok(Expr {
7092 kind: ExprKind::HashVar(name),
7093 line,
7094 })
7095 }
7096 Token::HashPercent => {
7097 self.advance();
7099 if matches!(self.peek(), Token::ScalarVar(_)) {
7100 let n = match self.advance() {
7101 (Token::ScalarVar(n), _) => n,
7102 (tok, l) => {
7103 return Err(self.syntax_err(
7104 format!("Expected scalar variable after %%, got {:?}", tok),
7105 l,
7106 ));
7107 }
7108 };
7109 return Ok(Expr {
7110 kind: ExprKind::Deref {
7111 expr: Box::new(Expr {
7112 kind: ExprKind::ScalarVar(n),
7113 line,
7114 }),
7115 kind: Sigil::Hash,
7116 },
7117 line,
7118 });
7119 }
7120 if matches!(self.peek(), Token::LBracket) {
7125 self.advance();
7126 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
7127 self.expect(&Token::RBracket)?;
7128 let href = Expr {
7129 kind: ExprKind::HashRef(pairs),
7130 line,
7131 };
7132 return Ok(Expr {
7133 kind: ExprKind::Deref {
7134 expr: Box::new(href),
7135 kind: Sigil::Hash,
7136 },
7137 line,
7138 });
7139 }
7140 self.expect(&Token::LBrace)?;
7141 let looks_like_pair = matches!(
7147 self.peek(),
7148 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
7149 ) && matches!(self.peek_at(1), Token::FatArrow);
7150 let inner = if looks_like_pair {
7151 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
7152 Expr {
7153 kind: ExprKind::HashRef(pairs),
7154 line,
7155 }
7156 } else {
7157 self.parse_expression()?
7158 };
7159 self.expect(&Token::RBrace)?;
7160 Ok(Expr {
7161 kind: ExprKind::Deref {
7162 expr: Box::new(inner),
7163 kind: Sigil::Hash,
7164 },
7165 line,
7166 })
7167 }
7168 Token::ArrayAt => {
7169 self.advance();
7170 if matches!(self.peek(), Token::LBrace) {
7172 self.advance();
7173 let inner = self.parse_expression()?;
7174 self.expect(&Token::RBrace)?;
7175 return Ok(Expr {
7176 kind: ExprKind::Deref {
7177 expr: Box::new(inner),
7178 kind: Sigil::Array,
7179 },
7180 line,
7181 });
7182 }
7183 if matches!(self.peek(), Token::LBracket) {
7187 self.advance();
7188 let mut elems = Vec::new();
7189 if !matches!(self.peek(), Token::RBracket) {
7190 elems.push(self.parse_assign_expr()?);
7191 while self.eat(&Token::Comma) {
7192 if matches!(self.peek(), Token::RBracket) {
7193 break;
7194 }
7195 elems.push(self.parse_assign_expr()?);
7196 }
7197 }
7198 self.expect(&Token::RBracket)?;
7199 let aref = Expr {
7200 kind: ExprKind::ArrayRef(elems),
7201 line,
7202 };
7203 return Ok(Expr {
7204 kind: ExprKind::Deref {
7205 expr: Box::new(aref),
7206 kind: Sigil::Array,
7207 },
7208 line,
7209 });
7210 }
7211 let container = match self.peek().clone() {
7213 Token::ScalarVar(n) => {
7214 self.advance();
7215 Expr {
7216 kind: ExprKind::ScalarVar(n),
7217 line,
7218 }
7219 }
7220 _ => {
7221 return Err(self.syntax_err(
7222 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
7223 line,
7224 ));
7225 }
7226 };
7227 if matches!(self.peek(), Token::LBrace) {
7228 self.advance();
7229 let keys = self.parse_arg_list()?;
7230 self.expect(&Token::RBrace)?;
7231 return Ok(Expr {
7232 kind: ExprKind::HashSliceDeref {
7233 container: Box::new(container),
7234 keys,
7235 },
7236 line,
7237 });
7238 }
7239 Ok(Expr {
7240 kind: ExprKind::Deref {
7241 expr: Box::new(container),
7242 kind: Sigil::Array,
7243 },
7244 line,
7245 })
7246 }
7247 Token::LParen => {
7248 self.advance();
7249 if matches!(self.peek(), Token::RParen) {
7250 self.advance();
7251 return Ok(Expr {
7252 kind: ExprKind::List(vec![]),
7253 line,
7254 });
7255 }
7256 let expr = self.parse_expression()?;
7257 self.expect(&Token::RParen)?;
7258 Ok(expr)
7259 }
7260 Token::LBracket => {
7261 self.advance();
7262 let elems = self.parse_arg_list()?;
7263 self.expect(&Token::RBracket)?;
7264 Ok(Expr {
7265 kind: ExprKind::ArrayRef(elems),
7266 line,
7267 })
7268 }
7269 Token::LBrace => {
7270 self.advance();
7272 let saved = self.pos;
7274 match self.try_parse_hash_ref() {
7275 Ok(pairs) => Ok(Expr {
7276 kind: ExprKind::HashRef(pairs),
7277 line,
7278 }),
7279 Err(_) => {
7280 self.pos = saved;
7281 let mut stmts = Vec::new();
7283 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
7284 if self.eat(&Token::Semicolon) {
7285 continue;
7286 }
7287 stmts.push(self.parse_statement()?);
7288 }
7289 self.expect(&Token::RBrace)?;
7290 Ok(Expr {
7291 kind: ExprKind::CodeRef {
7292 params: vec![],
7293 body: stmts,
7294 },
7295 line,
7296 })
7297 }
7298 }
7299 }
7300 Token::Diamond => {
7301 self.advance();
7302 Ok(Expr {
7303 kind: ExprKind::ReadLine(None),
7304 line,
7305 })
7306 }
7307 Token::ReadLine(handle) => {
7308 self.advance();
7309 Ok(Expr {
7310 kind: ExprKind::ReadLine(Some(handle)),
7311 line,
7312 })
7313 }
7314
7315 Token::ThreadArrow => {
7317 self.advance();
7318 self.parse_thread_macro(line)
7319 }
7320 Token::Ident(ref name) => {
7321 let name = name.clone();
7322 if name.starts_with('\x00') {
7324 self.advance();
7325 let parts: Vec<&str> = name.split('\x00').collect();
7326 if parts.len() >= 4 && parts[1] == "s" {
7327 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7328 return Ok(Expr {
7329 kind: ExprKind::Substitution {
7330 expr: Box::new(Expr {
7331 kind: ExprKind::ScalarVar("_".into()),
7332 line,
7333 }),
7334 pattern: parts[2].to_string(),
7335 replacement: parts[3].to_string(),
7336 flags: parts.get(4).unwrap_or(&"").to_string(),
7337 delim,
7338 },
7339 line,
7340 });
7341 }
7342 if parts.len() >= 4 && parts[1] == "tr" {
7343 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7344 return Ok(Expr {
7345 kind: ExprKind::Transliterate {
7346 expr: Box::new(Expr {
7347 kind: ExprKind::ScalarVar("_".into()),
7348 line,
7349 }),
7350 from: parts[2].to_string(),
7351 to: parts[3].to_string(),
7352 flags: parts.get(4).unwrap_or(&"").to_string(),
7353 delim,
7354 },
7355 line,
7356 });
7357 }
7358 return Err(self.syntax_err("Unexpected encoded token", line));
7359 }
7360 self.parse_named_expr(name)
7361 }
7362
7363 Token::Percent => {
7366 self.advance();
7367 match self.peek().clone() {
7368 Token::Ident(name) => {
7369 self.advance();
7370 Ok(Expr {
7371 kind: ExprKind::HashVar(name),
7372 line,
7373 })
7374 }
7375 Token::ScalarVar(n) => {
7376 self.advance();
7377 Ok(Expr {
7378 kind: ExprKind::Deref {
7379 expr: Box::new(Expr {
7380 kind: ExprKind::ScalarVar(n),
7381 line,
7382 }),
7383 kind: Sigil::Hash,
7384 },
7385 line,
7386 })
7387 }
7388 Token::LBrace => {
7389 self.advance();
7390 let looks_like_pair = matches!(
7391 self.peek(),
7392 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
7393 ) && matches!(self.peek_at(1), Token::FatArrow);
7394 let inner = if looks_like_pair {
7395 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
7396 Expr {
7397 kind: ExprKind::HashRef(pairs),
7398 line,
7399 }
7400 } else {
7401 self.parse_expression()?
7402 };
7403 self.expect(&Token::RBrace)?;
7404 Ok(Expr {
7405 kind: ExprKind::Deref {
7406 expr: Box::new(inner),
7407 kind: Sigil::Hash,
7408 },
7409 line,
7410 })
7411 }
7412 Token::LBracket => {
7413 self.advance();
7414 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
7415 self.expect(&Token::RBracket)?;
7416 let href = Expr {
7417 kind: ExprKind::HashRef(pairs),
7418 line,
7419 };
7420 Ok(Expr {
7421 kind: ExprKind::Deref {
7422 expr: Box::new(href),
7423 kind: Sigil::Hash,
7424 },
7425 line,
7426 })
7427 }
7428 tok => Err(self.syntax_err(
7429 format!(
7430 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
7431 tok
7432 ),
7433 line,
7434 )),
7435 }
7436 }
7437
7438 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
7439 }
7440 }
7441
7442 fn parse_named_expr(&mut self, mut name: String) -> PerlResult<Expr> {
7443 let line = self.peek_line();
7444 self.advance(); while self.eat(&Token::PackageSep) {
7446 match self.advance() {
7447 (Token::Ident(part), _) => {
7448 name = format!("{}::{}", name, part);
7449 }
7450 (tok, err_line) => {
7451 return Err(self.syntax_err(
7452 format!("Expected identifier after `::`, got {:?}", tok),
7453 err_line,
7454 ));
7455 }
7456 }
7457 }
7458
7459 if matches!(self.peek(), Token::FatArrow) {
7463 return Ok(Expr {
7464 kind: ExprKind::String(name),
7465 line,
7466 });
7467 }
7468
7469 if crate::compat_mode() {
7470 if let Some(ext) = Self::stryke_extension_name(&name) {
7471 if !self.declared_subs.contains(&name) {
7472 return Err(self.syntax_err(
7473 format!("`{ext}` is a stryke extension (disabled by --compat)"),
7474 line,
7475 ));
7476 }
7477 }
7478 }
7479
7480 match name.as_str() {
7481 "__FILE__" => Ok(Expr {
7482 kind: ExprKind::MagicConst(MagicConstKind::File),
7483 line,
7484 }),
7485 "__LINE__" => Ok(Expr {
7486 kind: ExprKind::MagicConst(MagicConstKind::Line),
7487 line,
7488 }),
7489 "__SUB__" => Ok(Expr {
7490 kind: ExprKind::MagicConst(MagicConstKind::Sub),
7491 line,
7492 }),
7493 "stdin" => Ok(Expr {
7494 kind: ExprKind::FuncCall {
7495 name: "stdin".into(),
7496 args: vec![],
7497 },
7498 line,
7499 }),
7500 "range" => {
7501 let args = self.parse_builtin_args()?;
7502 Ok(Expr {
7503 kind: ExprKind::FuncCall {
7504 name: "range".into(),
7505 args,
7506 },
7507 line,
7508 })
7509 }
7510 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
7511 "say" | "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
7512 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
7513 "die" => {
7514 let args = self.parse_list_until_terminator()?;
7515 Ok(Expr {
7516 kind: ExprKind::Die(args),
7517 line,
7518 })
7519 }
7520 "warn" => {
7521 let args = self.parse_list_until_terminator()?;
7522 Ok(Expr {
7523 kind: ExprKind::Warn(args),
7524 line,
7525 })
7526 }
7527 "croak" | "confess" => {
7532 let args = self.parse_list_until_terminator()?;
7533 Ok(Expr {
7534 kind: ExprKind::Die(args),
7535 line,
7536 })
7537 }
7538 "carp" | "cluck" => {
7540 let args = self.parse_list_until_terminator()?;
7541 Ok(Expr {
7542 kind: ExprKind::Warn(args),
7543 line,
7544 })
7545 }
7546 "chomp" => {
7547 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7548 return Ok(e);
7549 }
7550 let a = self.parse_one_arg_or_default()?;
7551 Ok(Expr {
7552 kind: ExprKind::Chomp(Box::new(a)),
7553 line,
7554 })
7555 }
7556 "chop" => {
7557 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7558 return Ok(e);
7559 }
7560 let a = self.parse_one_arg_or_default()?;
7561 Ok(Expr {
7562 kind: ExprKind::Chop(Box::new(a)),
7563 line,
7564 })
7565 }
7566 "length" => {
7567 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7568 return Ok(e);
7569 }
7570 let a = self.parse_one_arg_or_default()?;
7571 Ok(Expr {
7572 kind: ExprKind::Length(Box::new(a)),
7573 line,
7574 })
7575 }
7576 "defined" => {
7577 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7578 return Ok(e);
7579 }
7580 let a = self.parse_one_arg_or_default()?;
7581 Ok(Expr {
7582 kind: ExprKind::Defined(Box::new(a)),
7583 line,
7584 })
7585 }
7586 "ref" => {
7587 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7588 return Ok(e);
7589 }
7590 let a = self.parse_one_arg_or_default()?;
7591 Ok(Expr {
7592 kind: ExprKind::Ref(Box::new(a)),
7593 line,
7594 })
7595 }
7596 "undef" => {
7597 if matches!(
7598 self.peek(),
7599 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
7600 ) {
7601 let _ = self.advance();
7602 }
7603 Ok(Expr {
7604 kind: ExprKind::Undef,
7605 line,
7606 })
7607 }
7608 "scalar" => {
7609 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7610 return Ok(e);
7611 }
7612 let a = self.parse_one_arg_or_default()?;
7613 Ok(Expr {
7614 kind: ExprKind::ScalarContext(Box::new(a)),
7615 line,
7616 })
7617 }
7618 "abs" => {
7619 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7620 return Ok(e);
7621 }
7622 let a = self.parse_one_arg_or_default()?;
7623 Ok(Expr {
7624 kind: ExprKind::Abs(Box::new(a)),
7625 line,
7626 })
7627 }
7628 "inc" | "dec" => {
7633 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7634 return Ok(e);
7635 }
7636 let a = self.parse_one_arg_or_default()?;
7637 Ok(Expr {
7638 kind: ExprKind::FuncCall {
7639 name,
7640 args: vec![a],
7641 },
7642 line,
7643 })
7644 }
7645 "int" => {
7646 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7647 return Ok(e);
7648 }
7649 let a = self.parse_one_arg_or_default()?;
7650 Ok(Expr {
7651 kind: ExprKind::Int(Box::new(a)),
7652 line,
7653 })
7654 }
7655 "sqrt" => {
7656 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7657 return Ok(e);
7658 }
7659 let a = self.parse_one_arg_or_default()?;
7660 Ok(Expr {
7661 kind: ExprKind::Sqrt(Box::new(a)),
7662 line,
7663 })
7664 }
7665 "sin" => {
7666 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7667 return Ok(e);
7668 }
7669 let a = self.parse_one_arg_or_default()?;
7670 Ok(Expr {
7671 kind: ExprKind::Sin(Box::new(a)),
7672 line,
7673 })
7674 }
7675 "cos" => {
7676 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7677 return Ok(e);
7678 }
7679 let a = self.parse_one_arg_or_default()?;
7680 Ok(Expr {
7681 kind: ExprKind::Cos(Box::new(a)),
7682 line,
7683 })
7684 }
7685 "atan2" => {
7686 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7687 return Ok(e);
7688 }
7689 let args = self.parse_builtin_args()?;
7690 if args.len() != 2 {
7691 return Err(self.syntax_err("atan2 requires two arguments", line));
7692 }
7693 Ok(Expr {
7694 kind: ExprKind::Atan2 {
7695 y: Box::new(args[0].clone()),
7696 x: Box::new(args[1].clone()),
7697 },
7698 line,
7699 })
7700 }
7701 "exp" => {
7702 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7703 return Ok(e);
7704 }
7705 let a = self.parse_one_arg_or_default()?;
7706 Ok(Expr {
7707 kind: ExprKind::Exp(Box::new(a)),
7708 line,
7709 })
7710 }
7711 "log" => {
7712 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7713 return Ok(e);
7714 }
7715 let a = self.parse_one_arg_or_default()?;
7716 Ok(Expr {
7717 kind: ExprKind::Log(Box::new(a)),
7718 line,
7719 })
7720 }
7721 "input" => {
7722 let args = if matches!(
7723 self.peek(),
7724 Token::Semicolon
7725 | Token::RBrace
7726 | Token::RParen
7727 | Token::Eof
7728 | Token::Comma
7729 | Token::PipeForward
7730 ) {
7731 vec![]
7732 } else if matches!(self.peek(), Token::LParen) {
7733 self.advance();
7734 if matches!(self.peek(), Token::RParen) {
7735 self.advance();
7736 vec![]
7737 } else {
7738 let a = self.parse_expression()?;
7739 self.expect(&Token::RParen)?;
7740 vec![a]
7741 }
7742 } else {
7743 let a = self.parse_one_arg()?;
7744 vec![a]
7745 };
7746 Ok(Expr {
7747 kind: ExprKind::FuncCall {
7748 name: "input".to_string(),
7749 args,
7750 },
7751 line,
7752 })
7753 }
7754 "rand" => {
7755 if matches!(
7756 self.peek(),
7757 Token::Semicolon
7758 | Token::RBrace
7759 | Token::RParen
7760 | Token::Eof
7761 | Token::Comma
7762 | Token::PipeForward
7763 ) {
7764 Ok(Expr {
7765 kind: ExprKind::Rand(None),
7766 line,
7767 })
7768 } else if matches!(self.peek(), Token::LParen) {
7769 self.advance();
7770 if matches!(self.peek(), Token::RParen) {
7771 self.advance();
7772 Ok(Expr {
7773 kind: ExprKind::Rand(None),
7774 line,
7775 })
7776 } else {
7777 let a = self.parse_expression()?;
7778 self.expect(&Token::RParen)?;
7779 Ok(Expr {
7780 kind: ExprKind::Rand(Some(Box::new(a))),
7781 line,
7782 })
7783 }
7784 } else {
7785 let a = self.parse_one_arg()?;
7786 Ok(Expr {
7787 kind: ExprKind::Rand(Some(Box::new(a))),
7788 line,
7789 })
7790 }
7791 }
7792 "srand" => {
7793 if matches!(
7794 self.peek(),
7795 Token::Semicolon
7796 | Token::RBrace
7797 | Token::RParen
7798 | Token::Eof
7799 | Token::Comma
7800 | Token::PipeForward
7801 ) {
7802 Ok(Expr {
7803 kind: ExprKind::Srand(None),
7804 line,
7805 })
7806 } else if matches!(self.peek(), Token::LParen) {
7807 self.advance();
7808 if matches!(self.peek(), Token::RParen) {
7809 self.advance();
7810 Ok(Expr {
7811 kind: ExprKind::Srand(None),
7812 line,
7813 })
7814 } else {
7815 let a = self.parse_expression()?;
7816 self.expect(&Token::RParen)?;
7817 Ok(Expr {
7818 kind: ExprKind::Srand(Some(Box::new(a))),
7819 line,
7820 })
7821 }
7822 } else {
7823 let a = self.parse_one_arg()?;
7824 Ok(Expr {
7825 kind: ExprKind::Srand(Some(Box::new(a))),
7826 line,
7827 })
7828 }
7829 }
7830 "hex" => {
7831 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7832 return Ok(e);
7833 }
7834 let a = self.parse_one_arg_or_default()?;
7835 Ok(Expr {
7836 kind: ExprKind::Hex(Box::new(a)),
7837 line,
7838 })
7839 }
7840 "oct" => {
7841 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7842 return Ok(e);
7843 }
7844 let a = self.parse_one_arg_or_default()?;
7845 Ok(Expr {
7846 kind: ExprKind::Oct(Box::new(a)),
7847 line,
7848 })
7849 }
7850 "chr" => {
7851 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7852 return Ok(e);
7853 }
7854 let a = self.parse_one_arg_or_default()?;
7855 Ok(Expr {
7856 kind: ExprKind::Chr(Box::new(a)),
7857 line,
7858 })
7859 }
7860 "ord" => {
7861 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7862 return Ok(e);
7863 }
7864 let a = self.parse_one_arg_or_default()?;
7865 Ok(Expr {
7866 kind: ExprKind::Ord(Box::new(a)),
7867 line,
7868 })
7869 }
7870 "lc" => {
7871 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7872 return Ok(e);
7873 }
7874 let a = self.parse_one_arg_or_default()?;
7875 Ok(Expr {
7876 kind: ExprKind::Lc(Box::new(a)),
7877 line,
7878 })
7879 }
7880 "uc" => {
7881 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7882 return Ok(e);
7883 }
7884 let a = self.parse_one_arg_or_default()?;
7885 Ok(Expr {
7886 kind: ExprKind::Uc(Box::new(a)),
7887 line,
7888 })
7889 }
7890 "lcfirst" => {
7891 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7892 return Ok(e);
7893 }
7894 let a = self.parse_one_arg_or_default()?;
7895 Ok(Expr {
7896 kind: ExprKind::Lcfirst(Box::new(a)),
7897 line,
7898 })
7899 }
7900 "ucfirst" => {
7901 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7902 return Ok(e);
7903 }
7904 let a = self.parse_one_arg_or_default()?;
7905 Ok(Expr {
7906 kind: ExprKind::Ucfirst(Box::new(a)),
7907 line,
7908 })
7909 }
7910 "fc" => {
7911 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7912 return Ok(e);
7913 }
7914 let a = self.parse_one_arg_or_default()?;
7915 Ok(Expr {
7916 kind: ExprKind::Fc(Box::new(a)),
7917 line,
7918 })
7919 }
7920 "crypt" => {
7921 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
7922 return Ok(e);
7923 }
7924 let args = self.parse_builtin_args()?;
7925 if args.len() != 2 {
7926 return Err(self.syntax_err("crypt requires two arguments", line));
7927 }
7928 Ok(Expr {
7929 kind: ExprKind::Crypt {
7930 plaintext: Box::new(args[0].clone()),
7931 salt: Box::new(args[1].clone()),
7932 },
7933 line,
7934 })
7935 }
7936 "pos" => {
7937 if matches!(
7938 self.peek(),
7939 Token::Semicolon
7940 | Token::RBrace
7941 | Token::RParen
7942 | Token::Eof
7943 | Token::Comma
7944 | Token::PipeForward
7945 ) {
7946 Ok(Expr {
7947 kind: ExprKind::Pos(None),
7948 line,
7949 })
7950 } else if matches!(self.peek(), Token::Assign) {
7951 self.advance();
7953 let rhs = self.parse_assign_expr()?;
7954 Ok(Expr {
7955 kind: ExprKind::Assign {
7956 target: Box::new(Expr {
7957 kind: ExprKind::Pos(Some(Box::new(Expr {
7958 kind: ExprKind::ScalarVar("_".into()),
7959 line,
7960 }))),
7961 line,
7962 }),
7963 value: Box::new(rhs),
7964 },
7965 line,
7966 })
7967 } else if matches!(self.peek(), Token::LParen) {
7968 self.advance();
7969 if matches!(self.peek(), Token::RParen) {
7970 self.advance();
7971 Ok(Expr {
7972 kind: ExprKind::Pos(None),
7973 line,
7974 })
7975 } else {
7976 let a = self.parse_expression()?;
7977 self.expect(&Token::RParen)?;
7978 Ok(Expr {
7979 kind: ExprKind::Pos(Some(Box::new(a))),
7980 line,
7981 })
7982 }
7983 } else {
7984 let saved = self.pos;
7985 let subj = self.parse_unary()?;
7986 if matches!(self.peek(), Token::Assign) {
7987 self.advance();
7988 let rhs = self.parse_assign_expr()?;
7989 Ok(Expr {
7990 kind: ExprKind::Assign {
7991 target: Box::new(Expr {
7992 kind: ExprKind::Pos(Some(Box::new(subj))),
7993 line,
7994 }),
7995 value: Box::new(rhs),
7996 },
7997 line,
7998 })
7999 } else {
8000 self.pos = saved;
8001 let a = self.parse_one_arg()?;
8002 Ok(Expr {
8003 kind: ExprKind::Pos(Some(Box::new(a))),
8004 line,
8005 })
8006 }
8007 }
8008 }
8009 "study" => {
8010 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8011 return Ok(e);
8012 }
8013 let a = self.parse_one_arg_or_default()?;
8014 Ok(Expr {
8015 kind: ExprKind::Study(Box::new(a)),
8016 line,
8017 })
8018 }
8019 "push" => {
8020 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8021 return Ok(e);
8022 }
8023 let args = self.parse_builtin_args()?;
8024 let (first, rest) = args
8025 .split_first()
8026 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
8027 Ok(Expr {
8028 kind: ExprKind::Push {
8029 array: Box::new(first.clone()),
8030 values: rest.to_vec(),
8031 },
8032 line,
8033 })
8034 }
8035 "pop" => {
8036 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8037 return Ok(e);
8038 }
8039 let a = self.parse_one_arg_or_argv()?;
8040 Ok(Expr {
8041 kind: ExprKind::Pop(Box::new(a)),
8042 line,
8043 })
8044 }
8045 "shift" => {
8046 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8047 return Ok(e);
8048 }
8049 let a = self.parse_one_arg_or_argv()?;
8050 Ok(Expr {
8051 kind: ExprKind::Shift(Box::new(a)),
8052 line,
8053 })
8054 }
8055 "unshift" => {
8056 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8057 return Ok(e);
8058 }
8059 let args = self.parse_builtin_args()?;
8060 let (first, rest) = args
8061 .split_first()
8062 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
8063 Ok(Expr {
8064 kind: ExprKind::Unshift {
8065 array: Box::new(first.clone()),
8066 values: rest.to_vec(),
8067 },
8068 line,
8069 })
8070 }
8071 "splice" => {
8072 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8073 return Ok(e);
8074 }
8075 let args = self.parse_builtin_args()?;
8076 let mut iter = args.into_iter();
8077 let array = Box::new(
8078 iter.next()
8079 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
8080 );
8081 let offset = iter.next().map(Box::new);
8082 let length = iter.next().map(Box::new);
8083 let replacement: Vec<Expr> = iter.collect();
8084 Ok(Expr {
8085 kind: ExprKind::Splice {
8086 array,
8087 offset,
8088 length,
8089 replacement,
8090 },
8091 line,
8092 })
8093 }
8094 "delete" => {
8095 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8096 return Ok(e);
8097 }
8098 let a = self.parse_postfix()?;
8099 Ok(Expr {
8100 kind: ExprKind::Delete(Box::new(a)),
8101 line,
8102 })
8103 }
8104 "exists" => {
8105 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8106 return Ok(e);
8107 }
8108 let a = self.parse_postfix()?;
8109 Ok(Expr {
8110 kind: ExprKind::Exists(Box::new(a)),
8111 line,
8112 })
8113 }
8114 "keys" => {
8115 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8116 return Ok(e);
8117 }
8118 let a = self.parse_one_arg_or_default()?;
8119 Ok(Expr {
8120 kind: ExprKind::Keys(Box::new(a)),
8121 line,
8122 })
8123 }
8124 "values" => {
8125 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8126 return Ok(e);
8127 }
8128 let a = self.parse_one_arg_or_default()?;
8129 Ok(Expr {
8130 kind: ExprKind::Values(Box::new(a)),
8131 line,
8132 })
8133 }
8134 "each" => {
8135 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8136 return Ok(e);
8137 }
8138 let a = self.parse_one_arg_or_default()?;
8139 Ok(Expr {
8140 kind: ExprKind::Each(Box::new(a)),
8141 line,
8142 })
8143 }
8144 "fore" | "e" | "ep" => {
8145 if matches!(self.peek(), Token::LBrace) {
8147 let (block, list) = self.parse_block_list()?;
8148 Ok(Expr {
8149 kind: ExprKind::ForEachExpr {
8150 block,
8151 list: Box::new(list),
8152 },
8153 line,
8154 })
8155 } else if self.in_pipe_rhs() {
8156 let is_terminal = matches!(
8159 self.peek(),
8160 Token::Semicolon
8161 | Token::RParen
8162 | Token::Eof
8163 | Token::PipeForward
8164 | Token::RBrace
8165 );
8166 let block = if name == "ep" && is_terminal {
8167 vec![Statement {
8168 label: None,
8169 kind: StmtKind::Expression(Expr {
8170 kind: ExprKind::Say {
8171 handle: None,
8172 args: vec![Expr {
8173 kind: ExprKind::ScalarVar("_".into()),
8174 line,
8175 }],
8176 },
8177 line,
8178 }),
8179 line,
8180 }]
8181 } else {
8182 let expr = self.parse_assign_expr_stop_at_pipe()?;
8183 let expr = Self::lift_bareword_to_topic_call(expr);
8184 vec![Statement {
8185 label: None,
8186 kind: StmtKind::Expression(expr),
8187 line,
8188 }]
8189 };
8190 let list = self.pipe_placeholder_list(line);
8191 Ok(Expr {
8192 kind: ExprKind::ForEachExpr {
8193 block,
8194 list: Box::new(list),
8195 },
8196 line,
8197 })
8198 } else {
8199 let expr = self.parse_assign_expr()?;
8201 let expr = Self::lift_bareword_to_topic_call(expr);
8202 self.expect(&Token::Comma)?;
8203 let list_parts = self.parse_list_until_terminator()?;
8204 let list_expr = if list_parts.len() == 1 {
8205 list_parts.into_iter().next().unwrap()
8206 } else {
8207 Expr {
8208 kind: ExprKind::List(list_parts),
8209 line,
8210 }
8211 };
8212 let block = vec![Statement {
8213 label: None,
8214 kind: StmtKind::Expression(expr),
8215 line,
8216 }];
8217 Ok(Expr {
8218 kind: ExprKind::ForEachExpr {
8219 block,
8220 list: Box::new(list_expr),
8221 },
8222 line,
8223 })
8224 }
8225 }
8226 "rev" => {
8227 let a = if self.in_pipe_rhs()
8232 && matches!(
8233 self.peek(),
8234 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
8235 ) {
8236 self.pipe_placeholder_list(line)
8237 } else {
8238 self.parse_one_arg_or_default()?
8239 };
8240 Ok(Expr {
8241 kind: ExprKind::ScalarReverse(Box::new(a)),
8242 line,
8243 })
8244 }
8245 "reverse" | "reversed" => {
8246 let a = if self.in_pipe_rhs()
8248 && matches!(
8249 self.peek(),
8250 Token::Semicolon
8251 | Token::RBrace
8252 | Token::RParen
8253 | Token::Eof
8254 | Token::PipeForward
8255 ) {
8256 self.pipe_placeholder_list(line)
8257 } else {
8258 self.parse_one_arg()?
8259 };
8260 Ok(Expr {
8261 kind: ExprKind::ReverseExpr(Box::new(a)),
8262 line,
8263 })
8264 }
8265 "join" => {
8266 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8267 return Ok(e);
8268 }
8269 let args = self.parse_builtin_args()?;
8270 if args.is_empty() {
8271 return Err(self.syntax_err("join requires separator and list", line));
8272 }
8273 if args.len() < 2 && !self.in_pipe_rhs() {
8275 return Err(self.syntax_err("join requires separator and list", line));
8276 }
8277 Ok(Expr {
8278 kind: ExprKind::JoinExpr {
8279 separator: Box::new(args[0].clone()),
8280 list: Box::new(Expr {
8281 kind: ExprKind::List(args[1..].to_vec()),
8282 line,
8283 }),
8284 },
8285 line,
8286 })
8287 }
8288 "split" => {
8289 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8290 return Ok(e);
8291 }
8292 let args = self.parse_builtin_args()?;
8293 let pattern = args.first().cloned().unwrap_or(Expr {
8294 kind: ExprKind::String(" ".into()),
8295 line,
8296 });
8297 let string = args.get(1).cloned().unwrap_or(Expr {
8298 kind: ExprKind::ScalarVar("_".into()),
8299 line,
8300 });
8301 let limit = args.get(2).cloned().map(Box::new);
8302 Ok(Expr {
8303 kind: ExprKind::SplitExpr {
8304 pattern: Box::new(pattern),
8305 string: Box::new(string),
8306 limit,
8307 },
8308 line,
8309 })
8310 }
8311 "substr" => {
8312 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8313 return Ok(e);
8314 }
8315 let args = self.parse_builtin_args()?;
8316 Ok(Expr {
8317 kind: ExprKind::Substr {
8318 string: Box::new(args[0].clone()),
8319 offset: Box::new(args[1].clone()),
8320 length: args.get(2).cloned().map(Box::new),
8321 replacement: args.get(3).cloned().map(Box::new),
8322 },
8323 line,
8324 })
8325 }
8326 "index" => {
8327 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8328 return Ok(e);
8329 }
8330 let args = self.parse_builtin_args()?;
8331 Ok(Expr {
8332 kind: ExprKind::Index {
8333 string: Box::new(args[0].clone()),
8334 substr: Box::new(args[1].clone()),
8335 position: args.get(2).cloned().map(Box::new),
8336 },
8337 line,
8338 })
8339 }
8340 "rindex" => {
8341 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8342 return Ok(e);
8343 }
8344 let args = self.parse_builtin_args()?;
8345 Ok(Expr {
8346 kind: ExprKind::Rindex {
8347 string: Box::new(args[0].clone()),
8348 substr: Box::new(args[1].clone()),
8349 position: args.get(2).cloned().map(Box::new),
8350 },
8351 line,
8352 })
8353 }
8354 "sprintf" => {
8355 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
8356 return Ok(e);
8357 }
8358 let args = self.parse_builtin_args()?;
8359 let (first, rest) = args
8360 .split_first()
8361 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
8362 Ok(Expr {
8363 kind: ExprKind::Sprintf {
8364 format: Box::new(first.clone()),
8365 args: rest.to_vec(),
8366 },
8367 line,
8368 })
8369 }
8370 "map" | "flat_map" | "maps" | "flat_maps" => {
8371 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
8372 let stream = matches!(name.as_str(), "maps" | "flat_maps");
8373 if matches!(self.peek(), Token::LBrace) {
8374 let (block, list) = self.parse_block_list()?;
8375 Ok(Expr {
8376 kind: ExprKind::MapExpr {
8377 block,
8378 list: Box::new(list),
8379 flatten_array_refs,
8380 stream,
8381 },
8382 line,
8383 })
8384 } else {
8385 let expr = self.parse_assign_expr_stop_at_pipe()?;
8386 let expr = Self::lift_bareword_to_topic_call(expr);
8389 let list_expr = if self.in_pipe_rhs()
8390 && matches!(
8391 self.peek(),
8392 Token::Semicolon
8393 | Token::RBrace
8394 | Token::RParen
8395 | Token::Eof
8396 | Token::PipeForward
8397 ) {
8398 self.pipe_placeholder_list(line)
8399 } else {
8400 self.expect(&Token::Comma)?;
8401 let list_parts = self.parse_list_until_terminator()?;
8402 if list_parts.len() == 1 {
8403 list_parts.into_iter().next().unwrap()
8404 } else {
8405 Expr {
8406 kind: ExprKind::List(list_parts),
8407 line,
8408 }
8409 }
8410 };
8411 Ok(Expr {
8412 kind: ExprKind::MapExprComma {
8413 expr: Box::new(expr),
8414 list: Box::new(list_expr),
8415 flatten_array_refs,
8416 stream,
8417 },
8418 line,
8419 })
8420 }
8421 }
8422 "match" => {
8423 if crate::compat_mode() {
8424 return Err(self.syntax_err(
8425 "algebraic `match` is a stryke extension (disabled by --compat)",
8426 line,
8427 ));
8428 }
8429 self.parse_algebraic_match_expr(line)
8430 }
8431 "grep" | "greps" | "filter" | "f" | "find_all" => {
8432 let keyword = match name.as_str() {
8433 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
8434 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
8435 "filter" | "f" => crate::ast::GrepBuiltinKeyword::Filter,
8436 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
8437 _ => unreachable!(),
8438 };
8439 if matches!(self.peek(), Token::LBrace) {
8440 let (block, list) = self.parse_block_list()?;
8441 Ok(Expr {
8442 kind: ExprKind::GrepExpr {
8443 block,
8444 list: Box::new(list),
8445 keyword,
8446 },
8447 line,
8448 })
8449 } else {
8450 let expr = self.parse_assign_expr_stop_at_pipe()?;
8451 if self.in_pipe_rhs()
8452 && matches!(
8453 self.peek(),
8454 Token::Semicolon
8455 | Token::RBrace
8456 | Token::RParen
8457 | Token::Eof
8458 | Token::PipeForward
8459 )
8460 {
8461 let list = self.pipe_placeholder_list(line);
8466 let topic = Expr {
8467 kind: ExprKind::ScalarVar("_".into()),
8468 line,
8469 };
8470 let test = match &expr.kind {
8471 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
8472 kind: ExprKind::BinOp {
8473 op: BinOp::NumEq,
8474 left: Box::new(topic),
8475 right: Box::new(expr),
8476 },
8477 line,
8478 },
8479 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
8480 kind: ExprKind::BinOp {
8481 op: BinOp::StrEq,
8482 left: Box::new(topic),
8483 right: Box::new(expr),
8484 },
8485 line,
8486 },
8487 ExprKind::Regex { .. } => Expr {
8488 kind: ExprKind::BinOp {
8489 op: BinOp::BindMatch,
8490 left: Box::new(topic),
8491 right: Box::new(expr),
8492 },
8493 line,
8494 },
8495 _ => {
8496 Self::lift_bareword_to_topic_call(expr)
8498 }
8499 };
8500 let block = vec![Statement {
8501 label: None,
8502 kind: StmtKind::Expression(test),
8503 line,
8504 }];
8505 Ok(Expr {
8506 kind: ExprKind::GrepExpr {
8507 block,
8508 list: Box::new(list),
8509 keyword,
8510 },
8511 line,
8512 })
8513 } else {
8514 let expr = Self::lift_bareword_to_topic_call(expr);
8515 self.expect(&Token::Comma)?;
8516 let list_parts = self.parse_list_until_terminator()?;
8517 let list_expr = if list_parts.len() == 1 {
8518 list_parts.into_iter().next().unwrap()
8519 } else {
8520 Expr {
8521 kind: ExprKind::List(list_parts),
8522 line,
8523 }
8524 };
8525 Ok(Expr {
8526 kind: ExprKind::GrepExprComma {
8527 expr: Box::new(expr),
8528 list: Box::new(list_expr),
8529 keyword,
8530 },
8531 line,
8532 })
8533 }
8534 }
8535 }
8536 "sort" => {
8537 use crate::ast::SortComparator;
8538 if matches!(self.peek(), Token::LBrace) {
8539 let block = self.parse_block()?;
8540 let _ = self.eat(&Token::Comma);
8541 let list = if self.in_pipe_rhs()
8542 && matches!(
8543 self.peek(),
8544 Token::Semicolon
8545 | Token::RBrace
8546 | Token::RParen
8547 | Token::Eof
8548 | Token::PipeForward
8549 ) {
8550 self.pipe_placeholder_list(line)
8551 } else {
8552 self.parse_expression()?
8553 };
8554 Ok(Expr {
8555 kind: ExprKind::SortExpr {
8556 cmp: Some(SortComparator::Block(block)),
8557 list: Box::new(list),
8558 },
8559 line,
8560 })
8561 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
8562 let block = self.parse_block_or_bareword_cmp_block()?;
8564 let _ = self.eat(&Token::Comma);
8565 let list = if self.in_pipe_rhs()
8566 && matches!(
8567 self.peek(),
8568 Token::Semicolon
8569 | Token::RBrace
8570 | Token::RParen
8571 | Token::Eof
8572 | Token::PipeForward
8573 ) {
8574 self.pipe_placeholder_list(line)
8575 } else {
8576 self.parse_expression()?
8577 };
8578 Ok(Expr {
8579 kind: ExprKind::SortExpr {
8580 cmp: Some(SortComparator::Block(block)),
8581 list: Box::new(list),
8582 },
8583 line,
8584 })
8585 } else if matches!(self.peek(), Token::ScalarVar(_)) {
8586 self.suppress_indirect_paren_call =
8588 self.suppress_indirect_paren_call.saturating_add(1);
8589 let code = self.parse_assign_expr()?;
8590 self.suppress_indirect_paren_call =
8591 self.suppress_indirect_paren_call.saturating_sub(1);
8592 let list = if matches!(self.peek(), Token::LParen) {
8593 self.advance();
8594 let e = self.parse_expression()?;
8595 self.expect(&Token::RParen)?;
8596 e
8597 } else {
8598 self.parse_expression()?
8599 };
8600 Ok(Expr {
8601 kind: ExprKind::SortExpr {
8602 cmp: Some(SortComparator::Code(Box::new(code))),
8603 list: Box::new(list),
8604 },
8605 line,
8606 })
8607 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
8608 {
8609 let block = self.parse_block_or_bareword_cmp_block()?;
8611 let _ = self.eat(&Token::Comma);
8612 let list = if self.in_pipe_rhs()
8613 && matches!(
8614 self.peek(),
8615 Token::Semicolon
8616 | Token::RBrace
8617 | Token::RParen
8618 | Token::Eof
8619 | Token::PipeForward
8620 ) {
8621 self.pipe_placeholder_list(line)
8622 } else {
8623 self.parse_expression()?
8624 };
8625 Ok(Expr {
8626 kind: ExprKind::SortExpr {
8627 cmp: Some(SortComparator::Block(block)),
8628 list: Box::new(list),
8629 },
8630 line,
8631 })
8632 } else {
8633 let list = if self.in_pipe_rhs()
8636 && matches!(
8637 self.peek(),
8638 Token::Semicolon
8639 | Token::RBrace
8640 | Token::RParen
8641 | Token::Eof
8642 | Token::PipeForward
8643 ) {
8644 self.pipe_placeholder_list(line)
8645 } else {
8646 self.parse_expression()?
8647 };
8648 Ok(Expr {
8649 kind: ExprKind::SortExpr {
8650 cmp: None,
8651 list: Box::new(list),
8652 },
8653 line,
8654 })
8655 }
8656 }
8657 "reduce" | "fold" | "inject" => {
8658 let (block, list) = self.parse_block_list()?;
8659 Ok(Expr {
8660 kind: ExprKind::ReduceExpr {
8661 block,
8662 list: Box::new(list),
8663 },
8664 line,
8665 })
8666 }
8667 "pmap" => {
8669 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
8670 Ok(Expr {
8671 kind: ExprKind::PMapExpr {
8672 block,
8673 list: Box::new(list),
8674 progress: progress.map(Box::new),
8675 flat_outputs: false,
8676 on_cluster: None,
8677 },
8678 line,
8679 })
8680 }
8681 "pmap_on" => {
8682 let (cluster, block, list, progress) =
8683 self.parse_cluster_block_then_list_optional_progress()?;
8684 Ok(Expr {
8685 kind: ExprKind::PMapExpr {
8686 block,
8687 list: Box::new(list),
8688 progress: progress.map(Box::new),
8689 flat_outputs: false,
8690 on_cluster: Some(Box::new(cluster)),
8691 },
8692 line,
8693 })
8694 }
8695 "pflat_map" => {
8696 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
8697 Ok(Expr {
8698 kind: ExprKind::PMapExpr {
8699 block,
8700 list: Box::new(list),
8701 progress: progress.map(Box::new),
8702 flat_outputs: true,
8703 on_cluster: None,
8704 },
8705 line,
8706 })
8707 }
8708 "pflat_map_on" => {
8709 let (cluster, block, list, progress) =
8710 self.parse_cluster_block_then_list_optional_progress()?;
8711 Ok(Expr {
8712 kind: ExprKind::PMapExpr {
8713 block,
8714 list: Box::new(list),
8715 progress: progress.map(Box::new),
8716 flat_outputs: true,
8717 on_cluster: Some(Box::new(cluster)),
8718 },
8719 line,
8720 })
8721 }
8722 "pmap_chunked" => {
8723 let chunk_size = self.parse_assign_expr()?;
8724 let block = self.parse_block_or_bareword_block()?;
8725 self.eat(&Token::Comma);
8726 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
8727 Ok(Expr {
8728 kind: ExprKind::PMapChunkedExpr {
8729 chunk_size: Box::new(chunk_size),
8730 block,
8731 list: Box::new(list),
8732 progress: progress.map(Box::new),
8733 },
8734 line,
8735 })
8736 }
8737 "pgrep" => {
8738 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
8739 Ok(Expr {
8740 kind: ExprKind::PGrepExpr {
8741 block,
8742 list: Box::new(list),
8743 progress: progress.map(Box::new),
8744 },
8745 line,
8746 })
8747 }
8748 "pfor" => {
8749 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
8750 Ok(Expr {
8751 kind: ExprKind::PForExpr {
8752 block,
8753 list: Box::new(list),
8754 progress: progress.map(Box::new),
8755 },
8756 line,
8757 })
8758 }
8759 "par_lines" | "par_walk" => {
8760 let args = self.parse_builtin_args()?;
8761 if args.len() < 2 {
8762 return Err(
8763 self.syntax_err(format!("{} requires at least two arguments", name), line)
8764 );
8765 }
8766
8767 if name == "par_lines" {
8768 Ok(Expr {
8769 kind: ExprKind::ParLinesExpr {
8770 path: Box::new(args[0].clone()),
8771 callback: Box::new(args[1].clone()),
8772 progress: None,
8773 },
8774 line,
8775 })
8776 } else {
8777 Ok(Expr {
8778 kind: ExprKind::ParWalkExpr {
8779 path: Box::new(args[0].clone()),
8780 callback: Box::new(args[1].clone()),
8781 progress: None,
8782 },
8783 line,
8784 })
8785 }
8786 }
8787 "pwatch" | "watch" => {
8788 let args = self.parse_builtin_args()?;
8789 if args.len() < 2 {
8790 return Err(
8791 self.syntax_err(format!("{} requires at least two arguments", name), line)
8792 );
8793 }
8794 Ok(Expr {
8795 kind: ExprKind::PwatchExpr {
8796 path: Box::new(args[0].clone()),
8797 callback: Box::new(args[1].clone()),
8798 },
8799 line,
8800 })
8801 }
8802 "fan" => {
8803 let (count, block) = self.parse_fan_count_and_block(line)?;
8809 let progress = self.parse_fan_optional_progress("fan")?;
8810 Ok(Expr {
8811 kind: ExprKind::FanExpr {
8812 count,
8813 block,
8814 progress,
8815 capture: false,
8816 },
8817 line,
8818 })
8819 }
8820 "fan_cap" => {
8821 let (count, block) = self.parse_fan_count_and_block(line)?;
8822 let progress = self.parse_fan_optional_progress("fan_cap")?;
8823 Ok(Expr {
8824 kind: ExprKind::FanExpr {
8825 count,
8826 block,
8827 progress,
8828 capture: true,
8829 },
8830 line,
8831 })
8832 }
8833 "async" => {
8834 if !matches!(self.peek(), Token::LBrace) {
8835 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
8836 }
8837 let block = self.parse_block()?;
8838 Ok(Expr {
8839 kind: ExprKind::AsyncBlock { body: block },
8840 line,
8841 })
8842 }
8843 "spawn" => {
8844 if !matches!(self.peek(), Token::LBrace) {
8845 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
8846 }
8847 let block = self.parse_block()?;
8848 Ok(Expr {
8849 kind: ExprKind::SpawnBlock { body: block },
8850 line,
8851 })
8852 }
8853 "trace" => {
8854 if !matches!(self.peek(), Token::LBrace) {
8855 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
8856 }
8857 let block = self.parse_block()?;
8858 Ok(Expr {
8859 kind: ExprKind::Trace { body: block },
8860 line,
8861 })
8862 }
8863 "timer" => {
8864 let block = self.parse_block_or_bareword_block_no_args()?;
8865 Ok(Expr {
8866 kind: ExprKind::Timer { body: block },
8867 line,
8868 })
8869 }
8870 "bench" => {
8871 let block = self.parse_block_or_bareword_block_no_args()?;
8872 let times = Box::new(self.parse_expression()?);
8873 Ok(Expr {
8874 kind: ExprKind::Bench { body: block, times },
8875 line,
8876 })
8877 }
8878 "spinner" => {
8879 let (message, body) = if matches!(self.peek(), Token::LBrace) {
8881 let body = self.parse_block()?;
8882 (
8883 Box::new(Expr {
8884 kind: ExprKind::String("working".to_string()),
8885 line,
8886 }),
8887 body,
8888 )
8889 } else {
8890 let msg = self.parse_assign_expr()?;
8891 let body = self.parse_block()?;
8892 (Box::new(msg), body)
8893 };
8894 Ok(Expr {
8895 kind: ExprKind::Spinner { message, body },
8896 line,
8897 })
8898 }
8899 "thread" | "t" => {
8900 self.parse_thread_macro(line)
8910 }
8911 "retry" => {
8912 let body = if matches!(self.peek(), Token::LBrace) {
8915 self.parse_block()?
8916 } else {
8917 let bw_line = self.peek_line();
8918 let Token::Ident(ref name) = self.peek().clone() else {
8919 return Err(self
8920 .syntax_err("retry: expected block or bareword function name", line));
8921 };
8922 let name = name.clone();
8923 self.advance();
8924 vec![Statement::new(
8925 StmtKind::Expression(Expr {
8926 kind: ExprKind::FuncCall { name, args: vec![] },
8927 line: bw_line,
8928 }),
8929 bw_line,
8930 )]
8931 };
8932 self.eat(&Token::Comma);
8933 match self.peek() {
8934 Token::Ident(ref s) if s == "times" => {
8935 self.advance();
8936 }
8937 _ => {
8938 return Err(self.syntax_err("retry: expected `times =>` after block", line));
8939 }
8940 }
8941 self.expect(&Token::FatArrow)?;
8942 let times = Box::new(self.parse_assign_expr()?);
8943 let mut backoff = RetryBackoff::None;
8944 if self.eat(&Token::Comma) {
8945 match self.peek() {
8946 Token::Ident(ref s) if s == "backoff" => {
8947 self.advance();
8948 }
8949 _ => {
8950 return Err(
8951 self.syntax_err("retry: expected `backoff =>` after comma", line)
8952 );
8953 }
8954 }
8955 self.expect(&Token::FatArrow)?;
8956 let Token::Ident(mode) = self.peek().clone() else {
8957 return Err(self.syntax_err(
8958 "retry: expected backoff mode (none, linear, exponential)",
8959 line,
8960 ));
8961 };
8962 backoff = match mode.as_str() {
8963 "none" => RetryBackoff::None,
8964 "linear" => RetryBackoff::Linear,
8965 "exponential" => RetryBackoff::Exponential,
8966 _ => {
8967 return Err(
8968 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
8969 );
8970 }
8971 };
8972 self.advance();
8973 }
8974 Ok(Expr {
8975 kind: ExprKind::RetryBlock {
8976 body,
8977 times,
8978 backoff,
8979 },
8980 line,
8981 })
8982 }
8983 "rate_limit" => {
8984 self.expect(&Token::LParen)?;
8985 let max = Box::new(self.parse_assign_expr()?);
8986 self.expect(&Token::Comma)?;
8987 let window = Box::new(self.parse_assign_expr()?);
8988 self.expect(&Token::RParen)?;
8989 let body = self.parse_block_or_bareword_block_no_args()?;
8990 let slot = self.alloc_rate_limit_slot();
8991 Ok(Expr {
8992 kind: ExprKind::RateLimitBlock {
8993 slot,
8994 max,
8995 window,
8996 body,
8997 },
8998 line,
8999 })
9000 }
9001 "every" => {
9002 let has_paren = self.eat(&Token::LParen);
9005 let interval = Box::new(self.parse_assign_expr()?);
9006 if has_paren {
9007 self.expect(&Token::RParen)?;
9008 }
9009 let body = if matches!(self.peek(), Token::LBrace) {
9010 self.parse_block()?
9011 } else {
9012 let bline = self.peek_line();
9013 let expr = self.parse_assign_expr()?;
9014 vec![Statement::new(StmtKind::Expression(expr), bline)]
9015 };
9016 Ok(Expr {
9017 kind: ExprKind::EveryBlock { interval, body },
9018 line,
9019 })
9020 }
9021 "gen" => {
9022 if !matches!(self.peek(), Token::LBrace) {
9023 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
9024 }
9025 let body = self.parse_block()?;
9026 Ok(Expr {
9027 kind: ExprKind::GenBlock { body },
9028 line,
9029 })
9030 }
9031 "yield" => {
9032 let e = self.parse_assign_expr()?;
9033 Ok(Expr {
9034 kind: ExprKind::Yield(Box::new(e)),
9035 line,
9036 })
9037 }
9038 "await" => {
9039 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9040 return Ok(e);
9041 }
9042 let a = self.parse_one_arg_or_default()?;
9045 Ok(Expr {
9046 kind: ExprKind::Await(Box::new(a)),
9047 line,
9048 })
9049 }
9050 "slurp" | "cat" | "c" => {
9051 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9052 return Ok(e);
9053 }
9054 let a = self.parse_one_arg_or_default()?;
9055 Ok(Expr {
9056 kind: ExprKind::Slurp(Box::new(a)),
9057 line,
9058 })
9059 }
9060 "capture" => {
9061 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9062 return Ok(e);
9063 }
9064 let a = self.parse_one_arg()?;
9065 Ok(Expr {
9066 kind: ExprKind::Capture(Box::new(a)),
9067 line,
9068 })
9069 }
9070 "fetch_url" => {
9071 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9072 return Ok(e);
9073 }
9074 let a = self.parse_one_arg()?;
9075 Ok(Expr {
9076 kind: ExprKind::FetchUrl(Box::new(a)),
9077 line,
9078 })
9079 }
9080 "pchannel" => {
9081 let capacity = if self.eat(&Token::LParen) {
9082 if matches!(self.peek(), Token::RParen) {
9083 self.advance();
9084 None
9085 } else {
9086 let e = self.parse_expression()?;
9087 self.expect(&Token::RParen)?;
9088 Some(Box::new(e))
9089 }
9090 } else {
9091 None
9092 };
9093 Ok(Expr {
9094 kind: ExprKind::Pchannel { capacity },
9095 line,
9096 })
9097 }
9098 "psort" => {
9099 if matches!(self.peek(), Token::LBrace)
9100 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
9101 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
9102 {
9103 let block = self.parse_block_or_bareword_cmp_block()?;
9104 self.eat(&Token::Comma);
9105 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9106 Ok(Expr {
9107 kind: ExprKind::PSortExpr {
9108 cmp: Some(block),
9109 list: Box::new(list),
9110 progress: progress.map(Box::new),
9111 },
9112 line,
9113 })
9114 } else {
9115 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9116 Ok(Expr {
9117 kind: ExprKind::PSortExpr {
9118 cmp: None,
9119 list: Box::new(list),
9120 progress: progress.map(Box::new),
9121 },
9122 line,
9123 })
9124 }
9125 }
9126 "preduce" => {
9127 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9128 Ok(Expr {
9129 kind: ExprKind::PReduceExpr {
9130 block,
9131 list: Box::new(list),
9132 progress: progress.map(Box::new),
9133 },
9134 line,
9135 })
9136 }
9137 "preduce_init" => {
9138 let (init, block, list, progress) =
9139 self.parse_init_block_then_list_optional_progress()?;
9140 Ok(Expr {
9141 kind: ExprKind::PReduceInitExpr {
9142 init: Box::new(init),
9143 block,
9144 list: Box::new(list),
9145 progress: progress.map(Box::new),
9146 },
9147 line,
9148 })
9149 }
9150 "pmap_reduce" => {
9151 let map_block = self.parse_block_or_bareword_block()?;
9152 let reduce_block = if matches!(self.peek(), Token::LBrace) {
9155 self.parse_block()?
9156 } else {
9157 self.expect(&Token::Comma)?;
9159 self.parse_block_or_bareword_cmp_block()?
9160 };
9161 self.eat(&Token::Comma);
9162 let line = self.peek_line();
9163 if let Token::Ident(ref kw) = self.peek().clone() {
9164 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
9165 self.advance();
9166 self.expect(&Token::FatArrow)?;
9167 let prog = self.parse_assign_expr()?;
9168 return Ok(Expr {
9169 kind: ExprKind::PMapReduceExpr {
9170 map_block,
9171 reduce_block,
9172 list: Box::new(Expr {
9173 kind: ExprKind::List(vec![]),
9174 line,
9175 }),
9176 progress: Some(Box::new(prog)),
9177 },
9178 line,
9179 });
9180 }
9181 }
9182 if matches!(
9183 self.peek(),
9184 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
9185 ) {
9186 return Ok(Expr {
9187 kind: ExprKind::PMapReduceExpr {
9188 map_block,
9189 reduce_block,
9190 list: Box::new(Expr {
9191 kind: ExprKind::List(vec![]),
9192 line,
9193 }),
9194 progress: None,
9195 },
9196 line,
9197 });
9198 }
9199 let mut parts = vec![self.parse_assign_expr()?];
9200 loop {
9201 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
9202 break;
9203 }
9204 if matches!(
9205 self.peek(),
9206 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
9207 ) {
9208 break;
9209 }
9210 if let Token::Ident(ref kw) = self.peek().clone() {
9211 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
9212 self.advance();
9213 self.expect(&Token::FatArrow)?;
9214 let prog = self.parse_assign_expr()?;
9215 return Ok(Expr {
9216 kind: ExprKind::PMapReduceExpr {
9217 map_block,
9218 reduce_block,
9219 list: Box::new(merge_expr_list(parts)),
9220 progress: Some(Box::new(prog)),
9221 },
9222 line,
9223 });
9224 }
9225 }
9226 parts.push(self.parse_assign_expr()?);
9227 }
9228 Ok(Expr {
9229 kind: ExprKind::PMapReduceExpr {
9230 map_block,
9231 reduce_block,
9232 list: Box::new(merge_expr_list(parts)),
9233 progress: None,
9234 },
9235 line,
9236 })
9237 }
9238 "puniq" => {
9239 if self.pipe_supplies_slurped_list_operand() {
9240 return Ok(Expr {
9241 kind: ExprKind::FuncCall {
9242 name: "puniq".to_string(),
9243 args: vec![],
9244 },
9245 line,
9246 });
9247 }
9248 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9249 let mut args = vec![list];
9250 if let Some(p) = progress {
9251 args.push(p);
9252 }
9253 Ok(Expr {
9254 kind: ExprKind::FuncCall {
9255 name: "puniq".to_string(),
9256 args,
9257 },
9258 line,
9259 })
9260 }
9261 "pfirst" => {
9262 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9263 let cr = Expr {
9264 kind: ExprKind::CodeRef {
9265 params: vec![],
9266 body: block,
9267 },
9268 line,
9269 };
9270 let mut args = vec![cr, list];
9271 if let Some(p) = progress {
9272 args.push(p);
9273 }
9274 Ok(Expr {
9275 kind: ExprKind::FuncCall {
9276 name: "pfirst".to_string(),
9277 args,
9278 },
9279 line,
9280 })
9281 }
9282 "pany" => {
9283 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9284 let cr = Expr {
9285 kind: ExprKind::CodeRef {
9286 params: vec![],
9287 body: block,
9288 },
9289 line,
9290 };
9291 let mut args = vec![cr, list];
9292 if let Some(p) = progress {
9293 args.push(p);
9294 }
9295 Ok(Expr {
9296 kind: ExprKind::FuncCall {
9297 name: "pany".to_string(),
9298 args,
9299 },
9300 line,
9301 })
9302 }
9303 "uniq" | "distinct" => {
9304 if self.pipe_supplies_slurped_list_operand() {
9305 return Ok(Expr {
9306 kind: ExprKind::FuncCall {
9307 name: name.clone(),
9308 args: vec![],
9309 },
9310 line,
9311 });
9312 }
9313 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9314 if progress.is_some() {
9315 return Err(self.syntax_err(
9316 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
9317 line,
9318 ));
9319 }
9320 Ok(Expr {
9321 kind: ExprKind::FuncCall {
9322 name: name.clone(),
9323 args: vec![list],
9324 },
9325 line,
9326 })
9327 }
9328 "flatten" => {
9329 if self.pipe_supplies_slurped_list_operand() {
9330 return Ok(Expr {
9331 kind: ExprKind::FuncCall {
9332 name: "flatten".to_string(),
9333 args: vec![],
9334 },
9335 line,
9336 });
9337 }
9338 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9339 if progress.is_some() {
9340 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
9341 }
9342 Ok(Expr {
9343 kind: ExprKind::FuncCall {
9344 name: "flatten".to_string(),
9345 args: vec![list],
9346 },
9347 line,
9348 })
9349 }
9350 "set" => {
9351 if self.pipe_supplies_slurped_list_operand() {
9352 return Ok(Expr {
9353 kind: ExprKind::FuncCall {
9354 name: "set".to_string(),
9355 args: vec![],
9356 },
9357 line,
9358 });
9359 }
9360 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9361 if progress.is_some() {
9362 return Err(self.syntax_err("`progress =>` is not supported for set", line));
9363 }
9364 Ok(Expr {
9365 kind: ExprKind::FuncCall {
9366 name: "set".to_string(),
9367 args: vec![list],
9368 },
9369 line,
9370 })
9371 }
9372 "size" => {
9376 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9377 return Ok(e);
9378 }
9379 if self.pipe_supplies_slurped_list_operand() {
9380 return Ok(Expr {
9381 kind: ExprKind::FuncCall {
9382 name: "size".to_string(),
9383 args: vec![],
9384 },
9385 line,
9386 });
9387 }
9388 let a = self.parse_one_arg_or_default()?;
9389 Ok(Expr {
9390 kind: ExprKind::FuncCall {
9391 name: "size".to_string(),
9392 args: vec![a],
9393 },
9394 line,
9395 })
9396 }
9397 "list_count" | "list_size" | "count" | "len" | "cnt" => {
9398 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9399 return Ok(e);
9400 }
9401 if self.pipe_supplies_slurped_list_operand() {
9402 return Ok(Expr {
9403 kind: ExprKind::FuncCall {
9404 name: name.clone(),
9405 args: vec![],
9406 },
9407 line,
9408 });
9409 }
9410 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9411 if progress.is_some() {
9412 return Err(self.syntax_err(
9413 "`progress =>` is not supported for list_count / list_size / count / cnt",
9414 line,
9415 ));
9416 }
9417 Ok(Expr {
9418 kind: ExprKind::FuncCall {
9419 name: name.clone(),
9420 args: vec![list],
9421 },
9422 line,
9423 })
9424 }
9425 "shuffle" | "shuffled" => {
9426 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9427 return Ok(e);
9428 }
9429 if self.pipe_supplies_slurped_list_operand() {
9430 return Ok(Expr {
9431 kind: ExprKind::FuncCall {
9432 name: "shuffle".to_string(),
9433 args: vec![],
9434 },
9435 line,
9436 });
9437 }
9438 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9439 if progress.is_some() {
9440 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
9441 }
9442 Ok(Expr {
9443 kind: ExprKind::FuncCall {
9444 name: "shuffle".to_string(),
9445 args: vec![list],
9446 },
9447 line,
9448 })
9449 }
9450 "chunked" => {
9451 let mut parts = Vec::new();
9452 if self.eat(&Token::LParen) {
9453 if !matches!(self.peek(), Token::RParen) {
9454 parts.push(self.parse_assign_expr()?);
9455 while self.eat(&Token::Comma) {
9456 if matches!(self.peek(), Token::RParen) {
9457 break;
9458 }
9459 parts.push(self.parse_assign_expr()?);
9460 }
9461 }
9462 self.expect(&Token::RParen)?;
9463 } else {
9464 parts.push(self.parse_assign_expr_stop_at_pipe()?);
9468 loop {
9469 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
9470 break;
9471 }
9472 if matches!(
9473 self.peek(),
9474 Token::Semicolon
9475 | Token::RBrace
9476 | Token::RParen
9477 | Token::Eof
9478 | Token::PipeForward
9479 ) {
9480 break;
9481 }
9482 if self.peek_is_postfix_stmt_modifier_keyword() {
9483 break;
9484 }
9485 parts.push(self.parse_assign_expr_stop_at_pipe()?);
9486 }
9487 }
9488 if parts.len() == 1 {
9489 let n = parts.pop().unwrap();
9490 return Ok(Expr {
9491 kind: ExprKind::FuncCall {
9492 name: "chunked".to_string(),
9493 args: vec![n],
9494 },
9495 line,
9496 });
9497 }
9498 if parts.is_empty() {
9499 return Ok(Expr {
9500 kind: ExprKind::FuncCall {
9501 name: "chunked".to_string(),
9502 args: parts,
9503 },
9504 line,
9505 });
9506 }
9507 if parts.len() == 2 {
9508 let n = parts.pop().unwrap();
9509 let list = parts.pop().unwrap();
9510 return Ok(Expr {
9511 kind: ExprKind::FuncCall {
9512 name: "chunked".to_string(),
9513 args: vec![list, n],
9514 },
9515 line,
9516 });
9517 }
9518 Err(self.syntax_err(
9519 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
9520 line,
9521 ))
9522 }
9523 "windowed" => {
9524 let mut parts = Vec::new();
9525 if self.eat(&Token::LParen) {
9526 if !matches!(self.peek(), Token::RParen) {
9527 parts.push(self.parse_assign_expr()?);
9528 while self.eat(&Token::Comma) {
9529 if matches!(self.peek(), Token::RParen) {
9530 break;
9531 }
9532 parts.push(self.parse_assign_expr()?);
9533 }
9534 }
9535 self.expect(&Token::RParen)?;
9536 } else {
9537 parts.push(self.parse_assign_expr_stop_at_pipe()?);
9540 loop {
9541 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
9542 break;
9543 }
9544 if matches!(
9545 self.peek(),
9546 Token::Semicolon
9547 | Token::RBrace
9548 | Token::RParen
9549 | Token::Eof
9550 | Token::PipeForward
9551 ) {
9552 break;
9553 }
9554 if self.peek_is_postfix_stmt_modifier_keyword() {
9555 break;
9556 }
9557 parts.push(self.parse_assign_expr_stop_at_pipe()?);
9558 }
9559 }
9560 if parts.len() == 1 {
9561 let n = parts.pop().unwrap();
9562 return Ok(Expr {
9563 kind: ExprKind::FuncCall {
9564 name: "windowed".to_string(),
9565 args: vec![n],
9566 },
9567 line,
9568 });
9569 }
9570 if parts.is_empty() {
9571 return Ok(Expr {
9572 kind: ExprKind::FuncCall {
9573 name: "windowed".to_string(),
9574 args: parts,
9575 },
9576 line,
9577 });
9578 }
9579 if parts.len() == 2 {
9580 let n = parts.pop().unwrap();
9581 let list = parts.pop().unwrap();
9582 return Ok(Expr {
9583 kind: ExprKind::FuncCall {
9584 name: "windowed".to_string(),
9585 args: vec![list, n],
9586 },
9587 line,
9588 });
9589 }
9590 Err(self.syntax_err(
9591 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
9592 line,
9593 ))
9594 }
9595 "any" | "all" | "none" => {
9596 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9597 if progress.is_some() {
9598 return Err(self.syntax_err(
9599 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
9600 line,
9601 ));
9602 }
9603 let cr = Expr {
9604 kind: ExprKind::CodeRef {
9605 params: vec![],
9606 body: block,
9607 },
9608 line,
9609 };
9610 Ok(Expr {
9611 kind: ExprKind::FuncCall {
9612 name: name.clone(),
9613 args: vec![cr, list],
9614 },
9615 line,
9616 })
9617 }
9618 "first" | "detect" | "find" => {
9620 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9621 if progress.is_some() {
9622 return Err(self.syntax_err(
9623 "`progress =>` is not supported for first/detect/find (use pfirst for parallel + progress)",
9624 line,
9625 ));
9626 }
9627 let cr = Expr {
9628 kind: ExprKind::CodeRef {
9629 params: vec![],
9630 body: block,
9631 },
9632 line,
9633 };
9634 Ok(Expr {
9635 kind: ExprKind::FuncCall {
9636 name: "first".to_string(),
9637 args: vec![cr, list],
9638 },
9639 line,
9640 })
9641 }
9642 "take_while" | "drop_while" | "skip_while" | "reject" | "tap" | "peek"
9643 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
9644 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9645 if progress.is_some() {
9646 return Err(
9647 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
9648 );
9649 }
9650 let cr = Expr {
9651 kind: ExprKind::CodeRef {
9652 params: vec![],
9653 body: block,
9654 },
9655 line,
9656 };
9657 Ok(Expr {
9658 kind: ExprKind::FuncCall {
9659 name: name.to_string(),
9660 args: vec![cr, list],
9661 },
9662 line,
9663 })
9664 }
9665 "group_by" | "chunk_by" => {
9666 if matches!(self.peek(), Token::LBrace) {
9667 let (block, list) = self.parse_block_list()?;
9668 let cr = Expr {
9669 kind: ExprKind::CodeRef {
9670 params: vec![],
9671 body: block,
9672 },
9673 line,
9674 };
9675 Ok(Expr {
9676 kind: ExprKind::FuncCall {
9677 name: name.to_string(),
9678 args: vec![cr, list],
9679 },
9680 line,
9681 })
9682 } else {
9683 let key_expr = self.parse_assign_expr()?;
9684 self.expect(&Token::Comma)?;
9685 let list_parts = self.parse_list_until_terminator()?;
9686 let list_expr = if list_parts.len() == 1 {
9687 list_parts.into_iter().next().unwrap()
9688 } else {
9689 Expr {
9690 kind: ExprKind::List(list_parts),
9691 line,
9692 }
9693 };
9694 Ok(Expr {
9695 kind: ExprKind::FuncCall {
9696 name: name.to_string(),
9697 args: vec![key_expr, list_expr],
9698 },
9699 line,
9700 })
9701 }
9702 }
9703 "with_index" => {
9704 if self.pipe_supplies_slurped_list_operand() {
9705 return Ok(Expr {
9706 kind: ExprKind::FuncCall {
9707 name: "with_index".to_string(),
9708 args: vec![],
9709 },
9710 line,
9711 });
9712 }
9713 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
9714 if progress.is_some() {
9715 return Err(
9716 self.syntax_err("`progress =>` is not supported for with_index", line)
9717 );
9718 }
9719 Ok(Expr {
9720 kind: ExprKind::FuncCall {
9721 name: "with_index".to_string(),
9722 args: vec![list],
9723 },
9724 line,
9725 })
9726 }
9727 "pcache" => {
9728 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
9729 Ok(Expr {
9730 kind: ExprKind::PcacheExpr {
9731 block,
9732 list: Box::new(list),
9733 progress: progress.map(Box::new),
9734 },
9735 line,
9736 })
9737 }
9738 "pselect" => {
9739 let paren = self.eat(&Token::LParen);
9740 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
9741 if paren {
9742 self.expect(&Token::RParen)?;
9743 }
9744 if receivers.is_empty() {
9745 return Err(self.syntax_err("pselect needs at least one receiver", line));
9746 }
9747 Ok(Expr {
9748 kind: ExprKind::PselectExpr {
9749 receivers,
9750 timeout: timeout.map(Box::new),
9751 },
9752 line,
9753 })
9754 }
9755 "open" => {
9756 let paren = matches!(self.peek(), Token::LParen);
9757 if paren {
9758 self.advance();
9759 }
9760 if matches!(self.peek(), Token::Ident(ref s) if s == "my") {
9761 self.advance();
9762 let name = self.parse_scalar_var_name()?;
9763 self.expect(&Token::Comma)?;
9764 let mode = self.parse_assign_expr()?;
9765 let file = if self.eat(&Token::Comma) {
9766 Some(self.parse_assign_expr()?)
9767 } else {
9768 None
9769 };
9770 if paren {
9771 self.expect(&Token::RParen)?;
9772 }
9773 Ok(Expr {
9774 kind: ExprKind::Open {
9775 handle: Box::new(Expr {
9776 kind: ExprKind::OpenMyHandle { name },
9777 line,
9778 }),
9779 mode: Box::new(mode),
9780 file: file.map(Box::new),
9781 },
9782 line,
9783 })
9784 } else {
9785 let args = if paren {
9786 self.parse_arg_list()?
9787 } else {
9788 self.parse_list_until_terminator()?
9789 };
9790 if paren {
9791 self.expect(&Token::RParen)?;
9792 }
9793 if args.len() < 2 {
9794 return Err(self.syntax_err("open requires at least 2 arguments", line));
9795 }
9796 Ok(Expr {
9797 kind: ExprKind::Open {
9798 handle: Box::new(args[0].clone()),
9799 mode: Box::new(args[1].clone()),
9800 file: args.get(2).cloned().map(Box::new),
9801 },
9802 line,
9803 })
9804 }
9805 }
9806 "close" => {
9807 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9808 return Ok(e);
9809 }
9810 let a = self.parse_one_arg_or_default()?;
9811 Ok(Expr {
9812 kind: ExprKind::Close(Box::new(a)),
9813 line,
9814 })
9815 }
9816 "opendir" => {
9817 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9818 return Ok(e);
9819 }
9820 let args = self.parse_builtin_args()?;
9821 if args.len() != 2 {
9822 return Err(self.syntax_err("opendir requires two arguments", line));
9823 }
9824 Ok(Expr {
9825 kind: ExprKind::Opendir {
9826 handle: Box::new(args[0].clone()),
9827 path: Box::new(args[1].clone()),
9828 },
9829 line,
9830 })
9831 }
9832 "readdir" => {
9833 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9834 return Ok(e);
9835 }
9836 let a = self.parse_one_arg()?;
9837 Ok(Expr {
9838 kind: ExprKind::Readdir(Box::new(a)),
9839 line,
9840 })
9841 }
9842 "closedir" => {
9843 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9844 return Ok(e);
9845 }
9846 let a = self.parse_one_arg()?;
9847 Ok(Expr {
9848 kind: ExprKind::Closedir(Box::new(a)),
9849 line,
9850 })
9851 }
9852 "rewinddir" => {
9853 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9854 return Ok(e);
9855 }
9856 let a = self.parse_one_arg()?;
9857 Ok(Expr {
9858 kind: ExprKind::Rewinddir(Box::new(a)),
9859 line,
9860 })
9861 }
9862 "telldir" => {
9863 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9864 return Ok(e);
9865 }
9866 let a = self.parse_one_arg()?;
9867 Ok(Expr {
9868 kind: ExprKind::Telldir(Box::new(a)),
9869 line,
9870 })
9871 }
9872 "seekdir" => {
9873 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9874 return Ok(e);
9875 }
9876 let args = self.parse_builtin_args()?;
9877 if args.len() != 2 {
9878 return Err(self.syntax_err("seekdir requires two arguments", line));
9879 }
9880 Ok(Expr {
9881 kind: ExprKind::Seekdir {
9882 handle: Box::new(args[0].clone()),
9883 position: Box::new(args[1].clone()),
9884 },
9885 line,
9886 })
9887 }
9888 "eof" => {
9889 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9890 return Ok(e);
9891 }
9892 if matches!(self.peek(), Token::LParen) {
9893 self.advance();
9894 if matches!(self.peek(), Token::RParen) {
9895 self.advance();
9896 Ok(Expr {
9897 kind: ExprKind::Eof(None),
9898 line,
9899 })
9900 } else {
9901 let a = self.parse_expression()?;
9902 self.expect(&Token::RParen)?;
9903 Ok(Expr {
9904 kind: ExprKind::Eof(Some(Box::new(a))),
9905 line,
9906 })
9907 }
9908 } else {
9909 Ok(Expr {
9910 kind: ExprKind::Eof(None),
9911 line,
9912 })
9913 }
9914 }
9915 "system" => {
9916 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9917 return Ok(e);
9918 }
9919 let args = self.parse_builtin_args()?;
9920 Ok(Expr {
9921 kind: ExprKind::System(args),
9922 line,
9923 })
9924 }
9925 "exec" => {
9926 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9927 return Ok(e);
9928 }
9929 let args = self.parse_builtin_args()?;
9930 Ok(Expr {
9931 kind: ExprKind::Exec(args),
9932 line,
9933 })
9934 }
9935 "eval" => {
9936 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9937 return Ok(e);
9938 }
9939 let a = if matches!(self.peek(), Token::LBrace) {
9940 let block = self.parse_block()?;
9941 Expr {
9942 kind: ExprKind::CodeRef {
9943 params: vec![],
9944 body: block,
9945 },
9946 line,
9947 }
9948 } else {
9949 self.parse_one_arg_or_default()?
9950 };
9951 Ok(Expr {
9952 kind: ExprKind::Eval(Box::new(a)),
9953 line,
9954 })
9955 }
9956 "do" => {
9957 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9958 return Ok(e);
9959 }
9960 let a = self.parse_one_arg()?;
9961 Ok(Expr {
9962 kind: ExprKind::Do(Box::new(a)),
9963 line,
9964 })
9965 }
9966 "require" => {
9967 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9968 return Ok(e);
9969 }
9970 let a = self.parse_one_arg()?;
9971 Ok(Expr {
9972 kind: ExprKind::Require(Box::new(a)),
9973 line,
9974 })
9975 }
9976 "exit" => {
9977 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9978 return Ok(e);
9979 }
9980 if matches!(
9981 self.peek(),
9982 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
9983 ) {
9984 Ok(Expr {
9985 kind: ExprKind::Exit(None),
9986 line,
9987 })
9988 } else {
9989 let a = self.parse_one_arg()?;
9990 Ok(Expr {
9991 kind: ExprKind::Exit(Some(Box::new(a))),
9992 line,
9993 })
9994 }
9995 }
9996 "chdir" => {
9997 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9998 return Ok(e);
9999 }
10000 let a = self.parse_one_arg_or_default()?;
10001 Ok(Expr {
10002 kind: ExprKind::Chdir(Box::new(a)),
10003 line,
10004 })
10005 }
10006 "mkdir" => {
10007 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10008 return Ok(e);
10009 }
10010 let args = self.parse_builtin_args()?;
10011 Ok(Expr {
10012 kind: ExprKind::Mkdir {
10013 path: Box::new(args[0].clone()),
10014 mode: args.get(1).cloned().map(Box::new),
10015 },
10016 line,
10017 })
10018 }
10019 "unlink" | "rm" => {
10020 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10021 return Ok(e);
10022 }
10023 let args = self.parse_builtin_args()?;
10024 Ok(Expr {
10025 kind: ExprKind::Unlink(args),
10026 line,
10027 })
10028 }
10029 "rename" => {
10030 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10031 return Ok(e);
10032 }
10033 let args = self.parse_builtin_args()?;
10034 if args.len() != 2 {
10035 return Err(self.syntax_err("rename requires two arguments", line));
10036 }
10037 Ok(Expr {
10038 kind: ExprKind::Rename {
10039 old: Box::new(args[0].clone()),
10040 new: Box::new(args[1].clone()),
10041 },
10042 line,
10043 })
10044 }
10045 "chmod" => {
10046 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10047 return Ok(e);
10048 }
10049 let args = self.parse_builtin_args()?;
10050 if args.len() < 2 {
10051 return Err(self.syntax_err("chmod requires mode and at least one file", line));
10052 }
10053 Ok(Expr {
10054 kind: ExprKind::Chmod(args),
10055 line,
10056 })
10057 }
10058 "chown" => {
10059 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10060 return Ok(e);
10061 }
10062 let args = self.parse_builtin_args()?;
10063 if args.len() < 3 {
10064 return Err(
10065 self.syntax_err("chown requires uid, gid, and at least one file", line)
10066 );
10067 }
10068 Ok(Expr {
10069 kind: ExprKind::Chown(args),
10070 line,
10071 })
10072 }
10073 "stat" => {
10074 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10075 return Ok(e);
10076 }
10077 let args = self.parse_builtin_args()?;
10078 let arg = if args.len() == 1 {
10079 args[0].clone()
10080 } else if args.is_empty() {
10081 Expr {
10082 kind: ExprKind::ScalarVar("_".into()),
10083 line,
10084 }
10085 } else {
10086 return Err(self.syntax_err("stat requires zero or one argument", line));
10087 };
10088 Ok(Expr {
10089 kind: ExprKind::Stat(Box::new(arg)),
10090 line,
10091 })
10092 }
10093 "lstat" => {
10094 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10095 return Ok(e);
10096 }
10097 let args = self.parse_builtin_args()?;
10098 let arg = if args.len() == 1 {
10099 args[0].clone()
10100 } else if args.is_empty() {
10101 Expr {
10102 kind: ExprKind::ScalarVar("_".into()),
10103 line,
10104 }
10105 } else {
10106 return Err(self.syntax_err("lstat requires zero or one argument", line));
10107 };
10108 Ok(Expr {
10109 kind: ExprKind::Lstat(Box::new(arg)),
10110 line,
10111 })
10112 }
10113 "link" => {
10114 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10115 return Ok(e);
10116 }
10117 let args = self.parse_builtin_args()?;
10118 if args.len() != 2 {
10119 return Err(self.syntax_err("link requires two arguments", line));
10120 }
10121 Ok(Expr {
10122 kind: ExprKind::Link {
10123 old: Box::new(args[0].clone()),
10124 new: Box::new(args[1].clone()),
10125 },
10126 line,
10127 })
10128 }
10129 "symlink" => {
10130 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10131 return Ok(e);
10132 }
10133 let args = self.parse_builtin_args()?;
10134 if args.len() != 2 {
10135 return Err(self.syntax_err("symlink requires two arguments", line));
10136 }
10137 Ok(Expr {
10138 kind: ExprKind::Symlink {
10139 old: Box::new(args[0].clone()),
10140 new: Box::new(args[1].clone()),
10141 },
10142 line,
10143 })
10144 }
10145 "readlink" => {
10146 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10147 return Ok(e);
10148 }
10149 let args = self.parse_builtin_args()?;
10150 let arg = if args.len() == 1 {
10151 args[0].clone()
10152 } else if args.is_empty() {
10153 Expr {
10154 kind: ExprKind::ScalarVar("_".into()),
10155 line,
10156 }
10157 } else {
10158 return Err(self.syntax_err("readlink requires zero or one argument", line));
10159 };
10160 Ok(Expr {
10161 kind: ExprKind::Readlink(Box::new(arg)),
10162 line,
10163 })
10164 }
10165 "files" => {
10166 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10167 return Ok(e);
10168 }
10169 let args = self.parse_builtin_args()?;
10170 Ok(Expr {
10171 kind: ExprKind::Files(args),
10172 line,
10173 })
10174 }
10175 "filesf" => {
10176 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10177 return Ok(e);
10178 }
10179 let args = self.parse_builtin_args()?;
10180 Ok(Expr {
10181 kind: ExprKind::Filesf(args),
10182 line,
10183 })
10184 }
10185 "fr" => {
10186 let args = self.parse_builtin_args()?;
10187 Ok(Expr {
10188 kind: ExprKind::FilesfRecursive(args),
10189 line,
10190 })
10191 }
10192 "dirs" | "d" => {
10193 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10194 return Ok(e);
10195 }
10196 let args = self.parse_builtin_args()?;
10197 Ok(Expr {
10198 kind: ExprKind::Dirs(args),
10199 line,
10200 })
10201 }
10202 "dr" => {
10203 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10204 return Ok(e);
10205 }
10206 let args = self.parse_builtin_args()?;
10207 Ok(Expr {
10208 kind: ExprKind::DirsRecursive(args),
10209 line,
10210 })
10211 }
10212 "sym_links" => {
10213 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10214 return Ok(e);
10215 }
10216 let args = self.parse_builtin_args()?;
10217 Ok(Expr {
10218 kind: ExprKind::SymLinks(args),
10219 line,
10220 })
10221 }
10222 "sockets" => {
10223 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10224 return Ok(e);
10225 }
10226 let args = self.parse_builtin_args()?;
10227 Ok(Expr {
10228 kind: ExprKind::Sockets(args),
10229 line,
10230 })
10231 }
10232 "pipes" => {
10233 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10234 return Ok(e);
10235 }
10236 let args = self.parse_builtin_args()?;
10237 Ok(Expr {
10238 kind: ExprKind::Pipes(args),
10239 line,
10240 })
10241 }
10242 "block_devices" => {
10243 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10244 return Ok(e);
10245 }
10246 let args = self.parse_builtin_args()?;
10247 Ok(Expr {
10248 kind: ExprKind::BlockDevices(args),
10249 line,
10250 })
10251 }
10252 "char_devices" => {
10253 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10254 return Ok(e);
10255 }
10256 let args = self.parse_builtin_args()?;
10257 Ok(Expr {
10258 kind: ExprKind::CharDevices(args),
10259 line,
10260 })
10261 }
10262 "glob" => {
10263 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10264 return Ok(e);
10265 }
10266 let args = self.parse_builtin_args()?;
10267 Ok(Expr {
10268 kind: ExprKind::Glob(args),
10269 line,
10270 })
10271 }
10272 "glob_par" => {
10273 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10274 return Ok(e);
10275 }
10276 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
10277 Ok(Expr {
10278 kind: ExprKind::GlobPar { args, progress },
10279 line,
10280 })
10281 }
10282 "par_sed" => {
10283 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10284 return Ok(e);
10285 }
10286 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
10287 Ok(Expr {
10288 kind: ExprKind::ParSed { args, progress },
10289 line,
10290 })
10291 }
10292 "bless" => {
10293 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10294 return Ok(e);
10295 }
10296 let args = self.parse_builtin_args()?;
10297 Ok(Expr {
10298 kind: ExprKind::Bless {
10299 ref_expr: Box::new(args[0].clone()),
10300 class: args.get(1).cloned().map(Box::new),
10301 },
10302 line,
10303 })
10304 }
10305 "caller" => {
10306 if matches!(self.peek(), Token::LParen) {
10307 self.advance();
10308 if matches!(self.peek(), Token::RParen) {
10309 self.advance();
10310 Ok(Expr {
10311 kind: ExprKind::Caller(None),
10312 line,
10313 })
10314 } else {
10315 let a = self.parse_expression()?;
10316 self.expect(&Token::RParen)?;
10317 Ok(Expr {
10318 kind: ExprKind::Caller(Some(Box::new(a))),
10319 line,
10320 })
10321 }
10322 } else {
10323 Ok(Expr {
10324 kind: ExprKind::Caller(None),
10325 line,
10326 })
10327 }
10328 }
10329 "wantarray" => Ok(Expr {
10330 kind: ExprKind::Wantarray,
10331 line,
10332 }),
10333 "sub" | "fn" => {
10334 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
10336 let body = self.parse_block()?;
10337 Ok(Expr {
10338 kind: ExprKind::CodeRef { params, body },
10339 line,
10340 })
10341 }
10342 _ => {
10343 if matches!(self.peek(), Token::FatArrow) {
10346 return Ok(Expr {
10347 kind: ExprKind::String(name),
10348 line,
10349 });
10350 }
10351 if matches!(self.peek(), Token::LParen) {
10353 self.advance();
10354 let args = self.parse_arg_list()?;
10355 self.expect(&Token::RParen)?;
10356 Ok(Expr {
10357 kind: ExprKind::FuncCall { name, args },
10358 line,
10359 })
10360 } else if self.peek().is_term_start()
10361 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
10362 && matches!(self.peek_at(1), Token::Ident(_)))
10363 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
10364 {
10365 let args = self.parse_list_until_terminator()?;
10373 Ok(Expr {
10374 kind: ExprKind::FuncCall { name, args },
10375 line,
10376 })
10377 } else {
10378 Ok(Expr {
10384 kind: ExprKind::Bareword(name),
10385 line,
10386 })
10387 }
10388 }
10389 }
10390 }
10391
10392 fn parse_print_like(
10393 &mut self,
10394 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
10395 ) -> PerlResult<Expr> {
10396 let line = self.peek_line();
10397 let handle = if let Token::Ident(ref h) = self.peek().clone() {
10399 if h.chars().all(|c| c.is_uppercase() || c == '_')
10400 && !matches!(self.peek(), Token::LParen)
10401 {
10402 let h = h.clone();
10403 let saved = self.pos;
10404 self.advance();
10405 if self.peek().is_term_start()
10407 || matches!(
10408 self.peek(),
10409 Token::DoubleString(_) | Token::BacktickString(_) | Token::SingleString(_)
10410 )
10411 {
10412 Some(h)
10413 } else {
10414 self.pos = saved;
10415 None
10416 }
10417 } else {
10418 None
10419 }
10420 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
10421 let v = v.clone();
10431 if v == "_" {
10432 None
10433 } else {
10434 let saved = self.pos;
10435 self.advance();
10436 let next = self.peek().clone();
10437 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
10438 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
10439 if !is_stmt_modifier
10440 && !matches!(next, Token::LBracket | Token::LBrace)
10441 && (next.is_term_start()
10442 || matches!(
10443 next,
10444 Token::DoubleString(_)
10445 | Token::BacktickString(_)
10446 | Token::SingleString(_)
10447 ))
10448 {
10449 Some(format!("${v}"))
10451 } else {
10452 self.pos = saved;
10453 None
10454 }
10455 }
10456 } else {
10457 None
10458 };
10459 let args =
10464 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
10465 let line_topic = self.peek_line();
10466 self.advance(); self.advance(); vec![Expr {
10469 kind: ExprKind::ScalarVar("_".into()),
10470 line: line_topic,
10471 }]
10472 } else {
10473 self.parse_list_until_terminator()?
10474 };
10475 Ok(Expr {
10476 kind: make(handle, args),
10477 line,
10478 })
10479 }
10480
10481 fn parse_block_list(&mut self) -> PerlResult<(Block, Expr)> {
10482 let block = self.parse_block()?;
10483 let block_end_line = self.prev_line();
10484 self.eat(&Token::Comma);
10485 if self.in_pipe_rhs()
10489 && (matches!(
10490 self.peek(),
10491 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
10492 ) || self.peek_line() > block_end_line)
10493 {
10494 let line = self.peek_line();
10495 return Ok((block, self.pipe_placeholder_list(line)));
10496 }
10497 let list = self.parse_expression()?;
10498 Ok((block, list))
10499 }
10500
10501 fn parse_comma_expr_list_with_timeout_tail(
10504 &mut self,
10505 paren: bool,
10506 ) -> PerlResult<(Vec<Expr>, Option<Expr>)> {
10507 let mut parts = vec![self.parse_assign_expr()?];
10508 loop {
10509 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
10510 break;
10511 }
10512 if paren && matches!(self.peek(), Token::RParen) {
10513 break;
10514 }
10515 if matches!(
10516 self.peek(),
10517 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
10518 ) {
10519 break;
10520 }
10521 if self.peek_is_postfix_stmt_modifier_keyword() {
10522 break;
10523 }
10524 if let Token::Ident(ref kw) = self.peek().clone() {
10525 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
10526 self.advance();
10527 self.expect(&Token::FatArrow)?;
10528 let t = self.parse_assign_expr()?;
10529 return Ok((parts, Some(t)));
10530 }
10531 }
10532 parts.push(self.parse_assign_expr()?);
10533 }
10534 Ok((parts, None))
10535 }
10536
10537 fn parse_init_block_then_list_optional_progress(
10539 &mut self,
10540 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
10541 let init = self.parse_assign_expr()?;
10542 self.expect(&Token::Comma)?;
10543 let block = self.parse_block_or_bareword_block()?;
10544 self.eat(&Token::Comma);
10545 let line = self.peek_line();
10546 if let Token::Ident(ref kw) = self.peek().clone() {
10547 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10548 self.advance();
10549 self.expect(&Token::FatArrow)?;
10550 let prog = self.parse_assign_expr()?;
10551 return Ok((
10552 init,
10553 block,
10554 Expr {
10555 kind: ExprKind::List(vec![]),
10556 line,
10557 },
10558 Some(prog),
10559 ));
10560 }
10561 }
10562 if matches!(
10563 self.peek(),
10564 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
10565 ) {
10566 return Ok((
10567 init,
10568 block,
10569 Expr {
10570 kind: ExprKind::List(vec![]),
10571 line,
10572 },
10573 None,
10574 ));
10575 }
10576 let mut parts = vec![self.parse_assign_expr()?];
10577 loop {
10578 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
10579 break;
10580 }
10581 if matches!(
10582 self.peek(),
10583 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
10584 ) {
10585 break;
10586 }
10587 if self.peek_is_postfix_stmt_modifier_keyword() {
10588 break;
10589 }
10590 if let Token::Ident(ref kw) = self.peek().clone() {
10591 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10592 self.advance();
10593 self.expect(&Token::FatArrow)?;
10594 let prog = self.parse_assign_expr()?;
10595 return Ok((init, block, merge_expr_list(parts), Some(prog)));
10596 }
10597 }
10598 parts.push(self.parse_assign_expr()?);
10599 }
10600 Ok((init, block, merge_expr_list(parts), None))
10601 }
10602
10603 fn parse_cluster_block_then_list_optional_progress(
10605 &mut self,
10606 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
10607 let cluster = self.parse_assign_expr()?;
10608 let block = self.parse_block_or_bareword_block()?;
10609 self.eat(&Token::Comma);
10610 let line = self.peek_line();
10611 if let Token::Ident(ref kw) = self.peek().clone() {
10612 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10613 self.advance();
10614 self.expect(&Token::FatArrow)?;
10615 let prog = self.parse_assign_expr_stop_at_pipe()?;
10616 return Ok((
10617 cluster,
10618 block,
10619 Expr {
10620 kind: ExprKind::List(vec![]),
10621 line,
10622 },
10623 Some(prog),
10624 ));
10625 }
10626 }
10627 let empty_list_ok = matches!(
10628 self.peek(),
10629 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
10630 ) || (self.in_pipe_rhs() && matches!(self.peek(), Token::Comma));
10631 if empty_list_ok {
10632 return Ok((
10633 cluster,
10634 block,
10635 Expr {
10636 kind: ExprKind::List(vec![]),
10637 line,
10638 },
10639 None,
10640 ));
10641 }
10642 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
10643 loop {
10644 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
10645 break;
10646 }
10647 if matches!(
10648 self.peek(),
10649 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
10650 ) {
10651 break;
10652 }
10653 if self.peek_is_postfix_stmt_modifier_keyword() {
10654 break;
10655 }
10656 if let Token::Ident(ref kw) = self.peek().clone() {
10657 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10658 self.advance();
10659 self.expect(&Token::FatArrow)?;
10660 let prog = self.parse_assign_expr_stop_at_pipe()?;
10661 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
10662 }
10663 }
10664 parts.push(self.parse_assign_expr_stop_at_pipe()?);
10665 }
10666 Ok((cluster, block, merge_expr_list(parts), None))
10667 }
10668
10669 fn parse_block_then_list_optional_progress(
10678 &mut self,
10679 ) -> PerlResult<(Block, Expr, Option<Expr>)> {
10680 let block = self.parse_block_or_bareword_block()?;
10681 self.eat(&Token::Comma);
10682 let line = self.peek_line();
10683 if let Token::Ident(ref kw) = self.peek().clone() {
10684 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10685 self.advance();
10686 self.expect(&Token::FatArrow)?;
10687 let prog = self.parse_assign_expr_stop_at_pipe()?;
10688 return Ok((
10689 block,
10690 Expr {
10691 kind: ExprKind::List(vec![]),
10692 line,
10693 },
10694 Some(prog),
10695 ));
10696 }
10697 }
10698 let empty_list_ok = matches!(
10704 self.peek(),
10705 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
10706 ) || (self.in_pipe_rhs() && matches!(self.peek(), Token::Comma));
10707 if empty_list_ok {
10708 return Ok((
10709 block,
10710 Expr {
10711 kind: ExprKind::List(vec![]),
10712 line,
10713 },
10714 None,
10715 ));
10716 }
10717 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
10718 loop {
10719 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
10720 break;
10721 }
10722 if matches!(
10723 self.peek(),
10724 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
10725 ) {
10726 break;
10727 }
10728 if self.peek_is_postfix_stmt_modifier_keyword() {
10729 break;
10730 }
10731 if let Token::Ident(ref kw) = self.peek().clone() {
10732 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
10733 self.advance();
10734 self.expect(&Token::FatArrow)?;
10735 let prog = self.parse_assign_expr_stop_at_pipe()?;
10736 return Ok((block, merge_expr_list(parts), Some(prog)));
10737 }
10738 }
10739 parts.push(self.parse_assign_expr_stop_at_pipe()?);
10740 }
10741 Ok((block, merge_expr_list(parts), None))
10742 }
10743
10744 fn parse_fan_count_and_block(&mut self, line: usize) -> PerlResult<(Option<Box<Expr>>, Block)> {
10746 if matches!(self.peek(), Token::LBrace) {
10748 let block = self.parse_block()?;
10749 return Ok((None, block));
10750 }
10751 let saved = self.pos;
10752 let first = self.parse_postfix()?;
10754 if matches!(self.peek(), Token::LBrace) {
10755 let block = self.parse_block()?;
10757 Ok((Some(Box::new(first)), block))
10758 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
10759 || (matches!(self.peek(), Token::Comma)
10760 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
10761 {
10762 let block = self.bareword_to_no_arg_block(first);
10764 Ok((None, block))
10765 } else if matches!(first.kind, ExprKind::Integer(_)) {
10766 self.eat(&Token::Comma);
10768 let body = self.parse_fan_blockless_body(line)?;
10769 Ok((Some(Box::new(first)), body))
10770 } else {
10771 self.pos = saved;
10774 let body = self.parse_fan_blockless_body(line)?;
10775 Ok((None, body))
10776 }
10777 }
10778
10779 fn parse_fan_blockless_body(&mut self, line: usize) -> PerlResult<Block> {
10781 if matches!(self.peek(), Token::LBrace) {
10782 return self.parse_block();
10783 }
10784 if let Token::Ident(ref name) = self.peek().clone() {
10786 if matches!(
10787 self.peek_at(1),
10788 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
10789 ) {
10790 let name = name.clone();
10791 self.advance();
10792 let body = Expr {
10793 kind: ExprKind::FuncCall { name, args: vec![] },
10794 line,
10795 };
10796 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
10797 }
10798 }
10799 let expr = self.parse_assign_expr_stop_at_pipe()?;
10801 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
10802 }
10803
10804 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
10807 let line = expr.line;
10808 let body = match &expr.kind {
10809 ExprKind::Bareword(name) => Expr {
10810 kind: ExprKind::FuncCall {
10811 name: name.clone(),
10812 args: vec![],
10813 },
10814 line,
10815 },
10816 _ => expr,
10817 };
10818 vec![Statement::new(StmtKind::Expression(body), line)]
10819 }
10820
10821 fn parse_block_or_bareword_block(&mut self) -> PerlResult<Block> {
10830 if matches!(self.peek(), Token::LBrace) {
10831 return self.parse_block();
10832 }
10833 let line = self.peek_line();
10834 if let Token::Ident(ref name) = self.peek().clone() {
10837 if matches!(
10838 self.peek_at(1),
10839 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
10840 ) {
10841 let name = name.clone();
10842 self.advance();
10843 let body = Expr {
10844 kind: ExprKind::FuncCall {
10845 name,
10846 args: vec![Expr {
10847 kind: ExprKind::ScalarVar("_".to_string()),
10848 line,
10849 }],
10850 },
10851 line,
10852 };
10853 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
10854 }
10855 }
10856 let expr = self.parse_assign_expr_stop_at_pipe()?;
10858 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
10859 }
10860
10861 fn parse_block_or_bareword_block_no_args(&mut self) -> PerlResult<Block> {
10866 if matches!(self.peek(), Token::LBrace) {
10867 return self.parse_block();
10868 }
10869 let line = self.peek_line();
10870 if let Token::Ident(ref name) = self.peek().clone() {
10871 if matches!(
10872 self.peek_at(1),
10873 Token::Comma
10874 | Token::Semicolon
10875 | Token::RBrace
10876 | Token::Eof
10877 | Token::PipeForward
10878 | Token::Integer(_)
10879 ) {
10880 let name = name.clone();
10881 self.advance();
10882 let body = Expr {
10883 kind: ExprKind::FuncCall { name, args: vec![] },
10884 line,
10885 };
10886 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
10887 }
10888 }
10889 let expr = self.parse_postfix()?;
10890 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
10891 }
10892
10893 fn is_known_bareword(name: &str) -> bool {
10901 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
10902 }
10903
10904 fn is_try_builtin_name(name: &str) -> bool {
10910 crate::builtins::BUILTIN_ARMS
10911 .iter()
10912 .any(|arm| arm.contains(&name))
10913 }
10914
10915 fn is_perl5_core(name: &str) -> bool {
10920 matches!(
10921 name,
10922 "map" | "grep" | "sort" | "reverse" | "join" | "split"
10924 | "push" | "pop" | "shift" | "unshift" | "splice"
10925 | "pack" | "unpack"
10926 | "keys" | "values" | "each"
10928 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
10930 | "lc" | "uc" | "lcfirst" | "ucfirst"
10931 | "length" | "substr" | "index" | "rindex"
10932 | "sprintf" | "printf" | "print" | "say"
10933 | "pos" | "quotemeta" | "study"
10934 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
10936 | "exp" | "log" | "rand" | "srand"
10937 | "time" | "localtime" | "gmtime"
10939 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
10941 | "caller" | "delete" | "exists" | "bless" | "prototype"
10942 | "tie" | "untie" | "tied"
10943 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
10945 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
10946 | "format" | "formline" | "select" | "vec"
10947 | "sysopen" | "sysread" | "sysseek" | "syswrite"
10948 | "stat" | "lstat" | "rename" | "unlink" | "utime"
10950 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
10951 | "glob" | "opendir" | "readdir" | "closedir"
10952 | "link" | "readlink" | "symlink"
10953 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
10955 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
10957 | "semctl" | "semget" | "semop"
10958 | "shmctl" | "shmget" | "shmread" | "shmwrite"
10959 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
10961 | "fork" | "wait" | "waitpid" | "kill" | "alarm" | "sleep"
10962 | "chroot" | "times" | "umask" | "reset"
10963 | "getpgrp" | "setpgrp" | "getppid"
10964 | "getpriority" | "setpriority"
10965 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
10967 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
10968 | "getpeername" | "getsockname"
10969 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
10971 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
10972 | "getlogin"
10973 | "gethostbyname" | "gethostbyaddr" | "gethostent"
10974 | "getnetbyname" | "getnetent"
10975 | "getprotobyname" | "getprotoent"
10976 | "getservbyname" | "getservent"
10977 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
10978 | "endpwent" | "endgrent"
10979 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
10980 | "return" | "do" | "eval" | "require"
10982 | "my" | "our" | "local" | "use" | "no"
10983 | "sub" | "if" | "unless" | "while" | "until"
10984 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
10985 | "not" | "and" | "or"
10986 | "qw" | "qq" | "q"
10988 | "BEGIN" | "END"
10990 )
10991 }
10992
10993 fn stryke_extension_name(name: &str) -> Option<&str> {
10996 match name {
10997 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
10999 | "pgrep" | "pfor" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
11000 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
11001 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
11002 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
11003 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
11004 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "f" | "find_all" | "reduce" | "fold"
11006 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
11007 | "first" | "detect" | "find" | "compact" | "concat" | "chain" | "reject" | "flatten" | "set"
11008 | "min_by" | "max_by" | "sort_by" | "tally" | "find_index"
11009 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
11010 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
11011 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
11012 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
11013 | "zip_with" | "count_by" | "skip" | "first_or"
11014 | "input" | "lines" | "words" | "chars" | "digits" | "sentences" | "sents"
11016 | "paragraphs" | "paras" | "sections" | "sects"
11017 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
11018 | "trim" | "avg" | "stddev"
11019 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
11020 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
11021 | "frequencies" | "freq" | "interleave" | "ddump" | "stringify" | "str" | "top"
11022 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
11023 | "to_html" | "to_markdown" | "to_table" | "xopen"
11024 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste"
11025 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
11026 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
11027 | "to_hash" | "to_set"
11028 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
11029 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
11030 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
11031 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
11032 | "inc" | "dec" | "elapsed"
11033 | "files" | "filesf" | "fr" | "dirs" | "d" | "dr" | "sym_links"
11035 | "sockets" | "pipes" | "block_devices" | "char_devices"
11036 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
11037 | "copy" | "move" | "spurt" | "read_bytes" | "which"
11038 | "getcwd" | "touch" | "gethostname" | "uname"
11039 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
11041 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
11042 | "par_fetch" | "par_csv_read" | "par_pipeline"
11043 | "json_encode" | "json_decode" | "json_jq"
11044 | "http_request" | "serve" | "ssh"
11045 | "toml_encode" | "toml_decode"
11047 | "yaml_encode" | "yaml_decode"
11048 | "xml_encode" | "xml_decode"
11049 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
11051 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
11052 | "shake128" | "shake256"
11053 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
11054 | "uuid" | "crc32"
11055 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
11056 | "ripemd160" | "rmd160" | "md4"
11057 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
11058 | "murmur3" | "murmur3_32" | "murmur3_128"
11059 | "siphash" | "siphash_keyed"
11060 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
11061 | "poly1305" | "poly1305_mac"
11062 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
11063 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
11064 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
11065 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
11066 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
11067 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
11068 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
11069 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
11070 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
11071 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
11072 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
11073 | "secretbox" | "secretbox_seal" | "secretbox_open"
11074 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
11075 | "nacl_box_open" | "box_open"
11076 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
11077 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
11078 | "barcode_ean13" | "ean13" | "barcode_svg"
11079 | "argon2_hash" | "argon2" | "argon2_verify"
11080 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
11081 | "scrypt_hash" | "scrypt" | "scrypt_verify"
11082 | "pbkdf2" | "pbkdf2_derive"
11083 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
11084 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
11085 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
11086 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
11087 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
11088 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
11089 | "ecdsa_p256_verify" | "p256_verify"
11090 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
11091 | "ecdsa_p384_verify" | "p384_verify"
11092 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
11093 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
11094 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
11095 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
11096 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
11097 | "ed25519_verify" | "ed_verify"
11098 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
11099 | "base64_encode" | "base64_decode"
11100 | "hex_encode" | "hex_decode"
11101 | "url_encode" | "url_decode"
11102 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
11103 | "brotli" | "br" | "brotli_decode" | "ubr"
11104 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
11105 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
11106 | "lz4" | "lz4_decode" | "unlz4"
11107 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
11108 | "lzw" | "lzw_decode" | "unlzw"
11109 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
11110 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
11111 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
11112 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
11114 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
11115 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
11116 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
11117 | "gammaincc_reg" | "gamma_ur"
11118 | "datetime_utc" | "datetime_now_tz"
11120 | "datetime_format_tz" | "datetime_add_seconds"
11121 | "datetime_from_epoch"
11122 | "datetime_parse_rfc3339" | "datetime_parse_local"
11123 | "datetime_strftime"
11124 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
11126 | "log_info" | "log_warn" | "log_error"
11128 | "log_debug" | "log_trace" | "log_json" | "log_level"
11129 | "async" | "spawn" | "trace" | "timer" | "bench"
11131 | "eval_timeout" | "retry" | "rate_limit" | "every"
11132 | "gen" | "watch"
11133 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
11135 | "stdin"
11136 | "__stryke_rust_compile"
11138 | "p" | "rev"
11140 | "even" | "odd" | "zero" | "nonzero"
11142 | "positive" | "pos_n" | "negative" | "neg_n"
11143 | "sign" | "negate" | "double" | "triple" | "half"
11144 | "identity" | "id"
11145 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
11146 | "gcd" | "lcm" | "min2" | "max2"
11147 | "log2" | "log10" | "hypot"
11148 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
11149 | "pow2" | "abs_diff"
11150 | "factorial" | "fact" | "fibonacci" | "fib"
11151 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
11152 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
11153 | "median" | "mode_val" | "variance"
11154 | "is_empty" | "is_blank" | "is_numeric"
11156 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
11157 | "is_space" | "is_whitespace"
11158 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
11159 | "capitalize" | "cap" | "swap_case" | "repeat"
11160 | "title_case" | "title" | "squish"
11161 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
11162 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
11163 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
11164 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
11166 | "is_code" | "is_coderef" | "is_ref"
11167 | "is_undef" | "is_defined" | "is_def"
11168 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
11169 | "invert" | "merge_hash"
11171 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
11172 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
11174 | "riffle" | "intersperse" | "every_nth"
11176 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
11177 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
11179 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
11180 | "bits_count" | "popcount" | "leading_zeros" | "lz"
11181 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
11182 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
11184 | "shift_left" | "shl" | "shift_right" | "shr"
11185 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
11186 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
11188 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
11190 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
11191 | "yards_to_m" | "m_to_yards"
11192 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
11194 | "stone_to_kg" | "kg_to_stone"
11195 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
11197 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
11198 | "kb_to_mb" | "mb_to_gb"
11199 | "bits_to_bytes" | "bytes_to_bits"
11200 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
11202 | "seconds_to_hours" | "hours_to_seconds"
11203 | "seconds_to_days" | "days_to_seconds"
11204 | "minutes_to_hours" | "hours_to_minutes"
11205 | "hours_to_days" | "days_to_hours"
11206 | "is_leap_year" | "is_leap" | "days_in_month"
11208 | "month_name" | "month_short"
11209 | "weekday_name" | "weekday_short" | "quarter_of"
11210 | "now_ms" | "now_us" | "now_ns"
11212 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
11213 | "rgb_to_hex" | "hex_to_rgb"
11215 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
11216 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
11217 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
11218 | "strip_ansi"
11219 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
11220 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
11221 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
11222 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
11223 | "bright_magenta" | "bright_cyan" | "bright_white"
11224 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
11225 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
11226 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
11227 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
11228 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
11229 | "white_bold" | "bold_white"
11230 | "blink" | "rapid_blink" | "hidden" | "overline"
11231 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
11232 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
11233 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
11234 | "ipv4_to_int" | "int_to_ipv4"
11236 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
11237 | "path_ext" | "path_stem" | "path_parent" | "path_join" | "path_split"
11239 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
11240 | "const_fn" | "always_true" | "always_false"
11242 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
11243 | "count_eq" | "count_ne" | "all_eq"
11245 | "all_distinct" | "all_unique" | "has_duplicates"
11246 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
11247 | "quote" | "single_quote" | "unquote"
11249 | "extract_between" | "ellipsis"
11250 | "coin_flip" | "dice_roll"
11252 | "random_int" | "random_float" | "random_bool"
11253 | "random_choice" | "random_between"
11254 | "random_string" | "random_alpha" | "random_digit"
11255 | "os_name" | "os_arch" | "num_cpus"
11257 | "pid" | "ppid" | "uid" | "gid"
11258 | "username" | "home_dir" | "temp_dir"
11259 | "mem_total" | "mem_free" | "mem_used"
11260 | "swap_total" | "swap_free" | "swap_used"
11261 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
11262 | "load_avg" | "sys_uptime" | "page_size"
11263 | "os_version" | "os_family" | "endianness" | "pointer_width"
11264 | "proc_mem" | "rss"
11265 | "transpose" | "unzip"
11267 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
11268 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
11269 | "tan" | "asin" | "acos" | "atan"
11271 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
11272 | "sqr" | "cube_fn"
11273 | "mod_op" | "ceil_div" | "floor_div"
11274 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
11275 | "degrees" | "radians"
11276 | "min_abs" | "max_abs"
11277 | "saturate" | "sat01" | "wrap_around"
11278 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
11280 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
11281 | "first_word" | "last_word"
11282 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
11283 | "lowercase" | "uppercase"
11284 | "pascal_case" | "pc_case"
11285 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
11286 | "is_palindrome" | "hamming_distance"
11287 | "longest_common_prefix" | "lcp"
11288 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
11289 | "replace_first" | "replace_all_str"
11290 | "contains_any" | "contains_all"
11291 | "starts_with_any" | "ends_with_any"
11292 | "is_pair" | "is_triple"
11294 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
11295 | "is_empty_arr" | "is_empty_hash"
11296 | "is_subset" | "is_superset" | "is_permutation"
11297 | "first_eq" | "last_eq"
11299 | "index_of" | "last_index_of" | "positions_of"
11300 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
11301 | "distinct_count" | "longest" | "shortest"
11302 | "array_union" | "list_union"
11303 | "array_intersection" | "list_intersection"
11304 | "array_difference" | "list_difference"
11305 | "symmetric_diff" | "group_of_n" | "chunk_n"
11306 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
11307 | "pick_keys" | "pick" | "omit_keys" | "omit"
11309 | "map_keys_fn" | "map_values_fn"
11310 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
11311 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
11312 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
11314 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
11316 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
11318 | "argc" | "script_name"
11319 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
11320 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
11322 | "email_domain" | "email_local"
11324 | "url_host" | "url_path" | "url_query" | "url_scheme"
11325 | "file_size" | "fsize" | "file_mtime" | "mtime"
11327 | "file_atime" | "atime" | "file_ctime" | "ctime"
11328 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
11329 | "path_is_abs" | "path_is_rel"
11330 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
11332 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
11333 | "reverse_list" | "list_reverse"
11334 | "without" | "without_nth" | "take_last" | "drop_last"
11335 | "pairwise" | "zipmap"
11336 | "format_bytes" | "human_bytes"
11337 | "format_duration" | "human_duration"
11338 | "format_number" | "group_number"
11339 | "format_percent" | "pad_number"
11340 | "spaceship" | "cmp_num" | "cmp_str"
11341 | "compare_versions" | "version_cmp"
11342 | "hash_insert" | "hash_update" | "hash_delete"
11343 | "matches_regex" | "re_match"
11344 | "count_regex_matches" | "regex_extract"
11345 | "regex_split_str" | "regex_replace_str"
11346 | "shuffle_chars" | "random_char" | "nth_word"
11347 | "head_lines" | "tail_lines" | "count_substring"
11348 | "is_valid_hex" | "hex_upper" | "hex_lower"
11349 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
11350 | "us_to_ns" | "ns_to_us"
11351 | "liters_to_gallons" | "gallons_to_liters"
11352 | "liters_to_ml" | "ml_to_liters"
11353 | "cups_to_ml" | "ml_to_cups"
11354 | "newtons_to_lbf" | "lbf_to_newtons"
11355 | "joules_to_cal" | "cal_to_joules"
11356 | "watts_to_hp" | "hp_to_watts"
11357 | "pascals_to_psi" | "psi_to_pascals"
11358 | "bar_to_pascals" | "pascals_to_bar"
11359 | "match"
11361 | "fst" | "rest" | "rst" | "second" | "snd"
11363 | "last_clj" | "lastc" | "butlast" | "bl"
11364 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
11365 | "cons" | "conj"
11366 | "peek_clj" | "pkc" | "pop_clj" | "popc"
11367 | "some" | "not_any" | "not_every"
11368 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
11369 | "fnil" | "juxt"
11370 | "memoize" | "memo" | "curry" | "once"
11371 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
11372 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
11373 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
11374 | "reductions" | "rdcs"
11375 | "partition_by" | "pby" | "partition_all" | "pall"
11376 | "split_at" | "spat" | "split_with" | "spw"
11377 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
11378 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
11379 | "apply" | "appl"
11380 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
11382 | "zip_longest" | "zipl" | "combinations" | "comb" | "permutations" | "perm"
11383 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
11384 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
11385 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
11386 | "each_slice" | "eslice" | "each_cons" | "econs"
11387 | "one" | "none_match" | "nonem"
11388 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
11389 | "minmax" | "mmx" | "minmax_by" | "mmxb"
11390 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
11391 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
11392 | "sum_by" | "sumb" | "uniq_by" | "uqb"
11393 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
11394 | "step" | "upto" | "downto"
11395 | "find_last" | "fndl" | "find_last_index" | "fndli"
11397 | "at_index" | "ati" | "replace_at" | "repa"
11398 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
11399 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
11400 | "object_keys" | "okeys" | "object_values" | "ovals"
11401 | "object_entries" | "oents" | "object_from_entries" | "ofents"
11402 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
11404 | "nub" | "sort_on" | "srton"
11405 | "intersperse_val" | "isp" | "intercalate" | "ical"
11406 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
11407 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
11408 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
11410 | "partition_either" | "peith" | "try_fold" | "tfld"
11411 | "map_while" | "mapw" | "inspect" | "insp"
11412 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
11414 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
11416 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
11417 | "lines_from" | "lfrm" | "unlines" | "unlns"
11418 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
11419 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
11420 | "interpose" | "ipos" | "partition_n" | "partn"
11421 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
11422 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
11423 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
11425 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
11426 | "each_with_object" | "ewo" | "reduce_right" | "redr"
11427 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
11428 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
11429 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
11430 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
11431 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
11432 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
11433 | "union_list" | "unionl" | "intersect_list" | "intl"
11434 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
11435 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
11437 | "split_regex" | "splre" | "replace_regex" | "replre"
11438 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
11439 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
11440 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
11441 | "pluralize" | "plur" | "ordinalize" | "ordn"
11442 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
11443 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
11444 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
11445 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
11446 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
11448 | "dot_product" | "dotp" | "cross_product" | "crossp"
11449 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
11450 | "distance" | "dist" | "manhattan_distance" | "mdist"
11451 | "covariance" | "cov" | "correlation" | "corr"
11452 | "iqr" | "quantile" | "qntl" | "clamp_int" | "clpi"
11453 | "in_range" | "inrng" | "wrap_range" | "wrprng"
11454 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
11455 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
11457 | "diff_days" | "diffd" | "diff_hours" | "diffh"
11458 | "start_of_day" | "sod" | "end_of_day" | "eod"
11459 | "start_of_hour" | "soh" | "start_of_minute" | "som"
11460 | "urle" | "urld"
11462 | "html_encode" | "htmle" | "html_decode" | "htmld"
11463 | "adler32" | "adl32" | "fnv1a" | "djb2"
11464 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
11466 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
11467 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
11468 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
11469 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
11471 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
11472 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
11473 | "partition_point" | "ppt" | "lower_bound" | "lbound"
11474 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
11475 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
11477 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
11478 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
11479 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
11480 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
11481 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
11482 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
11483 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
11485 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
11486 | "connected_components_graph" | "ccgraph"
11487 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
11488 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
11490 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
11491 | "is_hostname_valid" | "ishost"
11492 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
11493 | "is_iso_datetime" | "isisodtm"
11494 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
11495 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
11497 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
11498 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
11499 | "find_all_indices" | "fndalli"
11500 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
11501 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
11502 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
11504 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
11505 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
11506 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
11507 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
11508 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
11509 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
11510 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
11511 | "longest_run" | "lrun" | "longest_increasing" | "linc"
11513 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
11514 | "majority_element" | "majority" | "kth_largest" | "kthl"
11515 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
11516 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
11517 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
11519 | "overlap_coefficient" | "overlapcoef"
11520 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
11521 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
11523 | "hamdist" | "jaro_similarity" | "jarosim"
11524 | "longest_common_substring" | "lcsub"
11525 | "longest_common_subsequence" | "lcseq"
11526 | "count_words" | "wcount" | "count_lines" | "lcount"
11527 | "count_chars" | "ccount" | "count_bytes" | "bcount"
11528 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
11530 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
11531 | "mobius" | "mob" | "is_squarefree" | "issqfr"
11532 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
11533 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
11534 | "day_of_year" | "doy" | "week_of_year" | "woy"
11536 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
11537 | "age_in_years" | "ageyrs"
11538 | "when_true" | "when_false" | "if_else" | "clamp_fn"
11541 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
11542 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
11543 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
11544 | "coalesce" | "default_to" | "fallback"
11545 | "apply_list" | "zip_apply" | "scan"
11546 | "keep_if" | "reject_if" | "group_consecutive"
11547 | "after_n" | "before_n" | "clamp_list" | "normalize_list" | "softmax"
11548
11549 | "matrix_multiply" | "mat_mul"
11553 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
11554
11555
11556
11557 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
11558 | "linspace" | "arange"
11559 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
11561 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
11562 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
11564 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
11565 | "stack_new" | "queue_new" | "lru_new"
11567 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
11568 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
11569 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
11571 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
11572 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
11573 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
11574 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
11575 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
11577 | "planck" | "speed_of_light" | "sqrt2"
11578 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
11580 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
11581 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
11582 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
11583 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
11584 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
11585 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
11587 | "cosine_similarity" | "cube_root" | "entropy" | "float_bits" | "fma"
11588 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
11589 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
11590 | "sigmoid" | "signum" | "square_root"
11591 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
11593 | "squares_seq" | "triangular_seq"
11594 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
11596 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
11597 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
11598 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
11599 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
11600 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
11601 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
11602 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
11603 | "xor_strings"
11604 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
11606 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
11607 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
11608 | "group_by_size" | "hash_filter_keys" | "hash_from_list" | "hash_map_values"
11609 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
11610 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
11611 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
11612 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
11613 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
11614 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
11615 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
11616 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
11617 | "wrap_index" | "digits_of"
11618 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
11620 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
11621 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
11622 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
11623 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
11624 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
11625 | "count_digits" | "count_letters" | "count_lower" | "count_match"
11627 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
11628 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
11629 | "truthy_count" | "undef_count"
11630 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
11632 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
11633 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
11634 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
11635 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
11636 | "range_exclusive" | "range_inclusive"
11637 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
11639 | "collatz_length" | "collatz_sequence" | "convolution" | "cross_entropy"
11640 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
11641 | "epsilon" | "euclidean_distance" | "euler_number" | "exponential_moving_average"
11642 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
11643 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
11644 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
11645 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
11646 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
11647 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
11648 | "tribonacci" | "weighted_mean" | "winsorize"
11649 | "chi_square_stat" | "describe" | "five_number_summary"
11651 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
11652 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
11653 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
11654 | "z_score" | "z_scores"
11655 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
11657 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
11658 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
11659 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
11660 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
11662 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
11663 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
11664 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
11665 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
11667 | "circle_from_three_points" | "convex_hull" | "ellipse_perimeter"
11668 | "frustum_volume" | "haversine_distance" | "line_intersection"
11669 | "point_in_polygon" | "polygon_perimeter" | "pyramid_volume"
11670 | "reflect_point" | "scale_point" | "sector_area"
11671 | "torus_surface" | "torus_volume" | "translate_point"
11672 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
11673 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
11675 | "gravitational_constant" | "phi" | "pi" | "planck_constant" | "proton_mass"
11676 | "sol" | "tau"
11677 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
11679 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
11681 | "bond_price" | "bond_yield" | "capm" | "continuous_compound"
11682 | "discounted_payback" | "duration" | "irr"
11683 | "max_drawdown" | "modified_duration" | "nper" | "num_periods" | "payback_period"
11684 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
11685 | "wacc" | "xirr"
11686 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
11688 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
11689 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
11690 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
11691 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
11692 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
11693 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
11695 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
11696 | "to_emoji_num"
11697 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
11699 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
11701 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
11703 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
11704 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
11705 | "rgb_to_hsl" | "rgb_to_hsv"
11706 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
11708 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
11709 | "matrix_transpose"
11710 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
11712 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
11713 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
11714 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
11715 | "zero_crossings"
11716 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
11718 | "downsample" | "energy" | "envelope" | "highpass_filter" | "idft"
11719 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
11720 | "power_spectrum" | "resample" | "spectral_centroid" | "spectrogram" | "upsample"
11721 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
11722 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
11724 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
11725 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
11726 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
11728 | "sierpinski" | "tower_of_hanoi" | "truth_table"
11729 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
11731 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
11733 | "geometric_series" | "stirling_approx"
11734 | "double_factorial" | "rising_factorial" | "falling_factorial"
11735 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
11736 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
11737 | "map_range"
11738 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
11740 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
11741 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
11742 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
11743 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
11744 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
11745 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
11746 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
11747 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
11748 | "projectile_range" | "projectile_max_height" | "projectile_time"
11749 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
11750 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
11751 | "lens_power" | "thin_lens" | "magnification_lens"
11752 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
11754 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
11755 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
11756 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
11758 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
11759 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
11760 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
11761 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
11762 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
11763 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
11764 | "matrix_solve" | "msolve" | "solve"
11766 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
11767 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
11768 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
11769 | "matrix_pinv" | "mpinv" | "pinv"
11770 | "matrix_cholesky" | "mchol" | "cholesky"
11771 | "matrix_det_general" | "mdetg" | "det"
11772 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
11774 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
11775 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
11776 | "confidence_interval" | "ci"
11777 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
11779 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
11780 | "t_pdf" | "tpdf" | "student_pdf"
11781 | "f_pdf" | "fpdf" | "fisher_pdf"
11782 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
11783 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
11784 | "pareto_pdf" | "paretopdf"
11785 | "lagrange_interp" | "lagrange" | "linterp"
11787 | "cubic_spline" | "cspline" | "spline"
11788 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
11789 | "trapz" | "trapezoid" | "simpson" | "simps"
11791 | "numerical_diff" | "numdiff" | "diff_array"
11792 | "cumtrapz" | "cumulative_trapz"
11793 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
11795 | "golden_section" | "golden" | "gss"
11796 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
11798 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
11800 | "floyd_warshall" | "floydwarshall" | "apsp"
11801 | "prim_mst" | "mst" | "prim"
11802 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
11804 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
11806 | "silu" | "swish" | "mish" | "softplus"
11807 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
11808 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
11810 | "lambert_w" | "lambertw" | "productlog"
11811 | "mod_exp" | "modexp" | "powmod"
11813 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
11814 | "miller_rabin" | "millerrabin" | "is_probable_prime"
11815 | "derangements" | "stirling2" | "stirling_second"
11817 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
11818 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
11820 | "bs_delta" | "bsdelta" | "option_delta"
11822 | "bs_gamma" | "bsgamma" | "option_gamma"
11823 | "bs_vega" | "bsvega" | "option_vega"
11824 | "bs_theta" | "bstheta" | "option_theta"
11825 | "bs_rho" | "bsrho" | "option_rho"
11826 | "bond_duration" | "mac_duration"
11827 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
11829 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
11831 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
11832 | "pnorm" | "qnorm" | "pbinom" | "dbinom" | "ppois"
11834 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
11835 | "rbind" | "cbind"
11837 | "row_sums" | "rowSums" | "col_sums" | "colSums"
11838 | "row_means" | "rowMeans" | "col_means" | "colMeans"
11839 | "outer_product" | "outer" | "crossprod" | "tcrossprod"
11840 | "nrow" | "ncol" | "prop_table" | "proptable"
11841 | "cummax" | "cummin" | "scale_vec" | "scale"
11843 | "which_fn" | "tabulate"
11844 | "duplicated" | "duped" | "rev_vec"
11845 | "seq_fn" | "rep_fn" | "rep"
11846 | "cut_bins" | "cut" | "find_interval" | "findInterval"
11847 | "ecdf_fn" | "ecdf" | "density_est" | "density"
11848 | "embed_ts" | "embed"
11849 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
11851 | "wilcox_test" | "wilcox" | "mann_whitney"
11852 | "prop_test" | "proptest" | "binom_test" | "binomtest"
11853 | "sapply" | "tapply" | "do_call" | "docall"
11855 | "kmeans" | "prcomp" | "pca"
11857 | "rnorm" | "runif" | "rexp" | "rbinom" | "rpois" | "rgeom"
11859 | "rgamma" | "rbeta" | "rchisq" | "rt" | "rf"
11860 | "rweibull" | "rlnorm" | "rcauchy"
11861 | "qunif" | "qexp" | "qweibull" | "qlnorm" | "qcauchy"
11863 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
11865 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
11867 | "lowess" | "loess" | "approx_fn" | "approx"
11869 | "lm_fit" | "lm"
11871 | "qgamma" | "qbeta" | "qchisq" | "qt_fn" | "qt" | "qf_fn" | "qf"
11873 | "qbinom" | "qpois"
11874 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
11876 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
11877 | "predict_lm" | "predict" | "confint_lm" | "confint"
11879 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
11881 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
11882 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
11883 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
11885 | "plot_svg" | "hist_svg" | "histogram_svg"
11886 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
11887 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
11888 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
11890 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
11891 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
11892 => Some(name),
11893 _ => None,
11894 }
11895 }
11896
11897 fn parse_block_or_bareword_cmp_block(&mut self) -> PerlResult<Block> {
11901 if matches!(self.peek(), Token::LBrace) {
11902 return self.parse_block();
11903 }
11904 let line = self.peek_line();
11905 if let Token::Ident(ref name) = self.peek().clone() {
11907 if matches!(
11908 self.peek_at(1),
11909 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
11910 ) {
11911 let name = name.clone();
11912 self.advance();
11913 let body = Expr {
11914 kind: ExprKind::FuncCall {
11915 name,
11916 args: vec![
11917 Expr {
11918 kind: ExprKind::ScalarVar("a".to_string()),
11919 line,
11920 },
11921 Expr {
11922 kind: ExprKind::ScalarVar("b".to_string()),
11923 line,
11924 },
11925 ],
11926 },
11927 line,
11928 };
11929 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
11930 }
11931 }
11932 let expr = self.parse_assign_expr_stop_at_pipe()?;
11934 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
11935 }
11936
11937 fn parse_fan_optional_progress(
11939 &mut self,
11940 which: &'static str,
11941 ) -> PerlResult<Option<Box<Expr>>> {
11942 let line = self.peek_line();
11943 if self.eat(&Token::Comma) {
11944 match self.peek() {
11945 Token::Ident(ref kw)
11946 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
11947 {
11948 self.advance();
11949 self.expect(&Token::FatArrow)?;
11950 return Ok(Some(Box::new(self.parse_assign_expr()?)));
11951 }
11952 _ => {
11953 return Err(self.syntax_err(
11954 format!("{which}: expected `progress => EXPR` after comma"),
11955 line,
11956 ));
11957 }
11958 }
11959 }
11960 if let Token::Ident(ref kw) = self.peek().clone() {
11961 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11962 self.advance();
11963 self.expect(&Token::FatArrow)?;
11964 return Ok(Some(Box::new(self.parse_assign_expr()?)));
11965 }
11966 }
11967 Ok(None)
11968 }
11969
11970 fn parse_assign_expr_list_optional_progress(&mut self) -> PerlResult<(Expr, Option<Expr>)> {
11977 if self.in_pipe_rhs()
11983 && matches!(
11984 self.peek(),
11985 Token::Semicolon
11986 | Token::RBrace
11987 | Token::RParen
11988 | Token::Eof
11989 | Token::PipeForward
11990 | Token::Comma
11991 )
11992 {
11993 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
11994 }
11995 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
11996 loop {
11997 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11998 break;
11999 }
12000 if matches!(
12001 self.peek(),
12002 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12003 ) {
12004 break;
12005 }
12006 if self.peek_is_postfix_stmt_modifier_keyword() {
12007 break;
12008 }
12009 if let Token::Ident(ref kw) = self.peek().clone() {
12010 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12011 self.advance();
12012 self.expect(&Token::FatArrow)?;
12013 let prog = self.parse_assign_expr_stop_at_pipe()?;
12014 return Ok((merge_expr_list(parts), Some(prog)));
12015 }
12016 }
12017 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12018 }
12019 Ok((merge_expr_list(parts), None))
12020 }
12021
12022 fn parse_one_arg(&mut self) -> PerlResult<Expr> {
12023 if matches!(self.peek(), Token::LParen) {
12024 self.advance();
12025 let expr = self.parse_expression()?;
12026 self.expect(&Token::RParen)?;
12027 Ok(expr)
12028 } else {
12029 self.parse_assign_expr_stop_at_pipe()
12030 }
12031 }
12032
12033 fn parse_one_arg_or_default(&mut self) -> PerlResult<Expr> {
12034 if matches!(
12041 self.peek(),
12042 Token::Semicolon
12044 | Token::RBrace
12045 | Token::RParen
12046 | Token::RBracket
12047 | Token::Eof
12048 | Token::Comma
12049 | Token::FatArrow
12050 | Token::PipeForward
12051 | Token::Question
12053 | Token::Colon
12054 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
12056 | Token::NumLe | Token::NumGe | Token::Spaceship
12057 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
12058 | Token::StrLe | Token::StrGe | Token::StrCmp
12059 | Token::LogAnd | Token::LogOr | Token::LogNot
12061 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
12062 | Token::DefinedOr
12063 | Token::Range | Token::RangeExclusive
12065 | Token::Assign | Token::PlusAssign | Token::MinusAssign
12067 | Token::MulAssign | Token::DivAssign | Token::ModAssign
12068 | Token::PowAssign | Token::DotAssign | Token::AndAssign
12069 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
12070 | Token::ShiftLeftAssign | Token::ShiftRightAssign
12071 | Token::BitAndAssign | Token::BitOrAssign
12072 ) {
12073 return Ok(Expr {
12074 kind: ExprKind::ScalarVar("_".into()),
12075 line: self.peek_line(),
12076 });
12077 }
12078 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
12082 let line = self.peek_line();
12083 self.advance(); self.advance(); return Ok(Expr {
12086 kind: ExprKind::ScalarVar("_".into()),
12087 line,
12088 });
12089 }
12090 self.parse_one_arg()
12091 }
12092
12093 fn parse_one_arg_or_argv(&mut self) -> PerlResult<Expr> {
12095 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
12097 self.advance();
12098 if matches!(self.peek(), Token::RParen) {
12099 self.advance();
12100 return Ok(Expr {
12101 kind: ExprKind::ArrayVar("_".into()),
12102 line: self.peek_line(),
12103 });
12104 }
12105 let expr = self.parse_expression()?;
12106 self.expect(&Token::RParen)?;
12107 return Ok(expr);
12108 }
12109 if matches!(
12111 self.peek(),
12112 Token::Semicolon
12113 | Token::RBrace
12114 | Token::RParen
12115 | Token::Eof
12116 | Token::Comma
12117 | Token::PipeForward
12118 ) || self.peek_line() > line
12119 {
12120 Ok(Expr {
12121 kind: ExprKind::ArrayVar("_".into()),
12122 line,
12123 })
12124 } else {
12125 self.parse_assign_expr()
12126 }
12127 }
12128
12129 fn parse_builtin_args(&mut self) -> PerlResult<Vec<Expr>> {
12130 if matches!(self.peek(), Token::LParen) {
12131 self.advance();
12132 let args = self.parse_arg_list()?;
12133 self.expect(&Token::RParen)?;
12134 Ok(args)
12135 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
12136 Ok(vec![])
12139 } else {
12140 self.parse_list_until_terminator()
12141 }
12142 }
12143
12144 #[inline]
12148 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
12149 if matches!(self.peek(), Token::FatArrow) {
12150 Some(Expr {
12151 kind: ExprKind::String(name.to_string()),
12152 line,
12153 })
12154 } else {
12155 None
12156 }
12157 }
12158
12159 fn parse_hash_subscript_key(&mut self) -> PerlResult<Expr> {
12164 let line = self.peek_line();
12165 if let Token::Ident(ref k) = self.peek().clone() {
12166 if matches!(self.peek_at(1), Token::RBrace) {
12167 let s = k.clone();
12168 self.advance();
12169 return Ok(Expr {
12170 kind: ExprKind::String(s),
12171 line,
12172 });
12173 }
12174 }
12175 self.parse_expression()
12176 }
12177
12178 #[inline]
12180 fn peek_is_glob_par_progress_kw(&self) -> bool {
12181 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
12182 && matches!(self.peek_at(1), Token::FatArrow)
12183 }
12184
12185 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> PerlResult<Vec<Expr>> {
12187 let mut args = Vec::new();
12188 loop {
12189 if matches!(self.peek(), Token::RParen | Token::Eof) {
12190 break;
12191 }
12192 if self.peek_is_glob_par_progress_kw() {
12193 break;
12194 }
12195 args.push(self.parse_assign_expr()?);
12196 match self.peek() {
12197 Token::RParen => break,
12198 Token::Comma => {
12199 self.advance();
12200 if matches!(self.peek(), Token::RParen) {
12201 break;
12202 }
12203 if self.peek_is_glob_par_progress_kw() {
12204 break;
12205 }
12206 }
12207 _ => {
12208 return Err(self.syntax_err(
12209 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
12210 self.peek_line(),
12211 ));
12212 }
12213 }
12214 }
12215 Ok(args)
12216 }
12217
12218 fn parse_pattern_list_glob_par_bare(&mut self) -> PerlResult<Vec<Expr>> {
12220 let mut args = Vec::new();
12221 loop {
12222 if matches!(
12223 self.peek(),
12224 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12225 ) {
12226 break;
12227 }
12228 if self.peek_is_postfix_stmt_modifier_keyword() {
12229 break;
12230 }
12231 if self.peek_is_glob_par_progress_kw() {
12232 break;
12233 }
12234 args.push(self.parse_assign_expr()?);
12235 if !self.eat(&Token::Comma) {
12236 break;
12237 }
12238 if self.peek_is_glob_par_progress_kw() {
12239 break;
12240 }
12241 }
12242 Ok(args)
12243 }
12244
12245 fn parse_glob_par_or_par_sed_args(&mut self) -> PerlResult<(Vec<Expr>, Option<Box<Expr>>)> {
12247 if matches!(self.peek(), Token::LParen) {
12248 self.advance();
12249 let args = self.parse_pattern_list_until_rparen_or_progress()?;
12250 let progress = if self.peek_is_glob_par_progress_kw() {
12251 self.advance();
12252 self.expect(&Token::FatArrow)?;
12253 Some(Box::new(self.parse_assign_expr()?))
12254 } else {
12255 None
12256 };
12257 self.expect(&Token::RParen)?;
12258 Ok((args, progress))
12259 } else {
12260 let args = self.parse_pattern_list_glob_par_bare()?;
12261 let progress = if self.peek_is_glob_par_progress_kw() {
12263 self.advance();
12264 self.expect(&Token::FatArrow)?;
12265 Some(Box::new(self.parse_assign_expr()?))
12266 } else {
12267 None
12268 };
12269 Ok((args, progress))
12270 }
12271 }
12272
12273 pub(crate) fn parse_arg_list(&mut self) -> PerlResult<Vec<Expr>> {
12274 let mut args = Vec::new();
12275 let saved_no_pf = self.no_pipe_forward_depth;
12279 self.no_pipe_forward_depth = 0;
12280 while !matches!(
12281 self.peek(),
12282 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
12283 ) {
12284 let arg = match self.parse_assign_expr() {
12285 Ok(e) => e,
12286 Err(err) => {
12287 self.no_pipe_forward_depth = saved_no_pf;
12288 return Err(err);
12289 }
12290 };
12291 args.push(arg);
12292 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12293 break;
12294 }
12295 }
12296 self.no_pipe_forward_depth = saved_no_pf;
12297 Ok(args)
12298 }
12299
12300 fn parse_method_arg_list_no_paren(&mut self) -> PerlResult<Vec<Expr>> {
12304 let mut args = Vec::new();
12305 loop {
12306 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
12309 break;
12310 }
12311 if matches!(
12312 self.peek(),
12313 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12314 ) {
12315 break;
12316 }
12317 if let Token::Ident(ref kw) = self.peek().clone() {
12318 if matches!(
12319 kw.as_str(),
12320 "if" | "unless" | "while" | "until" | "for" | "foreach"
12321 ) {
12322 break;
12323 }
12324 }
12325 if args.is_empty()
12328 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
12329 {
12330 break;
12331 }
12332 args.push(self.parse_assign_expr()?);
12333 if !self.eat(&Token::Comma) {
12334 break;
12335 }
12336 }
12337 Ok(args)
12338 }
12339
12340 fn peek_method_arg_infix_terminator(&self) -> bool {
12343 matches!(
12344 self.peek(),
12345 Token::Plus
12346 | Token::Minus
12347 | Token::Star
12348 | Token::Slash
12349 | Token::Percent
12350 | Token::Power
12351 | Token::Dot
12352 | Token::X
12353 | Token::NumEq
12354 | Token::NumNe
12355 | Token::NumLt
12356 | Token::NumGt
12357 | Token::NumLe
12358 | Token::NumGe
12359 | Token::Spaceship
12360 | Token::StrEq
12361 | Token::StrNe
12362 | Token::StrLt
12363 | Token::StrGt
12364 | Token::StrLe
12365 | Token::StrGe
12366 | Token::StrCmp
12367 | Token::LogAnd
12368 | Token::LogOr
12369 | Token::LogAndWord
12370 | Token::LogOrWord
12371 | Token::DefinedOr
12372 | Token::BitAnd
12373 | Token::BitOr
12374 | Token::BitXor
12375 | Token::ShiftLeft
12376 | Token::ShiftRight
12377 | Token::Range
12378 | Token::RangeExclusive
12379 | Token::BindMatch
12380 | Token::BindNotMatch
12381 | Token::Arrow
12382 | Token::Question
12384 | Token::Colon
12385 )
12386 }
12387
12388 fn parse_list_until_terminator(&mut self) -> PerlResult<Vec<Expr>> {
12389 let mut args = Vec::new();
12390 let call_line = self.prev_line();
12395 loop {
12396 if matches!(
12397 self.peek(),
12398 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12399 ) {
12400 break;
12401 }
12402 if let Token::Ident(ref kw) = self.peek().clone() {
12404 if matches!(
12405 kw.as_str(),
12406 "if" | "unless" | "while" | "until" | "for" | "foreach"
12407 ) {
12408 break;
12409 }
12410 }
12411 if args.is_empty() && self.peek_line() > call_line {
12418 break;
12419 }
12420 args.push(self.parse_assign_expr_stop_at_pipe()?);
12423 if !self.eat(&Token::Comma) {
12424 break;
12425 }
12426 }
12427 Ok(args)
12428 }
12429
12430 fn try_parse_hash_ref(&mut self) -> PerlResult<Vec<(Expr, Expr)>> {
12431 let mut pairs = Vec::new();
12432 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
12433 let line = self.peek_line();
12436 let key = if let Token::Ident(ref name) = self.peek().clone() {
12437 if matches!(self.peek_at(1), Token::FatArrow) {
12438 self.advance();
12439 Expr {
12440 kind: ExprKind::String(name.clone()),
12441 line,
12442 }
12443 } else {
12444 self.parse_assign_expr()?
12445 }
12446 } else {
12447 self.parse_assign_expr()?
12448 };
12449 if matches!(self.peek(), Token::RBrace | Token::Comma)
12453 && matches!(
12454 key.kind,
12455 ExprKind::HashVar(_)
12456 | ExprKind::Deref {
12457 kind: Sigil::Hash,
12458 ..
12459 }
12460 )
12461 {
12462 let sentinel_key = Expr {
12466 kind: ExprKind::String("__HASH_SPREAD__".into()),
12467 line,
12468 };
12469 pairs.push((sentinel_key, key));
12470 self.eat(&Token::Comma);
12471 continue;
12472 }
12473 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
12475 let val = self.parse_assign_expr()?;
12476 pairs.push((key, val));
12477 self.eat(&Token::Comma);
12478 } else {
12479 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
12480 }
12481 }
12482 self.expect(&Token::RBrace)?;
12483 Ok(pairs)
12484 }
12485
12486 fn parse_hashref_pairs_until(&mut self, term: &Token) -> PerlResult<Vec<(Expr, Expr)>> {
12491 let mut pairs = Vec::new();
12492 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
12493 && !matches!(self.peek(), Token::Eof)
12494 {
12495 let line = self.peek_line();
12496 let key = if let Token::Ident(ref name) = self.peek().clone() {
12497 if matches!(self.peek_at(1), Token::FatArrow) {
12498 self.advance();
12499 Expr {
12500 kind: ExprKind::String(name.clone()),
12501 line,
12502 }
12503 } else {
12504 self.parse_assign_expr()?
12505 }
12506 } else {
12507 self.parse_assign_expr()?
12508 };
12509 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
12510 let val = self.parse_assign_expr()?;
12511 pairs.push((key, val));
12512 self.eat(&Token::Comma);
12513 } else {
12514 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
12515 }
12516 }
12517 Ok(pairs)
12518 }
12519
12520 fn interp_chain_subscripts(
12526 &self,
12527 chars: &[char],
12528 i: &mut usize,
12529 mut base: Expr,
12530 line: usize,
12531 ) -> Expr {
12532 loop {
12533 let (after, requires_subscript) =
12535 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
12536 (*i + 2, true)
12537 } else {
12538 (*i, false)
12539 };
12540 if after >= chars.len() {
12541 break;
12542 }
12543 match chars[after] {
12544 '[' => {
12545 *i = after + 1;
12546 let mut idx_str = String::new();
12547 while *i < chars.len() && chars[*i] != ']' {
12548 idx_str.push(chars[*i]);
12549 *i += 1;
12550 }
12551 if *i < chars.len() {
12552 *i += 1;
12553 }
12554 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
12555 Expr {
12556 kind: ExprKind::ScalarVar(rest.to_string()),
12557 line,
12558 }
12559 } else if let Ok(n) = idx_str.parse::<i64>() {
12560 Expr {
12561 kind: ExprKind::Integer(n),
12562 line,
12563 }
12564 } else {
12565 Expr {
12566 kind: ExprKind::String(idx_str),
12567 line,
12568 }
12569 };
12570 base = Expr {
12571 kind: ExprKind::ArrowDeref {
12572 expr: Box::new(base),
12573 index: Box::new(idx_expr),
12574 kind: DerefKind::Array,
12575 },
12576 line,
12577 };
12578 }
12579 '{' => {
12580 *i = after + 1;
12581 let mut key = String::new();
12582 let mut depth = 1usize;
12583 while *i < chars.len() && depth > 0 {
12584 if chars[*i] == '{' {
12585 depth += 1;
12586 } else if chars[*i] == '}' {
12587 depth -= 1;
12588 if depth == 0 {
12589 break;
12590 }
12591 }
12592 key.push(chars[*i]);
12593 *i += 1;
12594 }
12595 if *i < chars.len() {
12596 *i += 1;
12597 }
12598 let key_expr = if let Some(rest) = key.strip_prefix('$') {
12599 Expr {
12600 kind: ExprKind::ScalarVar(rest.to_string()),
12601 line,
12602 }
12603 } else {
12604 Expr {
12605 kind: ExprKind::String(key),
12606 line,
12607 }
12608 };
12609 base = Expr {
12610 kind: ExprKind::ArrowDeref {
12611 expr: Box::new(base),
12612 index: Box::new(key_expr),
12613 kind: DerefKind::Hash,
12614 },
12615 line,
12616 };
12617 }
12618 _ => {
12619 if requires_subscript {
12620 }
12622 break;
12623 }
12624 }
12625 }
12626 base
12627 }
12628
12629 fn parse_interpolated_string(&self, s: &str, line: usize) -> PerlResult<Expr> {
12630 let mut parts = Vec::new();
12632 let mut literal = String::new();
12633 let chars: Vec<char> = s.chars().collect();
12634 let mut i = 0;
12635
12636 'istr: while i < chars.len() {
12637 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
12638 literal.push('$');
12639 i += 1;
12640 continue;
12641 }
12642 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
12644 literal.push('\\');
12645 i += 1;
12646 }
12648 if chars[i] == '$' && i + 1 < chars.len() {
12649 if !literal.is_empty() {
12650 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
12651 }
12652 i += 1; while i < chars.len() && chars[i].is_whitespace() {
12655 i += 1;
12656 }
12657 if i >= chars.len() {
12658 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
12659 }
12660 if chars[i] == '#' {
12662 i += 1;
12663 let mut sname = String::from("#");
12664 while i < chars.len()
12665 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
12666 {
12667 sname.push(chars[i]);
12668 i += 1;
12669 }
12670 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
12671 sname.push_str("::");
12672 i += 2;
12673 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
12674 sname.push(chars[i]);
12675 i += 1;
12676 }
12677 }
12678 parts.push(StringPart::ScalarVar(sname));
12679 continue;
12680 }
12681 if chars[i] == '$' {
12685 let next_c = chars.get(i + 1).copied();
12686 let is_pid = match next_c {
12687 None => true,
12688 Some(c)
12689 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
12690 {
12691 true
12692 }
12693 _ => false,
12694 };
12695 if is_pid {
12696 parts.push(StringPart::ScalarVar("$$".to_string()));
12697 i += 1; continue;
12699 }
12700 i += 1; }
12702 if chars[i] == '{' {
12703 i += 1;
12709 let mut inner = String::new();
12710 let mut depth = 1usize;
12711 while i < chars.len() && depth > 0 {
12712 match chars[i] {
12713 '{' => depth += 1,
12714 '}' => {
12715 depth -= 1;
12716 if depth == 0 {
12717 break;
12718 }
12719 }
12720 _ => {}
12721 }
12722 inner.push(chars[i]);
12723 i += 1;
12724 }
12725 if i < chars.len() {
12726 i += 1; }
12728
12729 let trimmed = inner.trim();
12733 let is_expr = trimmed.starts_with('$')
12734 || trimmed.starts_with('\\')
12735 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|']);
12738 let mut base: Expr = if is_expr {
12739 match parse_expression_from_str(trimmed, "<interp>") {
12743 Ok(e) => Expr {
12744 kind: ExprKind::Deref {
12745 expr: Box::new(e),
12746 kind: Sigil::Scalar,
12747 },
12748 line,
12749 },
12750 Err(_) => Expr {
12751 kind: ExprKind::ScalarVar(inner.clone()),
12752 line,
12753 },
12754 }
12755 } else {
12756 Expr {
12758 kind: ExprKind::ScalarVar(inner),
12759 line,
12760 }
12761 };
12762
12763 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
12767 parts.push(StringPart::Expr(base));
12768 } else if chars[i] == '^' {
12769 let mut name = String::from("^");
12771 i += 1;
12772 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
12773 name.push(chars[i]);
12774 i += 1;
12775 }
12776 if i < chars.len() && chars[i] == '{' {
12777 i += 1; let mut key = String::new();
12779 let mut depth = 1;
12780 while i < chars.len() && depth > 0 {
12781 if chars[i] == '{' {
12782 depth += 1;
12783 } else if chars[i] == '}' {
12784 depth -= 1;
12785 if depth == 0 {
12786 break;
12787 }
12788 }
12789 key.push(chars[i]);
12790 i += 1;
12791 }
12792 if i < chars.len() {
12793 i += 1;
12794 }
12795 let key_expr = if let Some(rest) = key.strip_prefix('$') {
12796 Expr {
12797 kind: ExprKind::ScalarVar(rest.to_string()),
12798 line,
12799 }
12800 } else {
12801 Expr {
12802 kind: ExprKind::String(key),
12803 line,
12804 }
12805 };
12806 parts.push(StringPart::Expr(Expr {
12807 kind: ExprKind::HashElement {
12808 hash: name,
12809 key: Box::new(key_expr),
12810 },
12811 line,
12812 }));
12813 } else if i < chars.len() && chars[i] == '[' {
12814 i += 1;
12815 let mut idx_str = String::new();
12816 while i < chars.len() && chars[i] != ']' {
12817 idx_str.push(chars[i]);
12818 i += 1;
12819 }
12820 if i < chars.len() {
12821 i += 1;
12822 }
12823 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
12824 Expr {
12825 kind: ExprKind::ScalarVar(rest.to_string()),
12826 line,
12827 }
12828 } else if let Ok(n) = idx_str.parse::<i64>() {
12829 Expr {
12830 kind: ExprKind::Integer(n),
12831 line,
12832 }
12833 } else {
12834 Expr {
12835 kind: ExprKind::String(idx_str),
12836 line,
12837 }
12838 };
12839 parts.push(StringPart::Expr(Expr {
12840 kind: ExprKind::ArrayElement {
12841 array: name,
12842 index: Box::new(idx_expr),
12843 },
12844 line,
12845 }));
12846 } else {
12847 parts.push(StringPart::ScalarVar(name));
12848 }
12849 } else if chars[i].is_alphabetic() || chars[i] == '_' {
12850 let mut name = String::new();
12851 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
12852 name.push(chars[i]);
12853 i += 1;
12854 }
12855 if name == "_" {
12857 while i < chars.len() && chars[i] == '<' {
12858 name.push('<');
12859 i += 1;
12860 }
12861 }
12862 let mut base = if i < chars.len() && chars[i] == '{' {
12867 i += 1; let mut key = String::new();
12870 let mut depth = 1;
12871 while i < chars.len() && depth > 0 {
12872 if chars[i] == '{' {
12873 depth += 1;
12874 } else if chars[i] == '}' {
12875 depth -= 1;
12876 if depth == 0 {
12877 break;
12878 }
12879 }
12880 key.push(chars[i]);
12881 i += 1;
12882 }
12883 if i < chars.len() {
12884 i += 1;
12885 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
12887 Expr {
12888 kind: ExprKind::ScalarVar(rest.to_string()),
12889 line,
12890 }
12891 } else {
12892 Expr {
12893 kind: ExprKind::String(key),
12894 line,
12895 }
12896 };
12897 Expr {
12898 kind: ExprKind::HashElement {
12899 hash: name,
12900 key: Box::new(key_expr),
12901 },
12902 line,
12903 }
12904 } else if i < chars.len() && chars[i] == '[' {
12905 i += 1;
12907 let mut idx_str = String::new();
12908 while i < chars.len() && chars[i] != ']' {
12909 idx_str.push(chars[i]);
12910 i += 1;
12911 }
12912 if i < chars.len() {
12913 i += 1;
12914 }
12915 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
12916 Expr {
12917 kind: ExprKind::ScalarVar(rest.to_string()),
12918 line,
12919 }
12920 } else if let Ok(n) = idx_str.parse::<i64>() {
12921 Expr {
12922 kind: ExprKind::Integer(n),
12923 line,
12924 }
12925 } else {
12926 Expr {
12927 kind: ExprKind::String(idx_str),
12928 line,
12929 }
12930 };
12931 Expr {
12932 kind: ExprKind::ArrayElement {
12933 array: name,
12934 index: Box::new(idx_expr),
12935 },
12936 line,
12937 }
12938 } else {
12939 Expr {
12941 kind: ExprKind::ScalarVar(name),
12942 line,
12943 }
12944 };
12945
12946 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
12950 parts.push(StringPart::Expr(base));
12951 } else if chars[i].is_ascii_digit() {
12952 if chars[i] == '0' {
12954 i += 1;
12955 if i < chars.len() && chars[i].is_ascii_digit() {
12956 return Err(self.syntax_err(
12957 "Numeric variables with more than one digit may not start with '0'",
12958 line,
12959 ));
12960 }
12961 parts.push(StringPart::ScalarVar("0".into()));
12962 } else {
12963 let start = i;
12964 while i < chars.len() && chars[i].is_ascii_digit() {
12965 i += 1;
12966 }
12967 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
12968 }
12969 } else {
12970 let c = chars[i];
12971 let probe = c.to_string();
12972 if Interpreter::is_special_scalar_name_for_get(&probe)
12973 || matches!(c, '\'' | '`')
12974 {
12975 i += 1;
12976 if i < chars.len() && chars[i] == '{' {
12978 i += 1; let mut key = String::new();
12980 let mut depth = 1;
12981 while i < chars.len() && depth > 0 {
12982 if chars[i] == '{' {
12983 depth += 1;
12984 } else if chars[i] == '}' {
12985 depth -= 1;
12986 if depth == 0 {
12987 break;
12988 }
12989 }
12990 key.push(chars[i]);
12991 i += 1;
12992 }
12993 if i < chars.len() {
12994 i += 1;
12995 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
12997 Expr {
12998 kind: ExprKind::ScalarVar(rest.to_string()),
12999 line,
13000 }
13001 } else {
13002 Expr {
13003 kind: ExprKind::String(key),
13004 line,
13005 }
13006 };
13007 let mut base = Expr {
13008 kind: ExprKind::HashElement {
13009 hash: probe,
13010 key: Box::new(key_expr),
13011 },
13012 line,
13013 };
13014 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
13015 parts.push(StringPart::Expr(base));
13016 } else {
13017 let mut base = Expr {
13019 kind: ExprKind::ScalarVar(probe),
13020 line,
13021 };
13022 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
13023 if matches!(base.kind, ExprKind::ScalarVar(_)) {
13024 if let ExprKind::ScalarVar(name) = base.kind {
13026 parts.push(StringPart::ScalarVar(name));
13027 }
13028 } else {
13029 parts.push(StringPart::Expr(base));
13030 }
13031 }
13032 } else {
13033 literal.push('$');
13034 literal.push(c);
13035 i += 1;
13036 }
13037 }
13038 } else if chars[i] == '@' && i + 1 < chars.len() {
13039 let next = chars[i + 1];
13040 if next == '$' {
13042 if !literal.is_empty() {
13043 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
13044 }
13045 i += 1; debug_assert_eq!(chars[i], '$');
13047 i += 1; while i < chars.len() && chars[i].is_whitespace() {
13049 i += 1;
13050 }
13051 if i >= chars.len() {
13052 return Err(self.syntax_err(
13053 "Expected variable or block after `@$` in double-quoted string",
13054 line,
13055 ));
13056 }
13057 let inner_expr = if chars[i] == '{' {
13058 i += 1;
13059 let start = i;
13060 let mut depth = 1usize;
13061 while i < chars.len() && depth > 0 {
13062 match chars[i] {
13063 '{' => depth += 1,
13064 '}' => {
13065 depth -= 1;
13066 if depth == 0 {
13067 break;
13068 }
13069 }
13070 _ => {}
13071 }
13072 i += 1;
13073 }
13074 if depth != 0 {
13075 return Err(self.syntax_err(
13076 "Unterminated `${ ... }` after `@` in double-quoted string",
13077 line,
13078 ));
13079 }
13080 let inner: String = chars[start..i].iter().collect();
13081 i += 1; parse_expression_from_str(inner.trim(), "-e")?
13083 } else {
13084 let mut name = String::new();
13085 if chars[i] == '^' {
13086 name.push('^');
13087 i += 1;
13088 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
13089 {
13090 name.push(chars[i]);
13091 i += 1;
13092 }
13093 } else {
13094 while i < chars.len()
13095 && (chars[i].is_alphanumeric()
13096 || chars[i] == '_'
13097 || chars[i] == ':')
13098 {
13099 name.push(chars[i]);
13100 i += 1;
13101 }
13102 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
13103 name.push_str("::");
13104 i += 2;
13105 while i < chars.len()
13106 && (chars[i].is_alphanumeric() || chars[i] == '_')
13107 {
13108 name.push(chars[i]);
13109 i += 1;
13110 }
13111 }
13112 }
13113 if name.is_empty() {
13114 return Err(self.syntax_err(
13115 "Expected identifier after `@$` in double-quoted string",
13116 line,
13117 ));
13118 }
13119 Expr {
13120 kind: ExprKind::ScalarVar(name),
13121 line,
13122 }
13123 };
13124 parts.push(StringPart::Expr(Expr {
13125 kind: ExprKind::Deref {
13126 expr: Box::new(inner_expr),
13127 kind: Sigil::Array,
13128 },
13129 line,
13130 }));
13131 continue 'istr;
13132 }
13133 if next == '{' {
13134 if !literal.is_empty() {
13135 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
13136 }
13137 i += 2; let start = i;
13139 let mut depth = 1usize;
13140 while i < chars.len() && depth > 0 {
13141 match chars[i] {
13142 '{' => depth += 1,
13143 '}' => {
13144 depth -= 1;
13145 if depth == 0 {
13146 break;
13147 }
13148 }
13149 _ => {}
13150 }
13151 i += 1;
13152 }
13153 if depth != 0 {
13154 return Err(
13155 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
13156 );
13157 }
13158 let inner: String = chars[start..i].iter().collect();
13159 i += 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
13161 parts.push(StringPart::Expr(Expr {
13162 kind: ExprKind::Deref {
13163 expr: Box::new(inner_expr),
13164 kind: Sigil::Array,
13165 },
13166 line,
13167 }));
13168 continue 'istr;
13169 }
13170 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
13171 literal.push(chars[i]);
13172 i += 1;
13173 } else {
13174 if !literal.is_empty() {
13175 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
13176 }
13177 i += 1;
13178 let mut name = String::new();
13179 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
13180 name.push(chars[i]);
13181 i += 1;
13182 } else {
13183 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
13184 name.push(chars[i]);
13185 i += 1;
13186 }
13187 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
13188 name.push_str("::");
13189 i += 2;
13190 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
13191 {
13192 name.push(chars[i]);
13193 i += 1;
13194 }
13195 }
13196 }
13197 if i < chars.len() && chars[i] == '[' {
13198 i += 1;
13199 let start_inner = i;
13200 let mut depth = 1usize;
13201 while i < chars.len() && depth > 0 {
13202 match chars[i] {
13203 '[' => depth += 1,
13204 ']' => depth -= 1,
13205 _ => {}
13206 }
13207 if depth == 0 {
13208 let inner: String = chars[start_inner..i].iter().collect();
13209 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
13211 parts.push(StringPart::Expr(Expr {
13212 kind: ExprKind::ArraySlice {
13213 array: name.clone(),
13214 indices,
13215 },
13216 line,
13217 }));
13218 continue 'istr;
13219 }
13220 i += 1;
13221 }
13222 return Err(self.syntax_err(
13223 "Unterminated [ in array slice inside quoted string",
13224 line,
13225 ));
13226 }
13227 parts.push(StringPart::ArrayVar(name));
13228 }
13229 } else if chars[i] == '#'
13230 && i + 1 < chars.len()
13231 && chars[i + 1] == '{'
13232 && !crate::compat_mode()
13233 {
13234 if !literal.is_empty() {
13236 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
13237 }
13238 i += 2; let mut inner = String::new();
13240 let mut depth = 1usize;
13241 while i < chars.len() && depth > 0 {
13242 match chars[i] {
13243 '{' => depth += 1,
13244 '}' => {
13245 depth -= 1;
13246 if depth == 0 {
13247 break;
13248 }
13249 }
13250 _ => {}
13251 }
13252 inner.push(chars[i]);
13253 i += 1;
13254 }
13255 if i < chars.len() {
13256 i += 1; }
13258 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
13259 parts.push(StringPart::Expr(expr));
13260 } else {
13261 literal.push(chars[i]);
13262 i += 1;
13263 }
13264 }
13265 if !literal.is_empty() {
13266 parts.push(StringPart::Literal(literal));
13267 }
13268
13269 if parts.len() == 1 {
13270 if let StringPart::Literal(s) = &parts[0] {
13271 return Ok(Expr {
13272 kind: ExprKind::String(s.clone()),
13273 line,
13274 });
13275 }
13276 }
13277 if parts.is_empty() {
13278 return Ok(Expr {
13279 kind: ExprKind::String(String::new()),
13280 line,
13281 });
13282 }
13283
13284 Ok(Expr {
13285 kind: ExprKind::InterpolatedString(parts),
13286 line,
13287 })
13288 }
13289
13290 fn expr_to_overload_key(&self, e: &Expr) -> PerlResult<String> {
13291 match &e.kind {
13292 ExprKind::String(s) => Ok(s.clone()),
13293 _ => Err(self.syntax_err(
13294 "overload key must be a string literal (e.g. '\"\"' or '+')",
13295 e.line,
13296 )),
13297 }
13298 }
13299
13300 fn expr_to_overload_sub(&self, e: &Expr) -> PerlResult<String> {
13301 match &e.kind {
13302 ExprKind::String(s) => Ok(s.clone()),
13303 ExprKind::Integer(n) => Ok(n.to_string()),
13304 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
13305 _ => Err(self.syntax_err(
13306 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
13307 e.line,
13308 )),
13309 }
13310 }
13311}
13312
13313fn merge_expr_list(parts: Vec<Expr>) -> Expr {
13314 if parts.len() == 1 {
13315 parts.into_iter().next().unwrap()
13316 } else {
13317 let line = parts.first().map(|e| e.line).unwrap_or(0);
13318 Expr {
13319 kind: ExprKind::List(parts),
13320 line,
13321 }
13322 }
13323}
13324
13325pub fn parse_expression_from_str(s: &str, file: &str) -> PerlResult<Expr> {
13327 let mut lexer = Lexer::new_with_file(s, file);
13328 let tokens = lexer.tokenize()?;
13329 let mut parser = Parser::new_with_file(tokens, file);
13330 let e = parser.parse_expression()?;
13331 if !parser.at_eof() {
13332 return Err(parser.syntax_err(
13333 "Extra tokens in embedded string expression",
13334 parser.peek_line(),
13335 ));
13336 }
13337 Ok(e)
13338}
13339
13340pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> PerlResult<Expr> {
13342 let mut lexer = Lexer::new_with_file(s, file);
13343 let tokens = lexer.tokenize()?;
13344 let mut parser = Parser::new_with_file(tokens, file);
13345 let stmts = parser.parse_statements()?;
13346 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
13347 let inner = Expr {
13348 kind: ExprKind::CodeRef {
13349 params: vec![],
13350 body: stmts,
13351 },
13352 line: inner_line,
13353 };
13354 Ok(Expr {
13355 kind: ExprKind::Do(Box::new(inner)),
13356 line,
13357 })
13358}
13359
13360pub fn parse_slice_indices_from_str(s: &str, file: &str) -> PerlResult<Vec<Expr>> {
13363 let mut lexer = Lexer::new_with_file(s, file);
13364 let tokens = lexer.tokenize()?;
13365 let mut parser = Parser::new_with_file(tokens, file);
13366 parser.parse_arg_list()
13367}
13368
13369pub fn parse_format_value_line(line: &str) -> PerlResult<Vec<Expr>> {
13370 let trimmed = line.trim();
13371 if trimmed.is_empty() {
13372 return Ok(vec![]);
13373 }
13374 let mut lexer = Lexer::new(trimmed);
13375 let tokens = lexer.tokenize()?;
13376 let mut parser = Parser::new(tokens);
13377 let mut exprs = Vec::new();
13378 loop {
13379 if parser.at_eof() {
13380 break;
13381 }
13382 exprs.push(parser.parse_assign_expr()?);
13384 if parser.eat(&Token::Comma) {
13385 continue;
13386 }
13387 if !parser.at_eof() {
13388 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
13389 }
13390 break;
13391 }
13392 Ok(exprs)
13393}
13394
13395#[cfg(test)]
13396mod tests {
13397 use super::*;
13398
13399 fn parse_ok(code: &str) -> Program {
13400 let mut lexer = Lexer::new(code);
13401 let tokens = lexer.tokenize().expect("tokenize");
13402 let mut parser = Parser::new(tokens);
13403 parser.parse_program().expect("parse")
13404 }
13405
13406 fn parse_err(code: &str) -> String {
13407 let mut lexer = Lexer::new(code);
13408 let tokens = match lexer.tokenize() {
13409 Ok(t) => t,
13410 Err(e) => return e.message,
13411 };
13412 let mut parser = Parser::new(tokens);
13413 parser.parse_program().unwrap_err().message
13414 }
13415
13416 #[test]
13417 fn parse_empty_program() {
13418 let p = parse_ok("");
13419 assert!(p.statements.is_empty());
13420 }
13421
13422 #[test]
13423 fn parse_semicolons_only() {
13424 let p = parse_ok(";;;");
13425 assert!(p.statements.len() <= 3);
13426 }
13427
13428 #[test]
13429 fn parse_simple_scalar_assignment() {
13430 let p = parse_ok("$x = 1;");
13431 assert_eq!(p.statements.len(), 1);
13432 }
13433
13434 #[test]
13435 fn parse_simple_array_assignment() {
13436 let p = parse_ok("@arr = (1, 2, 3);");
13437 assert_eq!(p.statements.len(), 1);
13438 }
13439
13440 #[test]
13441 fn parse_simple_hash_assignment() {
13442 let p = parse_ok("%h = (a => 1, b => 2);");
13443 assert_eq!(p.statements.len(), 1);
13444 }
13445
13446 #[test]
13447 fn parse_subroutine_decl() {
13448 let p = parse_ok("sub foo { 1 }");
13449 assert_eq!(p.statements.len(), 1);
13450 match &p.statements[0].kind {
13451 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
13452 _ => panic!("expected SubDecl"),
13453 }
13454 }
13455
13456 #[test]
13457 fn parse_subroutine_with_prototype() {
13458 let p = parse_ok("sub foo ($$) { 1 }");
13459 assert_eq!(p.statements.len(), 1);
13460 match &p.statements[0].kind {
13461 StmtKind::SubDecl { prototype, .. } => {
13462 assert!(prototype.is_some());
13463 }
13464 _ => panic!("expected SubDecl"),
13465 }
13466 }
13467
13468 #[test]
13469 fn parse_anonymous_sub() {
13470 let p = parse_ok("my $f = sub { 1 };");
13471 assert_eq!(p.statements.len(), 1);
13472 }
13473
13474 #[test]
13475 fn parse_if_statement() {
13476 let p = parse_ok("if (1) { 2 }");
13477 assert_eq!(p.statements.len(), 1);
13478 matches!(&p.statements[0].kind, StmtKind::If { .. });
13479 }
13480
13481 #[test]
13482 fn parse_if_elsif_else() {
13483 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
13484 assert_eq!(p.statements.len(), 1);
13485 }
13486
13487 #[test]
13488 fn parse_unless_statement() {
13489 let p = parse_ok("unless (0) { 1 }");
13490 assert_eq!(p.statements.len(), 1);
13491 }
13492
13493 #[test]
13494 fn parse_while_loop() {
13495 let p = parse_ok("while ($x) { $x-- }");
13496 assert_eq!(p.statements.len(), 1);
13497 }
13498
13499 #[test]
13500 fn parse_until_loop() {
13501 let p = parse_ok("until ($x) { $x++ }");
13502 assert_eq!(p.statements.len(), 1);
13503 }
13504
13505 #[test]
13506 fn parse_for_c_style() {
13507 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
13508 assert_eq!(p.statements.len(), 1);
13509 }
13510
13511 #[test]
13512 fn parse_foreach_loop() {
13513 let p = parse_ok("foreach my $x (@arr) { 1 }");
13514 assert_eq!(p.statements.len(), 1);
13515 }
13516
13517 #[test]
13518 fn parse_loop_with_label() {
13519 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
13520 assert_eq!(p.statements.len(), 1);
13521 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
13522 }
13523
13524 #[test]
13525 fn parse_begin_block() {
13526 let p = parse_ok("BEGIN { 1 }");
13527 assert_eq!(p.statements.len(), 1);
13528 matches!(&p.statements[0].kind, StmtKind::Begin(_));
13529 }
13530
13531 #[test]
13532 fn parse_end_block() {
13533 let p = parse_ok("END { 1 }");
13534 assert_eq!(p.statements.len(), 1);
13535 matches!(&p.statements[0].kind, StmtKind::End(_));
13536 }
13537
13538 #[test]
13539 fn parse_package_statement() {
13540 let p = parse_ok("package Foo::Bar;");
13541 assert_eq!(p.statements.len(), 1);
13542 match &p.statements[0].kind {
13543 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
13544 _ => panic!("expected Package"),
13545 }
13546 }
13547
13548 #[test]
13549 fn parse_use_statement() {
13550 let p = parse_ok("use strict;");
13551 assert_eq!(p.statements.len(), 1);
13552 }
13553
13554 #[test]
13555 fn parse_no_statement() {
13556 let p = parse_ok("no warnings;");
13557 assert_eq!(p.statements.len(), 1);
13558 }
13559
13560 #[test]
13561 fn parse_require_bareword() {
13562 let p = parse_ok("require Foo::Bar;");
13563 assert_eq!(p.statements.len(), 1);
13564 }
13565
13566 #[test]
13567 fn parse_require_string() {
13568 let p = parse_ok(r#"require "foo.pl";"#);
13569 assert_eq!(p.statements.len(), 1);
13570 }
13571
13572 #[test]
13573 fn parse_eval_block() {
13574 let p = parse_ok("eval { 1 };");
13575 assert_eq!(p.statements.len(), 1);
13576 }
13577
13578 #[test]
13579 fn parse_eval_string() {
13580 let p = parse_ok(r#"eval "1 + 2";"#);
13581 assert_eq!(p.statements.len(), 1);
13582 }
13583
13584 #[test]
13585 fn parse_qw_word_list() {
13586 let p = parse_ok("my @a = qw(foo bar baz);");
13587 assert_eq!(p.statements.len(), 1);
13588 }
13589
13590 #[test]
13591 fn parse_q_string() {
13592 let p = parse_ok("my $s = q{hello};");
13593 assert_eq!(p.statements.len(), 1);
13594 }
13595
13596 #[test]
13597 fn parse_qq_string() {
13598 let p = parse_ok(r#"my $s = qq(hello $x);"#);
13599 assert_eq!(p.statements.len(), 1);
13600 }
13601
13602 #[test]
13603 fn parse_regex_match() {
13604 let p = parse_ok(r#"$x =~ /foo/;"#);
13605 assert_eq!(p.statements.len(), 1);
13606 }
13607
13608 #[test]
13609 fn parse_regex_substitution() {
13610 let p = parse_ok(r#"$x =~ s/foo/bar/g;"#);
13611 assert_eq!(p.statements.len(), 1);
13612 }
13613
13614 #[test]
13615 fn parse_transliterate() {
13616 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/;"#);
13617 assert_eq!(p.statements.len(), 1);
13618 }
13619
13620 #[test]
13621 fn parse_ternary_operator() {
13622 let p = parse_ok("my $x = $a ? 1 : 2;");
13623 assert_eq!(p.statements.len(), 1);
13624 }
13625
13626 #[test]
13627 fn parse_arrow_method_call() {
13628 let p = parse_ok("$obj->method();");
13629 assert_eq!(p.statements.len(), 1);
13630 }
13631
13632 #[test]
13633 fn parse_arrow_deref_hash() {
13634 let p = parse_ok("$r->{key};");
13635 assert_eq!(p.statements.len(), 1);
13636 }
13637
13638 #[test]
13639 fn parse_arrow_deref_array() {
13640 let p = parse_ok("$r->[0];");
13641 assert_eq!(p.statements.len(), 1);
13642 }
13643
13644 #[test]
13645 fn parse_chained_arrow_deref() {
13646 let p = parse_ok("$r->{a}[0]{b};");
13647 assert_eq!(p.statements.len(), 1);
13648 }
13649
13650 #[test]
13651 fn parse_my_multiple_vars() {
13652 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3);");
13653 assert_eq!(p.statements.len(), 1);
13654 }
13655
13656 #[test]
13657 fn parse_our_scalar() {
13658 let p = parse_ok("our $VERSION = '1.0';");
13659 assert_eq!(p.statements.len(), 1);
13660 }
13661
13662 #[test]
13663 fn parse_local_scalar() {
13664 let p = parse_ok("local $/ = undef;");
13665 assert_eq!(p.statements.len(), 1);
13666 }
13667
13668 #[test]
13669 fn parse_state_variable() {
13670 let p = parse_ok("sub counter { state $n = 0; $n++ }");
13671 assert_eq!(p.statements.len(), 1);
13672 }
13673
13674 #[test]
13675 fn parse_postfix_if() {
13676 let p = parse_ok("print 1 if $x;");
13677 assert_eq!(p.statements.len(), 1);
13678 }
13679
13680 #[test]
13681 fn parse_postfix_unless() {
13682 let p = parse_ok("die 'error' unless $ok;");
13683 assert_eq!(p.statements.len(), 1);
13684 }
13685
13686 #[test]
13687 fn parse_postfix_while() {
13688 let p = parse_ok("$x++ while $x < 10;");
13689 assert_eq!(p.statements.len(), 1);
13690 }
13691
13692 #[test]
13693 fn parse_postfix_for() {
13694 let p = parse_ok("print for @arr;");
13695 assert_eq!(p.statements.len(), 1);
13696 }
13697
13698 #[test]
13699 fn parse_last_next_redo() {
13700 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
13701 assert_eq!(p.statements.len(), 1);
13702 }
13703
13704 #[test]
13705 fn parse_return_statement() {
13706 let p = parse_ok("sub foo { return 42 }");
13707 assert_eq!(p.statements.len(), 1);
13708 }
13709
13710 #[test]
13711 fn parse_wantarray() {
13712 let p = parse_ok("sub foo { wantarray ? @a : $a }");
13713 assert_eq!(p.statements.len(), 1);
13714 }
13715
13716 #[test]
13717 fn parse_caller_builtin() {
13718 let p = parse_ok("my @c = caller;");
13719 assert_eq!(p.statements.len(), 1);
13720 }
13721
13722 #[test]
13723 fn parse_ref_to_array() {
13724 let p = parse_ok("my $r = \\@arr;");
13725 assert_eq!(p.statements.len(), 1);
13726 }
13727
13728 #[test]
13729 fn parse_ref_to_hash() {
13730 let p = parse_ok("my $r = \\%hash;");
13731 assert_eq!(p.statements.len(), 1);
13732 }
13733
13734 #[test]
13735 fn parse_ref_to_scalar() {
13736 let p = parse_ok("my $r = \\$x;");
13737 assert_eq!(p.statements.len(), 1);
13738 }
13739
13740 #[test]
13741 fn parse_deref_scalar() {
13742 let p = parse_ok("my $v = $$r;");
13743 assert_eq!(p.statements.len(), 1);
13744 }
13745
13746 #[test]
13747 fn parse_deref_array() {
13748 let p = parse_ok("my @a = @$r;");
13749 assert_eq!(p.statements.len(), 1);
13750 }
13751
13752 #[test]
13753 fn parse_deref_hash() {
13754 let p = parse_ok("my %h = %$r;");
13755 assert_eq!(p.statements.len(), 1);
13756 }
13757
13758 #[test]
13759 fn parse_blessed_ref() {
13760 let p = parse_ok("bless $r, 'Foo';");
13761 assert_eq!(p.statements.len(), 1);
13762 }
13763
13764 #[test]
13765 fn parse_heredoc_basic() {
13766 let p = parse_ok("my $s = <<END;\nfoo\nEND");
13767 assert_eq!(p.statements.len(), 1);
13768 }
13769
13770 #[test]
13771 fn parse_heredoc_quoted() {
13772 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
13773 assert_eq!(p.statements.len(), 1);
13774 }
13775
13776 #[test]
13777 fn parse_do_block() {
13778 let p = parse_ok("my $x = do { 1 + 2 };");
13779 assert_eq!(p.statements.len(), 1);
13780 }
13781
13782 #[test]
13783 fn parse_do_file() {
13784 let p = parse_ok(r#"do "foo.pl";"#);
13785 assert_eq!(p.statements.len(), 1);
13786 }
13787
13788 #[test]
13789 fn parse_map_expression() {
13790 let p = parse_ok("my @b = map { $_ * 2 } @a;");
13791 assert_eq!(p.statements.len(), 1);
13792 }
13793
13794 #[test]
13795 fn parse_grep_expression() {
13796 let p = parse_ok("my @b = grep { $_ > 0 } @a;");
13797 assert_eq!(p.statements.len(), 1);
13798 }
13799
13800 #[test]
13801 fn parse_sort_expression() {
13802 let p = parse_ok("my @b = sort { $a <=> $b } @a;");
13803 assert_eq!(p.statements.len(), 1);
13804 }
13805
13806 #[test]
13807 fn parse_pipe_forward() {
13808 let p = parse_ok("@a |> map { $_ * 2 };");
13809 assert_eq!(p.statements.len(), 1);
13810 }
13811
13812 #[test]
13813 fn parse_expression_from_str_simple() {
13814 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
13815 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
13816 }
13817
13818 #[test]
13819 fn parse_expression_from_str_extra_tokens_error() {
13820 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
13821 assert!(err.message.contains("Extra tokens"));
13822 }
13823
13824 #[test]
13825 fn parse_slice_indices_from_str_basic() {
13826 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
13827 assert_eq!(indices.len(), 3);
13828 }
13829
13830 #[test]
13831 fn parse_format_value_line_empty() {
13832 let exprs = parse_format_value_line("").unwrap();
13833 assert!(exprs.is_empty());
13834 }
13835
13836 #[test]
13837 fn parse_format_value_line_single() {
13838 let exprs = parse_format_value_line("$x").unwrap();
13839 assert_eq!(exprs.len(), 1);
13840 }
13841
13842 #[test]
13843 fn parse_format_value_line_multiple() {
13844 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
13845 assert_eq!(exprs.len(), 3);
13846 }
13847
13848 #[test]
13849 fn parse_unclosed_brace_error() {
13850 let err = parse_err("sub foo {");
13851 assert!(!err.is_empty());
13852 }
13853
13854 #[test]
13855 fn parse_unclosed_paren_error() {
13856 let err = parse_err("print (1, 2");
13857 assert!(!err.is_empty());
13858 }
13859
13860 #[test]
13861 fn parse_invalid_statement_error() {
13862 let err = parse_err("???");
13863 assert!(!err.is_empty());
13864 }
13865
13866 #[test]
13867 fn merge_expr_list_single() {
13868 let e = Expr {
13869 kind: ExprKind::Integer(1),
13870 line: 1,
13871 };
13872 let merged = merge_expr_list(vec![e.clone()]);
13873 matches!(merged.kind, ExprKind::Integer(1));
13874 }
13875
13876 #[test]
13877 fn merge_expr_list_multiple() {
13878 let e1 = Expr {
13879 kind: ExprKind::Integer(1),
13880 line: 1,
13881 };
13882 let e2 = Expr {
13883 kind: ExprKind::Integer(2),
13884 line: 1,
13885 };
13886 let merged = merge_expr_list(vec![e1, e2]);
13887 matches!(merged.kind, ExprKind::List(_));
13888 }
13889}