1use crate::ast::*;
2use crate::error::{ErrorKind, PerlError, PerlResult};
3use crate::lexer::{Lexer, LITERAL_AT_IN_DQUOTE, LITERAL_DOLLAR_IN_DQUOTE};
4use crate::token::Token;
5use crate::vm_helper::VMHelper;
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 "oursync" => StmtKind::OurSync(decls),
28 "local" => StmtKind::Local(decls),
29 "state" => StmtKind::State(decls),
30 _ => unreachable!("parse_my_our_local keyword"),
31 };
32 Statement {
33 label: None,
34 kind,
35 line,
36 }
37}
38
39fn destructure_stmt_die_string(line: usize, msg: &str) -> Statement {
40 Statement {
41 label: None,
42 kind: StmtKind::Expression(Expr {
43 kind: ExprKind::Die(vec![Expr {
44 kind: ExprKind::String(msg.to_string()),
45 line,
46 }]),
47 line,
48 }),
49 line,
50 }
51}
52
53fn destructure_stmt_unless_die(line: usize, cond: Expr, msg: &str) -> Statement {
54 Statement {
55 label: None,
56 kind: StmtKind::Unless {
57 condition: cond,
58 body: vec![destructure_stmt_die_string(line, msg)],
59 else_block: None,
60 },
61 line,
62 }
63}
64
65fn destructure_expr_scalar_tmp(name: &str, line: usize) -> Expr {
66 Expr {
67 kind: ExprKind::ScalarVar(name.to_string()),
68 line,
69 }
70}
71
72fn destructure_expr_array_len(tmp: &str, line: usize) -> Expr {
73 Expr {
74 kind: ExprKind::Deref {
75 expr: Box::new(destructure_expr_scalar_tmp(tmp, line)),
76 kind: Sigil::Array,
77 },
78 line,
79 }
80}
81
82pub struct Parser {
83 tokens: Vec<(Token, usize)>,
84 pos: usize,
85 next_rate_limit_slot: u32,
87 suppress_indirect_paren_call: u32,
90 pipe_rhs_depth: u32,
96 block_depth: u32,
103 no_pipe_forward_depth: u32,
111 suppress_scalar_hash_brace: u32,
114 next_desugar_tmp: u32,
116 error_file: String,
118 declared_subs: std::collections::HashSet<String>,
120 suppress_parenless_call: u32,
124 suppress_slash_as_div: u32,
127 pub suppress_m_regex: u32,
130 suppress_colon_range: u32,
134 suppress_tilde_range: u32,
140 thread_last_mode: bool,
143 pub parsing_module: bool,
146 list_construct_close_pos: Option<usize>,
154 pending_synthetic_subs: Vec<Statement>,
159 next_overload_anon_id: u32,
161 pub bare_positional_indices: std::collections::HashSet<usize>,
168}
169
170impl Parser {
171 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
172 Self::new_with_file(tokens, "-e")
173 }
174
175 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
176 Self {
177 tokens,
178 pos: 0,
179 next_rate_limit_slot: 0,
180 suppress_indirect_paren_call: 0,
181 pipe_rhs_depth: 0,
182 no_pipe_forward_depth: 0,
183 suppress_scalar_hash_brace: 0,
184 next_desugar_tmp: 0,
185 error_file: file.into(),
186 declared_subs: std::collections::HashSet::new(),
187 suppress_parenless_call: 0,
188 suppress_slash_as_div: 0,
189 suppress_m_regex: 0,
190 suppress_colon_range: 0,
191 suppress_tilde_range: 0,
192 thread_last_mode: false,
193 pending_synthetic_subs: Vec::new(),
194 next_overload_anon_id: 0,
195 parsing_module: false,
196 list_construct_close_pos: None,
197 bare_positional_indices: std::collections::HashSet::new(),
198 block_depth: 0,
199 }
200 }
201
202 fn alloc_desugar_tmp(&mut self) -> u32 {
203 let n = self.next_desugar_tmp;
204 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
205 n
206 }
207
208 #[inline]
212 fn in_pipe_rhs(&self) -> bool {
213 self.pipe_rhs_depth > 0
214 }
215
216 fn pipe_supplies_slurped_list_operand(&self) -> bool {
219 self.in_pipe_rhs()
220 && (matches!(
221 self.peek(),
222 Token::Semicolon
223 | Token::RBrace
224 | Token::RParen
225 | Token::Eof
226 | Token::Comma
227 | Token::PipeForward
228 ) || self.peek_line() > self.prev_line())
229 }
230
231 #[inline]
236 fn pipe_placeholder_list(&self, line: usize) -> Expr {
237 Expr {
238 kind: ExprKind::List(vec![]),
239 line,
240 }
241 }
242
243 fn is_block_then_list_pipe_builtin(name: &str) -> bool {
248 matches!(
249 name,
250 "pfirst"
251 | "pany"
252 | "any"
253 | "all"
254 | "none"
255 | "first"
256 | "take_while"
257 | "drop_while"
258 | "skip_while"
259 | "reject"
260 | "grepv"
261 | "tap"
262 | "peek"
263 | "group_by"
264 | "chunk_by"
265 | "partition"
266 | "min_by"
267 | "max_by"
268 | "zip_with"
269 | "count_by"
270 )
271 }
272
273 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
284 let line = expr.line;
285 let topic = || Expr {
286 kind: ExprKind::ScalarVar("_".into()),
287 line,
288 };
289 match expr.kind {
290 ExprKind::Bareword(ref name) => Expr {
291 kind: ExprKind::FuncCall {
292 name: name.clone(),
293 args: vec![topic()],
294 },
295 line,
296 },
297 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
299 kind: ExprKind::Unlink(vec![topic()]),
300 line,
301 },
302 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
303 kind: ExprKind::Chmod(vec![topic()]),
304 line,
305 },
306 ExprKind::Stat(_) => expr,
308 ExprKind::Lstat(_) => expr,
309 ExprKind::Readlink(_) => expr,
310 ExprKind::Rev(ref inner) => {
312 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
313 Expr {
314 kind: ExprKind::Rev(Box::new(topic())),
315 line,
316 }
317 } else {
318 expr
319 }
320 }
321 _ => expr,
322 }
323 }
324
325 fn parse_assign_expr_stop_at_pipe(&mut self) -> PerlResult<Expr> {
333 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
334 let r = self.parse_assign_expr();
335 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
336 r
337 }
338
339 fn syntax_err(&self, message: impl Into<String>, line: usize) -> PerlError {
340 PerlError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
341 }
342
343 fn try_parse_coderef_listop_args(&mut self, line: usize) -> PerlResult<Option<Vec<Expr>>> {
350 if !matches!(self.peek(), Token::ScalarVar(_) | Token::Backslash) {
351 return Ok(None);
352 }
353 let f = self.parse_assign_expr_stop_at_pipe()?;
354 let _ = self.eat(&Token::Comma);
355 let list = if self.in_pipe_rhs()
356 && matches!(
357 self.peek(),
358 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
359 ) {
360 self.pipe_placeholder_list(line)
361 } else {
362 self.parse_expression()?
363 };
364 Ok(Some(vec![f, list]))
365 }
366
367 fn alloc_rate_limit_slot(&mut self) -> u32 {
368 let s = self.next_rate_limit_slot;
369 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
370 s
371 }
372
373 fn peek(&self) -> &Token {
374 self.tokens
375 .get(self.pos)
376 .map(|(t, _)| t)
377 .unwrap_or(&Token::Eof)
378 }
379
380 fn peek_line(&self) -> usize {
381 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
382 }
383
384 fn peek_at(&self, offset: usize) -> &Token {
385 self.tokens
386 .get(self.pos + offset)
387 .map(|(t, _)| t)
388 .unwrap_or(&Token::Eof)
389 }
390
391 fn advance(&mut self) -> (Token, usize) {
392 let tok = self
393 .tokens
394 .get(self.pos)
395 .cloned()
396 .unwrap_or((Token::Eof, 0));
397 self.pos += 1;
398 tok
399 }
400
401 fn prev_line(&self) -> usize {
403 if self.pos > 0 {
404 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
405 } else {
406 0
407 }
408 }
409
410 fn looks_like_hashref(&self) -> bool {
419 debug_assert!(matches!(self.peek(), Token::LBrace));
420 let tok1 = self.peek_at(1);
421 let tok2 = self.peek_at(2);
422 match tok1 {
423 Token::RBrace => true,
424 Token::Ident(_)
425 | Token::SingleString(_)
426 | Token::DoubleString(_)
427 | Token::ScalarVar(_)
428 | Token::Integer(_) => matches!(tok2, Token::FatArrow),
429 Token::HashVar(_) => matches!(tok2, Token::RBrace | Token::Comma),
430 _ => false,
431 }
432 }
433
434 fn expect(&mut self, expected: &Token) -> PerlResult<usize> {
435 let (tok, line) = self.advance();
436 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
437 Ok(line)
438 } else {
439 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
440 }
441 }
442
443 fn eat(&mut self, expected: &Token) -> bool {
444 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
445 self.advance();
446 true
447 } else {
448 false
449 }
450 }
451
452 fn at_eof(&self) -> bool {
453 matches!(self.peek(), Token::Eof)
454 }
455
456 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
458 matches!(
459 tok,
460 Token::RParen
461 | Token::Semicolon
462 | Token::Comma
463 | Token::RBrace
464 | Token::Eof
465 | Token::LogAnd
466 | Token::LogOr
467 | Token::LogAndWord
468 | Token::LogOrWord
469 | Token::PipeForward
470 )
471 }
472
473 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
477 if crate::compat_mode() {
479 return false;
480 }
481 if self.peek_line() == stmt_line {
482 return false;
483 }
484 matches!(
485 self.peek(),
486 Token::Ident(ref kw) if matches!(kw.as_str(),
487 "use" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
488 | "if" | "unless" | "while" | "until" | "for" | "foreach"
489 | "return" | "last" | "next" | "redo" | "package" | "require"
490 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
491 )
492 )
493 }
494
495 fn next_is_new_statement_start(&self, stmt_line: usize) -> bool {
499 if crate::compat_mode() {
500 return false;
501 }
502 if self.peek_line() == stmt_line {
503 return false;
504 }
505 matches!(
506 self.peek(),
507 Token::ScalarVar(_)
508 | Token::DerefScalarVar(_)
509 | Token::ArrayVar(_)
510 | Token::HashVar(_)
511 | Token::LBrace
512 ) || self.next_is_new_stmt_keyword(stmt_line)
513 }
514
515 pub fn parse_program(&mut self) -> PerlResult<Program> {
518 let mut statements = self.parse_statements()?;
519 if !self.pending_synthetic_subs.is_empty() {
523 let synthetics = std::mem::take(&mut self.pending_synthetic_subs);
524 let mut combined = Vec::with_capacity(synthetics.len() + statements.len());
525 combined.extend(synthetics);
526 combined.append(&mut statements);
527 statements = combined;
528 }
529 Ok(Program { statements })
530 }
531
532 pub fn parse_statements(&mut self) -> PerlResult<Vec<Statement>> {
534 let mut statements = Vec::new();
535 while !self.at_eof() {
536 if matches!(self.peek(), Token::Semicolon) {
537 let line = self.peek_line();
538 self.advance();
539 statements.push(Statement {
540 label: None,
541 kind: StmtKind::Empty,
542 line,
543 });
544 continue;
545 }
546 statements.push(self.parse_statement()?);
547 }
548 Ok(statements)
549 }
550
551 fn parse_statement(&mut self) -> PerlResult<Statement> {
554 let line = self.peek_line();
555
556 let label = match self.peek().clone() {
559 Token::Ident(_) => {
560 if matches!(self.peek_at(1), Token::Colon)
561 && !matches!(self.peek_at(2), Token::Colon)
562 {
563 let (tok, _) = self.advance();
564 let l = match tok {
565 Token::Ident(l) => l,
566 _ => unreachable!(),
567 };
568 self.advance(); Some(l)
570 } else {
571 None
572 }
573 }
574 _ => None,
575 };
576
577 let mut stmt = match self.peek().clone() {
578 Token::FormatDecl { .. } => {
579 let tok_line = self.peek_line();
580 let (tok, _) = self.advance();
581 match tok {
582 Token::FormatDecl { name, lines } => Statement {
583 label: label.clone(),
584 kind: StmtKind::FormatDecl { name, lines },
585 line: tok_line,
586 },
587 _ => unreachable!(),
588 }
589 }
590 Token::Ident(ref kw) => match kw.as_str() {
591 "if" => self.parse_if()?,
592 "unless" => self.parse_unless()?,
593 "while" => {
594 let mut s = self.parse_while()?;
595 if let StmtKind::While {
596 label: ref mut lbl, ..
597 } = s.kind
598 {
599 *lbl = label.clone();
600 }
601 s
602 }
603 "until" => {
604 let mut s = self.parse_until()?;
605 if let StmtKind::Until {
606 label: ref mut lbl, ..
607 } = s.kind
608 {
609 *lbl = label.clone();
610 }
611 s
612 }
613 "for" => {
614 let mut s = self.parse_for_or_foreach()?;
615 match s.kind {
616 StmtKind::For {
617 label: ref mut lbl, ..
618 }
619 | StmtKind::Foreach {
620 label: ref mut lbl, ..
621 } => *lbl = label.clone(),
622 _ => {}
623 }
624 s
625 }
626 "foreach" => {
627 let mut s = self.parse_foreach()?;
628 if let StmtKind::Foreach {
629 label: ref mut lbl, ..
630 } = s.kind
631 {
632 *lbl = label.clone();
633 }
634 s
635 }
636 "sub" => {
637 if crate::no_interop_mode() {
638 return Err(self.syntax_err(
639 "stryke uses `fn` instead of `sub` (--no-interop is active)",
640 self.peek_line(),
641 ));
642 }
643 self.parse_sub_decl(true)?
644 }
645 "fn" => self.parse_sub_decl(false)?,
646 "struct" => {
647 if crate::compat_mode() {
648 return Err(self.syntax_err(
649 "`struct` is a stryke extension (disabled by --compat)",
650 self.peek_line(),
651 ));
652 }
653 self.parse_struct_decl()?
654 }
655 "enum" => {
656 if crate::compat_mode() {
657 return Err(self.syntax_err(
658 "`enum` is a stryke extension (disabled by --compat)",
659 self.peek_line(),
660 ));
661 }
662 self.parse_enum_decl()?
663 }
664 "class" => {
665 if crate::compat_mode() {
666 return Err(self.syntax_err(
668 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
669 self.peek_line(),
670 ));
671 }
672 self.parse_class_decl(false, false)?
673 }
674 "abstract" => {
675 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
677 return Err(self.syntax_err(
678 "`abstract` must be followed by `class`",
679 self.peek_line(),
680 ));
681 }
682 self.parse_class_decl(true, false)?
683 }
684 "final" => {
685 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
687 return Err(self
688 .syntax_err("`final` must be followed by `class`", self.peek_line()));
689 }
690 self.parse_class_decl(false, true)?
691 }
692 "trait" => {
693 if crate::compat_mode() {
694 return Err(self.syntax_err(
695 "`trait` is a stryke extension (disabled by --compat)",
696 self.peek_line(),
697 ));
698 }
699 self.parse_trait_decl()?
700 }
701 "my" => self.parse_my_our_local("my", false)?,
702 "state" => self.parse_my_our_local("state", false)?,
703 "mysync" => {
704 if crate::compat_mode() {
705 return Err(self.syntax_err(
706 "`mysync` is a stryke extension (disabled by --compat)",
707 self.peek_line(),
708 ));
709 }
710 self.parse_my_our_local("mysync", false)?
711 }
712 "oursync" => {
713 if crate::compat_mode() {
714 return Err(self.syntax_err(
715 "`oursync` is a stryke extension (disabled by --compat)",
716 self.peek_line(),
717 ));
718 }
719 self.parse_my_our_local("oursync", false)?
720 }
721 "frozen" | "const" => {
722 let leading = kw.as_str().to_string();
723 if crate::compat_mode() {
724 return Err(self.syntax_err(
725 format!("`{leading}` is a stryke extension (disabled by --compat)"),
726 self.peek_line(),
727 ));
728 }
729 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
735 if kw == "my" {
736 let mut stmt = self.parse_my_our_local("my", false)?;
737 if let StmtKind::My(ref mut decls) = stmt.kind {
738 for decl in decls.iter_mut() {
739 decl.frozen = true;
740 }
741 }
742 stmt
743 } else {
744 return Err(self.syntax_err(
745 format!("Expected 'my' after '{leading}'"),
746 self.peek_line(),
747 ));
748 }
749 } else {
750 return Err(self.syntax_err(
751 format!("Expected 'my' after '{leading}'"),
752 self.peek_line(),
753 ));
754 }
755 }
756 "typed" => {
757 if crate::compat_mode() {
758 return Err(self.syntax_err(
759 "`typed` is a stryke extension (disabled by --compat)",
760 self.peek_line(),
761 ));
762 }
763 self.advance();
764 if let Token::Ident(ref kw) = self.peek().clone() {
765 if kw == "my" {
766 self.parse_my_our_local("my", true)?
767 } else {
768 return Err(
769 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
770 );
771 }
772 } else {
773 return Err(
774 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
775 );
776 }
777 }
778 "our" => self.parse_my_our_local("our", false)?,
779 "local" => self.parse_my_our_local("local", false)?,
780 "package" => self.parse_package()?,
781 "use" => self.parse_use()?,
782 "no" => self.parse_no()?,
783 "return" => self.parse_return()?,
784 "last" => {
785 self.advance();
786 let lbl = if let Token::Ident(ref s) = self.peek() {
787 if s.chars().all(|c| c.is_uppercase() || c == '_') {
788 let (Token::Ident(l), _) = self.advance() else {
789 unreachable!()
790 };
791 Some(l)
792 } else {
793 None
794 }
795 } else {
796 None
797 };
798 let stmt = Statement {
799 label: None,
800 kind: StmtKind::Last(lbl.or(label.clone())),
801 line,
802 };
803 self.parse_stmt_postfix_modifier(stmt)?
804 }
805 "next" => {
806 self.advance();
807 let lbl = if let Token::Ident(ref s) = self.peek() {
808 if s.chars().all(|c| c.is_uppercase() || c == '_') {
809 let (Token::Ident(l), _) = self.advance() else {
810 unreachable!()
811 };
812 Some(l)
813 } else {
814 None
815 }
816 } else {
817 None
818 };
819 let stmt = Statement {
820 label: None,
821 kind: StmtKind::Next(lbl.or(label.clone())),
822 line,
823 };
824 self.parse_stmt_postfix_modifier(stmt)?
825 }
826 "redo" => {
827 self.advance();
828 self.eat(&Token::Semicolon);
829 Statement {
830 label: None,
831 kind: StmtKind::Redo(label.clone()),
832 line,
833 }
834 }
835 "BEGIN" => {
836 self.advance();
837 let block = self.parse_block()?;
838 Statement {
839 label: None,
840 kind: StmtKind::Begin(block),
841 line,
842 }
843 }
844 "END" => {
845 self.advance();
846 let block = self.parse_block()?;
847 Statement {
848 label: None,
849 kind: StmtKind::End(block),
850 line,
851 }
852 }
853 "UNITCHECK" => {
854 self.advance();
855 let block = self.parse_block()?;
856 Statement {
857 label: None,
858 kind: StmtKind::UnitCheck(block),
859 line,
860 }
861 }
862 "CHECK" => {
863 self.advance();
864 let block = self.parse_block()?;
865 Statement {
866 label: None,
867 kind: StmtKind::Check(block),
868 line,
869 }
870 }
871 "INIT" => {
872 self.advance();
873 let block = self.parse_block()?;
874 Statement {
875 label: None,
876 kind: StmtKind::Init(block),
877 line,
878 }
879 }
880 "goto" => {
881 self.advance();
882 let target = self.parse_expression()?;
883 let stmt = Statement {
884 label: None,
885 kind: StmtKind::Goto {
886 target: Box::new(target),
887 },
888 line,
889 };
890 self.parse_stmt_postfix_modifier(stmt)?
892 }
893 "continue" => {
894 self.advance();
895 let block = self.parse_block()?;
896 Statement {
897 label: None,
898 kind: StmtKind::Continue(block),
899 line,
900 }
901 }
902 "before"
903 if matches!(
904 self.peek_at(1),
905 Token::SingleString(_) | Token::DoubleString(_)
906 ) =>
907 {
908 self.parse_advice_decl(crate::ast::AdviceKind::Before)?
909 }
910 "after"
911 if matches!(
912 self.peek_at(1),
913 Token::SingleString(_) | Token::DoubleString(_)
914 ) =>
915 {
916 self.parse_advice_decl(crate::ast::AdviceKind::After)?
917 }
918 "around"
919 if matches!(
920 self.peek_at(1),
921 Token::SingleString(_) | Token::DoubleString(_)
922 ) =>
923 {
924 self.parse_advice_decl(crate::ast::AdviceKind::Around)?
925 }
926 "try" => self.parse_try_catch()?,
927 "defer" => self.parse_defer_stmt()?,
928 "tie" => self.parse_tie_stmt()?,
929 "given" => self.parse_given()?,
930 "when" => self.parse_when_stmt()?,
931 "default" => self.parse_default_stmt()?,
932 "eval_timeout" => self.parse_eval_timeout()?,
933 "do" => {
934 if matches!(self.peek_at(1), Token::LBrace) {
935 self.advance();
936 let body = self.parse_block()?;
937 if let Token::Ident(ref w) = self.peek().clone() {
938 if w == "while" {
939 self.advance();
940 self.expect(&Token::LParen)?;
941 let mut condition = self.parse_expression()?;
942 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
943 self.expect(&Token::RParen)?;
944 self.eat(&Token::Semicolon);
945 Statement {
946 label: label.clone(),
947 kind: StmtKind::DoWhile { body, condition },
948 line,
949 }
950 } else {
951 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
952 let inner = Expr {
953 kind: ExprKind::CodeRef {
954 params: vec![],
955 body,
956 },
957 line: inner_line,
958 };
959 let expr = Expr {
960 kind: ExprKind::Do(Box::new(inner)),
961 line,
962 };
963 let stmt = Statement {
964 label: label.clone(),
965 kind: StmtKind::Expression(expr),
966 line,
967 };
968 self.parse_stmt_postfix_modifier(stmt)?
970 }
971 } else {
972 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
973 let inner = Expr {
974 kind: ExprKind::CodeRef {
975 params: vec![],
976 body,
977 },
978 line: inner_line,
979 };
980 let expr = Expr {
981 kind: ExprKind::Do(Box::new(inner)),
982 line,
983 };
984 let stmt = Statement {
985 label: label.clone(),
986 kind: StmtKind::Expression(expr),
987 line,
988 };
989 self.parse_stmt_postfix_modifier(stmt)?
990 }
991 } else {
992 if let Some(expr) = self.try_parse_bareword_stmt_call() {
993 let stmt = self.maybe_postfix_modifier(expr)?;
994 self.parse_stmt_postfix_modifier(stmt)?
995 } else {
996 let expr = self.parse_expression()?;
997 let stmt = self.maybe_postfix_modifier(expr)?;
998 self.parse_stmt_postfix_modifier(stmt)?
999 }
1000 }
1001 }
1002 _ => {
1003 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1005 let stmt = self.maybe_postfix_modifier(expr)?;
1006 self.parse_stmt_postfix_modifier(stmt)?
1007 } else {
1008 let expr = self.parse_expression()?;
1009 let stmt = self.maybe_postfix_modifier(expr)?;
1010 self.parse_stmt_postfix_modifier(stmt)?
1011 }
1012 }
1013 },
1014 Token::LBrace => {
1015 if self.looks_like_hashref() {
1018 let expr = self.parse_expression()?;
1019 let stmt = self.maybe_postfix_modifier(expr)?;
1020 self.parse_stmt_postfix_modifier(stmt)?
1021 } else {
1022 let block = self.parse_block()?;
1023 let stmt = Statement {
1024 label: None,
1025 kind: StmtKind::Block(block),
1026 line,
1027 };
1028 self.parse_stmt_postfix_modifier(stmt)?
1030 }
1031 }
1032 _ => {
1033 let expr = self.parse_expression()?;
1034 let stmt = self.maybe_postfix_modifier(expr)?;
1035 self.parse_stmt_postfix_modifier(stmt)?
1036 }
1037 };
1038
1039 stmt.label = label;
1040 Ok(stmt)
1041 }
1042
1043 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> PerlResult<Statement> {
1045 let line = stmt.line;
1046 if self.peek_line() > self.prev_line() {
1051 self.eat(&Token::Semicolon);
1052 return Ok(stmt);
1053 }
1054 if let Token::Ident(ref kw) = self.peek().clone() {
1055 match kw.as_str() {
1056 "if" => {
1057 self.advance();
1058 let mut cond = self.parse_expression()?;
1059 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1060 self.eat(&Token::Semicolon);
1061 return Ok(Statement {
1062 label: None,
1063 kind: StmtKind::If {
1064 condition: cond,
1065 body: vec![stmt],
1066 elsifs: vec![],
1067 else_block: None,
1068 },
1069 line,
1070 });
1071 }
1072 "unless" => {
1073 self.advance();
1074 let mut cond = self.parse_expression()?;
1075 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1076 self.eat(&Token::Semicolon);
1077 return Ok(Statement {
1078 label: None,
1079 kind: StmtKind::Unless {
1080 condition: cond,
1081 body: vec![stmt],
1082 else_block: None,
1083 },
1084 line,
1085 });
1086 }
1087 "while" | "until" | "for" | "foreach" => {
1088 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
1091 let out = self.maybe_postfix_modifier(expr)?;
1092 self.eat(&Token::Semicolon);
1093 return Ok(out);
1094 }
1095 return Err(self.syntax_err(
1096 format!("postfix `{}` is not supported on this statement form", kw),
1097 self.peek_line(),
1098 ));
1099 }
1100 "pmap" | "pflat_map" | "pgrep" | "pfor" | "preduce" | "pcache" => {
1102 let line = stmt.line;
1103 let block = self.stmt_into_parallel_block(stmt)?;
1104 let which = kw.as_str();
1105 self.advance();
1106 self.eat(&Token::Comma);
1107 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
1108 self.eat(&Token::Semicolon);
1109 let list = Box::new(list);
1110 let progress = progress.map(Box::new);
1111 let kind = match which {
1112 "pmap" => ExprKind::PMapExpr {
1113 block,
1114 list,
1115 progress,
1116 flat_outputs: false,
1117 on_cluster: None,
1118 stream: false,
1119 },
1120 "pflat_map" => ExprKind::PMapExpr {
1121 block,
1122 list,
1123 progress,
1124 flat_outputs: true,
1125 on_cluster: None,
1126 stream: false,
1127 },
1128 "pgrep" => ExprKind::PGrepExpr {
1129 block,
1130 list,
1131 progress,
1132 stream: false,
1133 },
1134 "pfor" => ExprKind::PForExpr {
1135 block,
1136 list,
1137 progress,
1138 },
1139 "preduce" => ExprKind::PReduceExpr {
1140 block,
1141 list,
1142 progress,
1143 },
1144 "pcache" => ExprKind::PcacheExpr {
1145 block,
1146 list,
1147 progress,
1148 },
1149 _ => unreachable!(),
1150 };
1151 return Ok(Statement {
1152 label: None,
1153 kind: StmtKind::Expression(Expr { kind, line }),
1154 line,
1155 });
1156 }
1157 _ => {}
1158 }
1159 }
1160 self.eat(&Token::Semicolon);
1161 Ok(stmt)
1162 }
1163
1164 fn stmt_into_parallel_block(&self, stmt: Statement) -> PerlResult<Block> {
1167 let line = stmt.line;
1168 match stmt.kind {
1169 StmtKind::Block(block) => Ok(block),
1170 StmtKind::Expression(expr) => {
1171 if let ExprKind::Do(ref inner) = expr.kind {
1172 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
1173 return Ok(body.clone());
1174 }
1175 }
1176 Ok(vec![Statement {
1177 label: None,
1178 kind: StmtKind::Expression(expr),
1179 line,
1180 }])
1181 }
1182 _ => Err(self.syntax_err(
1183 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
1184 line,
1185 )),
1186 }
1187 }
1188
1189 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
1192 match stmt.kind {
1193 StmtKind::Expression(expr) => Some(expr),
1194 StmtKind::Block(block) => {
1195 let line = stmt.line;
1196 let inner = Expr {
1197 kind: ExprKind::CodeRef {
1198 params: vec![],
1199 body: block,
1200 },
1201 line,
1202 };
1203 Some(Expr {
1204 kind: ExprKind::Do(Box::new(inner)),
1205 line,
1206 })
1207 }
1208 _ => None,
1209 }
1210 }
1211
1212 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
1215 matches!(
1216 self.peek(),
1217 Token::Ident(ref kw)
1218 if matches!(
1219 kw.as_str(),
1220 "if" | "unless" | "while" | "until" | "for" | "foreach"
1221 )
1222 )
1223 }
1224
1225 fn peek_is_named_unary_terminator(&self) -> bool {
1232 matches!(
1233 self.peek(),
1234 Token::Semicolon
1235 | Token::RBrace
1236 | Token::RParen
1237 | Token::RBracket
1238 | Token::Eof
1239 | Token::Comma
1240 | Token::FatArrow
1241 | Token::PipeForward
1242 | Token::Question
1243 | Token::Colon
1244 | Token::NumEq
1245 | Token::NumNe
1246 | Token::NumLt
1247 | Token::NumGt
1248 | Token::NumLe
1249 | Token::NumGe
1250 | Token::Spaceship
1251 | Token::StrEq
1252 | Token::StrNe
1253 | Token::StrLt
1254 | Token::StrGt
1255 | Token::StrLe
1256 | Token::StrGe
1257 | Token::StrCmp
1258 | Token::LogAnd
1259 | Token::LogOr
1260 | Token::LogAndWord
1261 | Token::LogOrWord
1262 | Token::DefinedOr
1263 | Token::Range
1264 | Token::RangeExclusive
1265 | Token::Assign
1266 | Token::PlusAssign
1267 | Token::MinusAssign
1268 | Token::MulAssign
1269 | Token::DivAssign
1270 | Token::ModAssign
1271 | Token::PowAssign
1272 | Token::DotAssign
1273 | Token::AndAssign
1274 | Token::OrAssign
1275 | Token::XorAssign
1276 | Token::DefinedOrAssign
1277 | Token::ShiftLeftAssign
1278 | Token::ShiftRightAssign
1279 | Token::BitAndAssign
1280 | Token::BitOrAssign
1281 )
1282 }
1283
1284 fn maybe_postfix_modifier(&mut self, expr: Expr) -> PerlResult<Statement> {
1285 let line = expr.line;
1286 if self.peek_line() > self.prev_line() {
1288 return Ok(Statement {
1289 label: None,
1290 kind: StmtKind::Expression(expr),
1291 line,
1292 });
1293 }
1294 match self.peek() {
1295 Token::Ident(ref kw) => match kw.as_str() {
1296 "if" => {
1297 self.advance();
1298 let cond = self.parse_expression()?;
1299 Ok(Statement {
1300 label: None,
1301 kind: StmtKind::Expression(Expr {
1302 kind: ExprKind::PostfixIf {
1303 expr: Box::new(expr),
1304 condition: Box::new(cond),
1305 },
1306 line,
1307 }),
1308 line,
1309 })
1310 }
1311 "unless" => {
1312 self.advance();
1313 let cond = self.parse_expression()?;
1314 Ok(Statement {
1315 label: None,
1316 kind: StmtKind::Expression(Expr {
1317 kind: ExprKind::PostfixUnless {
1318 expr: Box::new(expr),
1319 condition: Box::new(cond),
1320 },
1321 line,
1322 }),
1323 line,
1324 })
1325 }
1326 "while" => {
1327 self.advance();
1328 let cond = self.parse_expression()?;
1329 Ok(Statement {
1330 label: None,
1331 kind: StmtKind::Expression(Expr {
1332 kind: ExprKind::PostfixWhile {
1333 expr: Box::new(expr),
1334 condition: Box::new(cond),
1335 },
1336 line,
1337 }),
1338 line,
1339 })
1340 }
1341 "until" => {
1342 self.advance();
1343 let cond = self.parse_expression()?;
1344 Ok(Statement {
1345 label: None,
1346 kind: StmtKind::Expression(Expr {
1347 kind: ExprKind::PostfixUntil {
1348 expr: Box::new(expr),
1349 condition: Box::new(cond),
1350 },
1351 line,
1352 }),
1353 line,
1354 })
1355 }
1356 "for" | "foreach" => {
1357 self.advance();
1358 let list = self.parse_expression()?;
1359 Ok(Statement {
1360 label: None,
1361 kind: StmtKind::Expression(Expr {
1362 kind: ExprKind::PostfixForeach {
1363 expr: Box::new(expr),
1364 list: Box::new(list),
1365 },
1366 line,
1367 }),
1368 line,
1369 })
1370 }
1371 _ => Ok(Statement {
1372 label: None,
1373 kind: StmtKind::Expression(expr),
1374 line,
1375 }),
1376 },
1377 _ => Ok(Statement {
1378 label: None,
1379 kind: StmtKind::Expression(expr),
1380 line,
1381 }),
1382 }
1383 }
1384
1385 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1387 let saved = self.pos;
1388 let line = self.peek_line();
1389 let mut name = match self.peek() {
1390 Token::Ident(n) => n.clone(),
1391 _ => return None,
1392 };
1393 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1395 return None;
1396 }
1397 self.advance();
1398 while self.eat(&Token::PackageSep) {
1399 match self.advance() {
1400 (Token::Ident(part), _) => {
1401 name = format!("{}::{}", name, part);
1402 }
1403 _ => {
1404 self.pos = saved;
1405 return None;
1406 }
1407 }
1408 }
1409 match self.peek() {
1410 Token::Semicolon | Token::RBrace => Some(Expr {
1411 kind: ExprKind::FuncCall { name, args: vec![] },
1412 line,
1413 }),
1414 _ => {
1415 self.pos = saved;
1416 None
1417 }
1418 }
1419 }
1420
1421 pub(crate) fn operator_keyword_to_ident_str(tok: &Token) -> Option<&'static str> {
1425 Some(match tok {
1426 Token::StrEq => "eq",
1427 Token::StrNe => "ne",
1428 Token::StrLt => "lt",
1429 Token::StrGt => "gt",
1430 Token::StrLe => "le",
1431 Token::StrGe => "ge",
1432 Token::StrCmp => "cmp",
1433 Token::LogAndWord => "and",
1434 Token::LogOrWord => "or",
1435 Token::LogNotWord => "not",
1436 Token::X => "x",
1437 _ => return None,
1438 })
1439 }
1440
1441 pub(crate) fn is_underscore_topic_slot(name: &str) -> bool {
1445 if name == "_" {
1446 return true;
1447 }
1448 if !name.starts_with('_') || name.len() < 2 {
1449 return false;
1450 }
1451 let bytes = name.as_bytes();
1452 let mut i = 1;
1453 while i < bytes.len() && bytes[i].is_ascii_digit() {
1455 i += 1;
1456 }
1457 let chevrons_start = i;
1459 while i < bytes.len() && bytes[i] == b'<' {
1460 i += 1;
1461 }
1462 i == bytes.len() && (i > 1 || chevrons_start > 1)
1464 }
1465
1466 pub(crate) fn is_reserved_special_var_name(name: &str) -> bool {
1476 matches!(
1477 name,
1478 "STDIN" | "STDOUT" | "STDERR" | "ARGV" | "ARGVOUT" | "DATA"
1480 | "ENV" | "INC" | "SIG" | "ISA"
1488 | "EXPORT" | "EXPORT_OK" | "EXPORT_TAGS"
1489 | "VERSION"
1490 | "__FILE__" | "__LINE__" | "__PACKAGE__" | "__SUB__"
1492 | "__DATA__" | "__END__"
1493 )
1494 }
1495
1496 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1498 if Self::is_underscore_topic_slot(name) {
1503 return false;
1504 }
1505 !matches!(
1506 name,
1507 "__FILE__"
1508 | "__LINE__"
1509 | "abs"
1510 | "async"
1511 | "spawn"
1512 | "atan2"
1513 | "await"
1514 | "barrier"
1515 | "bless"
1516 | "caller"
1517 | "capture"
1518 | "cat"
1519 | "chdir"
1520 | "chmod"
1521 | "chomp"
1522 | "chop"
1523 | "chr"
1524 | "chown"
1525 | "closedir"
1526 | "close"
1527 | "collect"
1528 | "cos"
1529 | "crypt"
1530 | "defined"
1531 | "dec"
1532 | "delete"
1533 | "die"
1534 | "deque"
1535 | "do"
1536 | "each"
1537 | "eof"
1538 | "fore"
1539 | "eval"
1540 | "exec"
1541 | "exists"
1542 | "exit"
1543 | "exp"
1544 | "fan"
1545 | "fan_cap"
1546 | "fc"
1547 | "fetch_url"
1548 | "d"
1549 | "dirs"
1550 | "dr"
1551 | "f"
1552 | "fi"
1553 | "files"
1554 | "filesf"
1555 | "filter"
1556 | "fr"
1557 | "getcwd"
1558 | "glob_par"
1559 | "par_sed"
1560 | "glob"
1561 | "grep"
1562 | "greps"
1563 | "heap"
1564 | "hex"
1565 | "inc"
1566 | "index"
1567 | "int"
1568 | "join"
1569 | "keys"
1570 | "lcfirst"
1571 | "lc"
1572 | "length"
1573 | "link"
1574 | "log"
1575 | "lstat"
1576 | "map"
1577 | "flat_map"
1578 | "maps"
1579 | "flat_maps"
1580 | "flatten"
1581 | "frequencies"
1582 | "freq"
1583 | "interleave"
1584 | "ddump"
1585 | "stringify"
1586 | "str"
1587 | "s"
1588 | "input"
1589 | "lines"
1590 | "words"
1591 | "chars"
1592 | "digits"
1593 | "letters"
1594 | "letters_uc"
1595 | "letters_lc"
1596 | "punctuation"
1597 | "sentences"
1598 | "paragraphs"
1599 | "sections"
1600 | "numbers"
1601 | "graphemes"
1602 | "columns"
1603 | "trim"
1604 | "avg"
1605 | "top"
1606 | "pager"
1607 | "pg"
1608 | "less"
1609 | "count_by"
1610 | "to_file"
1611 | "to_json"
1612 | "to_csv"
1613 | "grep_v"
1614 | "select_keys"
1615 | "pluck"
1616 | "clamp"
1617 | "normalize"
1618 | "stddev"
1619 | "squared"
1620 | "square"
1621 | "cubed"
1622 | "cube"
1623 | "expt"
1624 | "pow"
1625 | "pw"
1626 | "snake_case"
1627 | "camel_case"
1628 | "kebab_case"
1629 | "to_toml"
1630 | "to_yaml"
1631 | "to_xml"
1632 | "to_html"
1633 | "to_markdown"
1634 | "xopen"
1635 | "clip"
1636 | "paste"
1637 | "to_table"
1638 | "sparkline"
1639 | "bar_chart"
1640 | "flame"
1641 | "set"
1642 | "list_count"
1643 | "list_size"
1644 | "count"
1645 | "size"
1646 | "cnt"
1647 | "len"
1648 | "all"
1649 | "any"
1650 | "none"
1651 | "take_while"
1652 | "drop_while"
1653 | "skip_while"
1654 | "skip"
1655 | "first_or"
1656 | "tap"
1657 | "peek"
1658 | "partition"
1659 | "min_by"
1660 | "max_by"
1661 | "zip_with"
1662 | "group_by"
1663 | "chunk_by"
1664 | "with_index"
1665 | "puniq"
1666 | "pfirst"
1667 | "pany"
1668 | "uniq"
1669 | "distinct"
1670 | "shuffle"
1671 | "shuffled"
1672 | "chunked"
1673 | "windowed"
1674 | "match"
1675 | "mkdir"
1676 | "every"
1677 | "gen"
1678 | "oct"
1679 | "open"
1680 | "p"
1681 | "opendir"
1682 | "ord"
1683 | "par_lines"
1684 | "par_walk"
1685 | "pipe"
1686 | "pipes"
1687 | "block_devices"
1688 | "char_devices"
1689 | "exe"
1690 | "executables"
1691 | "rate_limit"
1692 | "retry"
1693 | "pcache"
1694 | "pchannel"
1695 | "pfor"
1696 | "pgrep"
1697 | "pgreps"
1698 | "pipeline"
1699 | "pmap_chunked"
1700 | "pmap_reduce"
1701 | "pmap_on"
1702 | "pflat_map_on"
1703 | "pmap"
1704 | "pmaps"
1705 | "pflat_map"
1706 | "pflat_maps"
1707 | "pop"
1708 | "pos"
1709 | "ppool"
1710 | "preduce_init"
1711 | "preduce"
1712 | "pselect"
1713 | "printf"
1714 | "print"
1715 | "pr"
1716 | "psort"
1717 | "push"
1718 | "pwatch"
1719 | "rand"
1720 | "readdir"
1721 | "readlink"
1722 | "reduce"
1723 | "fold"
1724 | "inject"
1725 | "first"
1726 | "detect"
1727 | "find"
1728 | "find_all"
1729 | "ref"
1730 | "rename"
1731 | "require"
1732 | "rev"
1733 | "reverse"
1734 | "reversed"
1735 | "rewinddir"
1736 | "rindex"
1737 | "rmdir"
1738 | "rm"
1739 | "say"
1740 | "scalar"
1741 | "seekdir"
1742 | "shift"
1743 | "sin"
1744 | "slurp"
1745 | "sockets"
1746 | "sort"
1747 | "splice"
1748 | "splice_last"
1749 | "splice1"
1750 | "spl_last"
1751 | "split"
1752 | "sprintf"
1753 | "sqrt"
1754 | "srand"
1755 | "stat"
1756 | "study"
1757 | "substr"
1758 | "symlink"
1759 | "sym_links"
1760 | "system"
1761 | "telldir"
1762 | "timer"
1763 | "trace"
1764 | "ucfirst"
1765 | "uc"
1766 | "undef"
1767 | "umask"
1768 | "unlink"
1769 | "unshift"
1770 | "utime"
1771 | "values"
1772 | "wantarray"
1773 | "warn"
1774 | "watch"
1775 | "yield"
1776 | "sub"
1777 )
1778 }
1779
1780 fn parse_block(&mut self) -> PerlResult<Block> {
1781 self.expect(&Token::LBrace)?;
1782 let saved_pipe_rhs_depth = self.pipe_rhs_depth;
1785 self.pipe_rhs_depth = 0;
1786 self.block_depth += 1;
1787 let mut stmts = Vec::new();
1788 if let Some(param_stmts) = self.try_parse_block_params()? {
1792 stmts.extend(param_stmts);
1793 }
1794 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1795 if self.eat(&Token::Semicolon) {
1796 continue;
1797 }
1798 stmts.push(self.parse_statement()?);
1799 }
1800 self.expect(&Token::RBrace)?;
1801 self.pipe_rhs_depth = saved_pipe_rhs_depth;
1802 self.block_depth -= 1;
1803 Self::default_topic_for_sole_bareword(&mut stmts);
1804 Ok(stmts)
1805 }
1806
1807 fn try_parse_block_params(&mut self) -> PerlResult<Option<Vec<Statement>>> {
1812 if !matches!(self.peek(), Token::BitOr) {
1813 return Ok(None);
1814 }
1815 let mut i = 1; loop {
1818 match self.peek_at(i) {
1819 Token::ScalarVar(_) => i += 1,
1820 _ => return Ok(None), }
1822 match self.peek_at(i) {
1823 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
1827 }
1828 let line = self.peek_line();
1830 self.advance(); let mut names = Vec::new();
1832 loop {
1833 if let Token::ScalarVar(ref name) = self.peek().clone() {
1834 names.push(name.clone());
1835 self.advance();
1836 }
1837 if self.eat(&Token::BitOr) {
1838 break;
1839 }
1840 self.expect(&Token::Comma)?;
1841 }
1842 let sources: Vec<&str> = match names.len() {
1847 1 => vec!["_"],
1848 2 => vec!["a", "b"],
1849 n => {
1850 let _ = n;
1852 vec![] }
1854 };
1855 let mut stmts = Vec::with_capacity(names.len());
1856 if !sources.is_empty() {
1857 for (name, src) in names.iter().zip(sources.iter()) {
1858 stmts.push(Statement {
1859 label: None,
1860 kind: StmtKind::My(vec![VarDecl {
1861 sigil: Sigil::Scalar,
1862 name: name.clone(),
1863 initializer: Some(Expr {
1864 kind: ExprKind::ScalarVar(src.to_string()),
1865 line,
1866 }),
1867 frozen: false,
1868 type_annotation: None,
1869 }]),
1870 line,
1871 });
1872 }
1873 } else {
1874 for (idx, name) in names.iter().enumerate() {
1876 let src = if idx == 0 {
1877 "_".to_string()
1878 } else {
1879 format!("_{idx}")
1880 };
1881 stmts.push(Statement {
1882 label: None,
1883 kind: StmtKind::My(vec![VarDecl {
1884 sigil: Sigil::Scalar,
1885 name: name.clone(),
1886 initializer: Some(Expr {
1887 kind: ExprKind::ScalarVar(src),
1888 line,
1889 }),
1890 frozen: false,
1891 type_annotation: None,
1892 }]),
1893 line,
1894 });
1895 }
1896 }
1897 Ok(Some(stmts))
1898 }
1899
1900 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
1915 let [only] = stmts else { return };
1916 let StmtKind::Expression(ref mut expr) = only.kind else {
1917 return;
1918 };
1919 let topic_line = expr.line;
1920 let topic_arg = || Expr {
1921 kind: ExprKind::ScalarVar("_".to_string()),
1922 line: topic_line,
1923 };
1924 match expr.kind {
1925 ExprKind::FuncCall {
1927 ref name,
1928 ref mut args,
1929 } if args.is_empty()
1930 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1931 {
1932 args.push(topic_arg());
1933 }
1934 ExprKind::Bareword(ref name)
1938 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1939 {
1940 let n = name.clone();
1941 expr.kind = ExprKind::FuncCall {
1942 name: n,
1943 args: vec![topic_arg()],
1944 };
1945 }
1946 _ => {}
1947 }
1948 }
1949
1950 fn parse_defer_stmt(&mut self) -> PerlResult<Statement> {
1954 let line = self.peek_line();
1955 self.advance(); let body = self.parse_block()?;
1957 self.eat(&Token::Semicolon);
1958 let coderef = Expr {
1960 kind: ExprKind::CodeRef {
1961 params: vec![],
1962 body,
1963 },
1964 line,
1965 };
1966 Ok(Statement {
1967 label: None,
1968 kind: StmtKind::Expression(Expr {
1969 kind: ExprKind::FuncCall {
1970 name: "defer__internal".to_string(),
1971 args: vec![coderef],
1972 },
1973 line,
1974 }),
1975 line,
1976 })
1977 }
1978
1979 fn parse_try_catch(&mut self) -> PerlResult<Statement> {
1981 let line = self.peek_line();
1982 self.advance(); let try_block = self.parse_block()?;
1984 match self.peek() {
1985 Token::Ident(ref k) if k == "catch" => {
1986 self.advance();
1987 }
1988 _ => {
1989 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
1990 }
1991 }
1992 self.expect(&Token::LParen)?;
1993 let catch_var = self.parse_scalar_var_name()?;
1994 self.expect(&Token::RParen)?;
1995 let catch_block = self.parse_block()?;
1996 let finally_block = match self.peek() {
1997 Token::Ident(ref k) if k == "finally" => {
1998 self.advance();
1999 Some(self.parse_block()?)
2000 }
2001 _ => None,
2002 };
2003 self.eat(&Token::Semicolon);
2004 Ok(Statement {
2005 label: None,
2006 kind: StmtKind::TryCatch {
2007 try_block,
2008 catch_var,
2009 catch_block,
2010 finally_block,
2011 },
2012 line,
2013 })
2014 }
2015
2016 fn parse_thread_macro(&mut self, _line: usize, thread_last: bool) -> PerlResult<Expr> {
2030 let saved_thread_last = self.thread_last_mode;
2032 self.thread_last_mode = thread_last;
2033
2034 let pipe_rhs_wrap = self.in_pipe_rhs();
2035 let mut result = if pipe_rhs_wrap {
2036 Expr {
2037 kind: ExprKind::ArrayElement {
2038 array: "_".to_string(),
2039 index: Box::new(Expr {
2040 kind: ExprKind::Integer(0),
2041 line: _line,
2042 }),
2043 },
2044 line: _line,
2045 }
2046 } else {
2047 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
2050 let expr = self.parse_thread_input();
2051 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
2052 expr?
2053 };
2054
2055 let mut last_stage_end_line = self.prev_line();
2057
2058 loop {
2060 if self.peek_line() > last_stage_end_line {
2065 break;
2066 }
2067
2068 match self.peek() {
2072 Token::Semicolon
2073 | Token::RBrace
2074 | Token::RParen
2075 | Token::RBracket
2076 | Token::PipeForward
2077 | Token::Eof
2078 | Token::ScalarVar(_)
2079 | Token::ArrayVar(_)
2080 | Token::HashVar(_)
2081 | Token::Comma => break,
2082 Token::Ident(ref kw)
2083 if matches!(
2084 kw.as_str(),
2085 "my" | "our"
2086 | "local"
2087 | "state"
2088 | "if"
2089 | "unless"
2090 | "while"
2091 | "until"
2092 | "for"
2093 | "foreach"
2094 | "return"
2095 | "last"
2096 | "next"
2097 | "redo"
2098 ) =>
2099 {
2100 break
2101 }
2102 _ => {}
2103 }
2104
2105 let stage_line = self.peek_line();
2106
2107 match self.peek().clone() {
2109 Token::ArrowBrace => {
2111 self.advance(); let mut stmts = Vec::new();
2113 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2114 if self.eat(&Token::Semicolon) {
2115 continue;
2116 }
2117 stmts.push(self.parse_statement()?);
2118 }
2119 self.expect(&Token::RBrace)?;
2120 let code_ref = Expr {
2121 kind: ExprKind::CodeRef {
2122 params: vec![],
2123 body: stmts,
2124 },
2125 line: stage_line,
2126 };
2127 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2128 }
2129 Token::Ident(ref name) if name == "sub" => {
2131 if crate::no_interop_mode() {
2132 return Err(self.syntax_err(
2133 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
2134 stage_line,
2135 ));
2136 }
2137 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2139 let body = self.parse_block()?;
2140 let code_ref = Expr {
2141 kind: ExprKind::CodeRef { params, body },
2142 line: stage_line,
2143 };
2144 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2145 }
2146 Token::Ident(ref name) if name == "fn" => {
2148 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2150 self.parse_sub_attributes()?;
2151 let body = self.parse_fn_eq_body_or_block(false)?;
2152 let code_ref = Expr {
2153 kind: ExprKind::CodeRef { params, body },
2154 line: stage_line,
2155 };
2156 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2157 }
2158 Token::Ident(ref name) => {
2160 let mut func_name = name.clone();
2161 self.advance();
2162
2163 while matches!(self.peek(), Token::PackageSep) {
2165 self.advance(); if let Token::Ident(ref part) = self.peek().clone() {
2167 func_name.push_str("::");
2168 func_name.push_str(part);
2169 self.advance();
2170 } else {
2171 return Err(self.syntax_err(
2172 format!(
2173 "Expected identifier after `::` in thread stage, got {:?}",
2174 self.peek()
2175 ),
2176 stage_line,
2177 ));
2178 }
2179 }
2180
2181 if func_name.starts_with('\x00') {
2183 let parts: Vec<&str> = func_name.split('\x00').collect();
2184 if parts.len() >= 4 && parts[1] == "s" {
2185 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2186 let stage = Expr {
2187 kind: ExprKind::Substitution {
2188 expr: Box::new(result.clone()),
2189 pattern: parts[2].to_string(),
2190 replacement: parts[3].to_string(),
2191 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2192 delim,
2193 },
2194 line: stage_line,
2195 };
2196 result = stage;
2197 last_stage_end_line = self.prev_line();
2198 continue;
2199 }
2200 if parts.len() >= 4 && parts[1] == "tr" {
2201 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2202 let stage = Expr {
2203 kind: ExprKind::Transliterate {
2204 expr: Box::new(result.clone()),
2205 from: parts[2].to_string(),
2206 to: parts[3].to_string(),
2207 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2208 delim,
2209 },
2210 line: stage_line,
2211 };
2212 result = stage;
2213 last_stage_end_line = self.prev_line();
2214 continue;
2215 }
2216 return Err(
2217 self.syntax_err("Unexpected encoded token in thread", stage_line)
2218 );
2219 }
2220
2221 if matches!(self.peek(), Token::Plus)
2226 && matches!(self.peek_at(1), Token::LBrace)
2227 {
2228 self.advance(); self.expect(&Token::LBrace)?;
2230 let pairs = self.try_parse_hash_ref()?;
2232 let hashref_expr = Expr {
2233 kind: ExprKind::HashRef(pairs),
2234 line: stage_line,
2235 };
2236 let flatten_array_refs =
2237 matches!(func_name.as_str(), "flat_map" | "flat_maps");
2238 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
2239 let placeholder = Expr {
2241 kind: ExprKind::Undef,
2242 line: stage_line,
2243 };
2244 let map_node = Expr {
2245 kind: ExprKind::MapExprComma {
2246 expr: Box::new(hashref_expr),
2247 list: Box::new(placeholder),
2248 flatten_array_refs,
2249 stream,
2250 },
2251 line: stage_line,
2252 };
2253 result = self.pipe_forward_apply(result, map_node, stage_line)?;
2254 } else if func_name == "pmap_chunked" {
2256 let chunk_size = self.parse_assign_expr()?;
2257 let block = self.parse_block_or_bareword_block()?;
2258 let placeholder = self.pipe_placeholder_list(stage_line);
2259 let stage = Expr {
2260 kind: ExprKind::PMapChunkedExpr {
2261 chunk_size: Box::new(chunk_size),
2262 block,
2263 list: Box::new(placeholder),
2264 progress: None,
2265 },
2266 line: stage_line,
2267 };
2268 result = self.pipe_forward_apply(result, stage, stage_line)?;
2269 } else if func_name == "preduce_init" {
2271 let init = self.parse_assign_expr()?;
2272 let block = self.parse_block_or_bareword_block()?;
2273 let placeholder = self.pipe_placeholder_list(stage_line);
2274 let stage = Expr {
2275 kind: ExprKind::PReduceInitExpr {
2276 init: Box::new(init),
2277 block,
2278 list: Box::new(placeholder),
2279 progress: None,
2280 },
2281 line: stage_line,
2282 };
2283 result = self.pipe_forward_apply(result, stage, stage_line)?;
2284 } else if func_name == "pmap_reduce" {
2286 let map_block = self.parse_block_or_bareword_block()?;
2287 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2288 self.parse_block()?
2289 } else {
2290 self.expect(&Token::Comma)?;
2291 self.parse_block_or_bareword_cmp_block()?
2292 };
2293 let placeholder = self.pipe_placeholder_list(stage_line);
2294 let stage = Expr {
2295 kind: ExprKind::PMapReduceExpr {
2296 map_block,
2297 reduce_block,
2298 list: Box::new(placeholder),
2299 progress: None,
2300 },
2301 line: stage_line,
2302 };
2303 result = self.pipe_forward_apply(result, stage, stage_line)?;
2304 } else if func_name == "pmap_on" || func_name == "pflat_map_on" {
2309 self.suppress_scalar_hash_brace =
2312 self.suppress_scalar_hash_brace.saturating_add(1);
2313 let cluster = self.parse_assign_expr();
2314 self.suppress_scalar_hash_brace =
2315 self.suppress_scalar_hash_brace.saturating_sub(1);
2316 let cluster = cluster?;
2317 self.eat(&Token::Comma);
2320 let block = self.parse_block_or_bareword_block()?;
2321 let placeholder = self.pipe_placeholder_list(stage_line);
2322 let stage = Expr {
2323 kind: ExprKind::PMapExpr {
2324 block,
2325 list: Box::new(placeholder),
2326 progress: None,
2327 flat_outputs: func_name == "pflat_map_on",
2328 on_cluster: Some(Box::new(cluster)),
2329 stream: false,
2330 },
2331 line: stage_line,
2332 };
2333 result = self.pipe_forward_apply(result, stage, stage_line)?;
2334 } else if matches!(self.peek(), Token::LBrace) {
2336 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
2338 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
2339 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
2340 result = self.pipe_forward_apply(result, stage, stage_line)?;
2341 } else if matches!(self.peek(), Token::LParen) {
2342 if func_name == "join" {
2345 self.advance(); let separator = self.parse_assign_expr()?;
2347 self.expect(&Token::RParen)?;
2348 let placeholder = self.pipe_placeholder_list(stage_line);
2349 let stage = Expr {
2350 kind: ExprKind::JoinExpr {
2351 separator: Box::new(separator),
2352 list: Box::new(placeholder),
2353 },
2354 line: stage_line,
2355 };
2356 result = self.pipe_forward_apply(result, stage, stage_line)?;
2357 } else if func_name == "split" {
2358 self.advance(); let pattern = self.parse_assign_expr()?;
2360 let limit = if self.eat(&Token::Comma) {
2361 Some(Box::new(self.parse_assign_expr()?))
2362 } else {
2363 None
2364 };
2365 self.expect(&Token::RParen)?;
2366 let placeholder = Expr {
2367 kind: ExprKind::ScalarVar("_".to_string()),
2368 line: stage_line,
2369 };
2370 let stage = Expr {
2371 kind: ExprKind::SplitExpr {
2372 pattern: Box::new(pattern),
2373 string: Box::new(placeholder),
2374 limit,
2375 },
2376 line: stage_line,
2377 };
2378 result = self.pipe_forward_apply(result, stage, stage_line)?;
2379 } else {
2380 self.advance(); let mut call_args = Vec::new();
2391 while !matches!(self.peek(), Token::RParen | Token::Eof) {
2392 call_args.push(self.parse_assign_expr()?);
2393 if !self.eat(&Token::Comma) {
2394 break;
2395 }
2396 }
2397 self.expect(&Token::RParen)?;
2398 if !call_args.iter().any(Self::expr_contains_topic_var) {
2402 let topic = Expr {
2403 kind: ExprKind::ScalarVar("_".to_string()),
2404 line: stage_line,
2405 };
2406 if self.thread_last_mode {
2407 call_args.push(topic);
2408 } else {
2409 call_args.insert(0, topic);
2410 }
2411 }
2412 let call_expr = Expr {
2413 kind: ExprKind::FuncCall {
2414 name: func_name.clone(),
2415 args: call_args,
2416 },
2417 line: stage_line,
2418 };
2419 let code_ref = Expr {
2420 kind: ExprKind::CodeRef {
2421 params: vec![],
2422 body: vec![Statement {
2423 label: None,
2424 kind: StmtKind::Expression(call_expr),
2425 line: stage_line,
2426 }],
2427 },
2428 line: stage_line,
2429 };
2430 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2431 }
2432 } else {
2433 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
2435 }
2436 }
2437 Token::Regex(ref pattern, ref flags, delim) => {
2439 let pattern = pattern.clone();
2440 let flags = flags.clone();
2441 self.advance();
2442 result =
2443 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
2444 }
2445 Token::Slash => {
2448 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
2454 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
2455 && matches!(self.peek_at(1), Token::Regex(..))
2456 {
2457 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
2464 self.peek().clone()
2465 {
2466 let _ = (misparsed_pattern, misparsed_flags);
2476 }
2477 }
2478 }
2479
2480 let mut pattern = String::new();
2482 loop {
2483 match self.peek().clone() {
2484 Token::Slash => {
2485 self.advance(); break;
2487 }
2488 Token::Eof | Token::Semicolon | Token::Newline => {
2489 return Err(self
2490 .syntax_err("Unterminated regex in thread stage", stage_line));
2491 }
2492 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
2494 if pattern.is_empty()
2502 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
2503 {
2504 let _ = (inner_pattern, inner_flags, delim);
2510 }
2511 return Err(self.syntax_err(
2513 "Complex regex in thread stage - use m/pattern/ syntax instead",
2514 stage_line,
2515 ));
2516 }
2517 Token::Ident(ref s) => {
2518 pattern.push_str(s);
2519 self.advance();
2520 }
2521 Token::Integer(n) => {
2522 pattern.push_str(&n.to_string());
2523 self.advance();
2524 }
2525 Token::ScalarVar(ref v) => {
2526 pattern.push('$');
2527 pattern.push_str(v);
2528 self.advance();
2529 }
2530 Token::Dot => {
2531 pattern.push('.');
2532 self.advance();
2533 }
2534 Token::Star => {
2535 pattern.push('*');
2536 self.advance();
2537 }
2538 Token::Plus => {
2539 pattern.push('+');
2540 self.advance();
2541 }
2542 Token::Question => {
2543 pattern.push('?');
2544 self.advance();
2545 }
2546 Token::LParen => {
2547 pattern.push('(');
2548 self.advance();
2549 }
2550 Token::RParen => {
2551 pattern.push(')');
2552 self.advance();
2553 }
2554 Token::LBracket => {
2555 pattern.push('[');
2556 self.advance();
2557 }
2558 Token::RBracket => {
2559 pattern.push(']');
2560 self.advance();
2561 }
2562 Token::Backslash => {
2563 pattern.push('\\');
2564 self.advance();
2565 }
2566 Token::BitOr => {
2567 pattern.push('|');
2568 self.advance();
2569 }
2570 Token::Power => {
2571 pattern.push_str("**");
2572 self.advance();
2573 }
2574 Token::BitXor => {
2575 pattern.push('^');
2576 self.advance();
2577 }
2578 Token::Minus => {
2579 pattern.push('-');
2580 self.advance();
2581 }
2582 _ => {
2583 return Err(self.syntax_err(
2584 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2585 stage_line,
2586 ));
2587 }
2588 }
2589 }
2590 let mut flags = String::new();
2594 if let Token::Ident(ref s) = self.peek().clone() {
2595 let is_flag_only =
2596 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2597 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2598 if is_flag_only && !followed_by_brace {
2599 flags.push_str(s);
2600 self.advance();
2601 }
2602 }
2603 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2604 }
2605 tok => {
2606 return Err(self.syntax_err(
2607 format!(
2608 "thread: expected stage (ident, fn {{}}, s///, tr///, or /re/), got {:?}",
2609 tok
2610 ),
2611 stage_line,
2612 ));
2613 }
2614 };
2615 last_stage_end_line = self.prev_line();
2616 }
2617
2618 self.thread_last_mode = saved_thread_last;
2620
2621 if pipe_rhs_wrap {
2622 let body_line = result.line;
2625 return Ok(Expr {
2626 kind: ExprKind::CodeRef {
2627 params: vec![],
2628 body: vec![Statement {
2629 label: None,
2630 kind: StmtKind::Expression(result),
2631 line: body_line,
2632 }],
2633 },
2634 line: _line,
2635 });
2636 }
2637 Ok(result)
2638 }
2639
2640 fn thread_regex_grep_stage(
2642 &self,
2643 list: Expr,
2644 pattern: String,
2645 flags: String,
2646 delim: char,
2647 line: usize,
2648 ) -> Expr {
2649 let topic = Expr {
2650 kind: ExprKind::ScalarVar("_".to_string()),
2651 line,
2652 };
2653 let match_expr = Expr {
2654 kind: ExprKind::Match {
2655 expr: Box::new(topic),
2656 pattern,
2657 flags,
2658 scalar_g: false,
2659 delim,
2660 },
2661 line,
2662 };
2663 let block = vec![Statement {
2664 label: None,
2665 kind: StmtKind::Expression(match_expr),
2666 line,
2667 }];
2668 Expr {
2669 kind: ExprKind::GrepExpr {
2670 block,
2671 list: Box::new(list),
2672 keyword: crate::ast::GrepBuiltinKeyword::Grep,
2673 },
2674 line,
2675 }
2676 }
2677
2678 fn expr_contains_topic_var(e: &Expr) -> bool {
2689 format!("{:?}", e).contains("ScalarVar(\"_\")")
2690 }
2691
2692 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> PerlResult<Expr> {
2694 let kind = match name {
2695 "uc" => ExprKind::Uc(Box::new(arg)),
2697 "lc" => ExprKind::Lc(Box::new(arg)),
2698 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
2699 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
2700 "fc" => ExprKind::Fc(Box::new(arg)),
2701 "chomp" => ExprKind::Chomp(Box::new(arg)),
2702 "chop" => ExprKind::Chop(Box::new(arg)),
2703 "length" => ExprKind::Length(Box::new(arg)),
2704 "len" | "cnt" => ExprKind::FuncCall {
2705 name: "count".to_string(),
2706 args: vec![arg],
2707 },
2708 "quotemeta" | "qm" => ExprKind::FuncCall {
2709 name: "quotemeta".to_string(),
2710 args: vec![arg],
2711 },
2712 "abs" => ExprKind::Abs(Box::new(arg)),
2714 "int" => ExprKind::Int(Box::new(arg)),
2715 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
2716 "sin" => ExprKind::Sin(Box::new(arg)),
2717 "cos" => ExprKind::Cos(Box::new(arg)),
2718 "exp" => ExprKind::Exp(Box::new(arg)),
2719 "log" => ExprKind::Log(Box::new(arg)),
2720 "hex" => ExprKind::Hex(Box::new(arg)),
2721 "oct" => ExprKind::Oct(Box::new(arg)),
2722 "chr" => ExprKind::Chr(Box::new(arg)),
2723 "ord" => ExprKind::Ord(Box::new(arg)),
2724 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
2726 "ref" => ExprKind::Ref(Box::new(arg)),
2727 "scalar" => {
2728 if crate::no_interop_mode() {
2729 return Err(self.syntax_err(
2730 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
2731 line,
2732 ));
2733 }
2734 ExprKind::ScalarContext(Box::new(arg))
2735 }
2736 "keys" => ExprKind::Keys(Box::new(arg)),
2738 "values" => ExprKind::Values(Box::new(arg)),
2739 "each" => ExprKind::Each(Box::new(arg)),
2740 "pop" => ExprKind::Pop(Box::new(arg)),
2741 "shift" => ExprKind::Shift(Box::new(arg)),
2742 "reverse" => {
2743 if crate::no_interop_mode() {
2744 return Err(self.syntax_err(
2745 "stryke uses `rev` instead of `reverse` (--no-interop)",
2746 line,
2747 ));
2748 }
2749 ExprKind::ReverseExpr(Box::new(arg))
2750 }
2751 "reversed" | "rv" | "rev" => ExprKind::Rev(Box::new(arg)),
2752 "sort" | "so" => ExprKind::SortExpr {
2753 cmp: None,
2754 list: Box::new(arg),
2755 },
2756 "psort" => ExprKind::PSortExpr {
2757 cmp: None,
2758 list: Box::new(arg),
2759 progress: None,
2760 },
2761 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
2762 name: "uniq".to_string(),
2763 args: vec![arg],
2764 },
2765 "trim" | "tm" => ExprKind::FuncCall {
2766 name: "trim".to_string(),
2767 args: vec![arg],
2768 },
2769 "flatten" | "fl" => ExprKind::FuncCall {
2770 name: "flatten".to_string(),
2771 args: vec![arg],
2772 },
2773 "compact" | "cpt" => ExprKind::FuncCall {
2774 name: "compact".to_string(),
2775 args: vec![arg],
2776 },
2777 "shuffle" | "shuf" => ExprKind::FuncCall {
2778 name: "shuffle".to_string(),
2779 args: vec![arg],
2780 },
2781 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
2782 name: "frequencies".to_string(),
2783 args: vec![arg],
2784 },
2785 "dedup" | "dup" => ExprKind::FuncCall {
2786 name: "dedup".to_string(),
2787 args: vec![arg],
2788 },
2789 "enumerate" | "en" => ExprKind::FuncCall {
2790 name: "enumerate".to_string(),
2791 args: vec![arg],
2792 },
2793 "lines" | "ln" => ExprKind::FuncCall {
2794 name: "lines".to_string(),
2795 args: vec![arg],
2796 },
2797 "words" | "wd" => ExprKind::FuncCall {
2798 name: "words".to_string(),
2799 args: vec![arg],
2800 },
2801 "chars" | "ch" => ExprKind::FuncCall {
2802 name: "chars".to_string(),
2803 args: vec![arg],
2804 },
2805 "digits" | "dg" => ExprKind::FuncCall {
2806 name: "digits".to_string(),
2807 args: vec![arg],
2808 },
2809 "letters" | "lts" => ExprKind::FuncCall {
2810 name: "letters".to_string(),
2811 args: vec![arg],
2812 },
2813 "letters_uc" => ExprKind::FuncCall {
2814 name: "letters_uc".to_string(),
2815 args: vec![arg],
2816 },
2817 "letters_lc" => ExprKind::FuncCall {
2818 name: "letters_lc".to_string(),
2819 args: vec![arg],
2820 },
2821 "punctuation" | "punct" => ExprKind::FuncCall {
2822 name: "punctuation".to_string(),
2823 args: vec![arg],
2824 },
2825 "sentences" | "sents" => ExprKind::FuncCall {
2826 name: "sentences".to_string(),
2827 args: vec![arg],
2828 },
2829 "paragraphs" | "paras" => ExprKind::FuncCall {
2830 name: "paragraphs".to_string(),
2831 args: vec![arg],
2832 },
2833 "sections" | "sects" => ExprKind::FuncCall {
2834 name: "sections".to_string(),
2835 args: vec![arg],
2836 },
2837 "numbers" | "nums" => ExprKind::FuncCall {
2838 name: "numbers".to_string(),
2839 args: vec![arg],
2840 },
2841 "graphemes" | "grs" => ExprKind::FuncCall {
2842 name: "graphemes".to_string(),
2843 args: vec![arg],
2844 },
2845 "columns" | "cols" => ExprKind::FuncCall {
2846 name: "columns".to_string(),
2847 args: vec![arg],
2848 },
2849 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
2851 "chdir" => ExprKind::Chdir(Box::new(arg)),
2852 "stat" => ExprKind::Stat(Box::new(arg)),
2853 "lstat" => ExprKind::Lstat(Box::new(arg)),
2854 "readlink" => ExprKind::Readlink(Box::new(arg)),
2855 "readdir" => ExprKind::Readdir(Box::new(arg)),
2856 "close" => ExprKind::Close(Box::new(arg)),
2857 "basename" | "bn" => ExprKind::FuncCall {
2858 name: "basename".to_string(),
2859 args: vec![arg],
2860 },
2861 "dirname" | "dn" => ExprKind::FuncCall {
2862 name: "dirname".to_string(),
2863 args: vec![arg],
2864 },
2865 "realpath" | "rp" => ExprKind::FuncCall {
2866 name: "realpath".to_string(),
2867 args: vec![arg],
2868 },
2869 "which" | "wh" => ExprKind::FuncCall {
2870 name: "which".to_string(),
2871 args: vec![arg],
2872 },
2873 "eval" => ExprKind::Eval(Box::new(arg)),
2875 "require" => ExprKind::Require(Box::new(arg)),
2876 "study" => ExprKind::Study(Box::new(arg)),
2877 "snake_case" | "sc" => ExprKind::FuncCall {
2879 name: "snake_case".to_string(),
2880 args: vec![arg],
2881 },
2882 "camel_case" | "cc" => ExprKind::FuncCall {
2883 name: "camel_case".to_string(),
2884 args: vec![arg],
2885 },
2886 "kebab_case" | "kc" => ExprKind::FuncCall {
2887 name: "kebab_case".to_string(),
2888 args: vec![arg],
2889 },
2890 "to_json" | "tj" => ExprKind::FuncCall {
2892 name: "to_json".to_string(),
2893 args: vec![arg],
2894 },
2895 "to_yaml" | "ty" => ExprKind::FuncCall {
2896 name: "to_yaml".to_string(),
2897 args: vec![arg],
2898 },
2899 "to_toml" | "tt" => ExprKind::FuncCall {
2900 name: "to_toml".to_string(),
2901 args: vec![arg],
2902 },
2903 "to_csv" | "tc" => ExprKind::FuncCall {
2904 name: "to_csv".to_string(),
2905 args: vec![arg],
2906 },
2907 "to_xml" | "tx" => ExprKind::FuncCall {
2908 name: "to_xml".to_string(),
2909 args: vec![arg],
2910 },
2911 "to_html" | "th" => ExprKind::FuncCall {
2912 name: "to_html".to_string(),
2913 args: vec![arg],
2914 },
2915 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
2916 name: "to_markdown".to_string(),
2917 args: vec![arg],
2918 },
2919 "xopen" | "xo" => ExprKind::FuncCall {
2920 name: "xopen".to_string(),
2921 args: vec![arg],
2922 },
2923 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
2924 name: "clip".to_string(),
2925 args: vec![arg],
2926 },
2927 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
2928 name: "to_table".to_string(),
2929 args: vec![arg],
2930 },
2931 "sparkline" | "spark" => ExprKind::FuncCall {
2932 name: "sparkline".to_string(),
2933 args: vec![arg],
2934 },
2935 "bar_chart" | "bars" => ExprKind::FuncCall {
2936 name: "bar_chart".to_string(),
2937 args: vec![arg],
2938 },
2939 "flame" | "flamechart" => ExprKind::FuncCall {
2940 name: "flame".to_string(),
2941 args: vec![arg],
2942 },
2943 "ddump" | "dd" => ExprKind::FuncCall {
2944 name: "ddump".to_string(),
2945 args: vec![arg],
2946 },
2947 "say" => {
2948 if crate::no_interop_mode() {
2949 return Err(
2950 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
2951 );
2952 }
2953 ExprKind::Say {
2954 handle: None,
2955 args: vec![arg],
2956 }
2957 }
2958 "p" => ExprKind::Say {
2959 handle: None,
2960 args: vec![arg],
2961 },
2962 "print" => ExprKind::Print {
2963 handle: None,
2964 args: vec![arg],
2965 },
2966 "warn" => ExprKind::Warn(vec![arg]),
2967 "die" => ExprKind::Die(vec![arg]),
2968 "stringify" | "str" => ExprKind::FuncCall {
2969 name: "stringify".to_string(),
2970 args: vec![arg],
2971 },
2972 "json_decode" | "jd" => ExprKind::FuncCall {
2973 name: "json_decode".to_string(),
2974 args: vec![arg],
2975 },
2976 "yaml_decode" | "yd" => ExprKind::FuncCall {
2977 name: "yaml_decode".to_string(),
2978 args: vec![arg],
2979 },
2980 "toml_decode" | "td" => ExprKind::FuncCall {
2981 name: "toml_decode".to_string(),
2982 args: vec![arg],
2983 },
2984 "xml_decode" | "xd" => ExprKind::FuncCall {
2985 name: "xml_decode".to_string(),
2986 args: vec![arg],
2987 },
2988 "json_encode" | "je" => ExprKind::FuncCall {
2989 name: "json_encode".to_string(),
2990 args: vec![arg],
2991 },
2992 "yaml_encode" | "ye" => ExprKind::FuncCall {
2993 name: "yaml_encode".to_string(),
2994 args: vec![arg],
2995 },
2996 "toml_encode" | "te" => ExprKind::FuncCall {
2997 name: "toml_encode".to_string(),
2998 args: vec![arg],
2999 },
3000 "xml_encode" | "xe" => ExprKind::FuncCall {
3001 name: "xml_encode".to_string(),
3002 args: vec![arg],
3003 },
3004 "base64_encode" | "b64e" => ExprKind::FuncCall {
3006 name: "base64_encode".to_string(),
3007 args: vec![arg],
3008 },
3009 "base64_decode" | "b64d" => ExprKind::FuncCall {
3010 name: "base64_decode".to_string(),
3011 args: vec![arg],
3012 },
3013 "hex_encode" | "hxe" => ExprKind::FuncCall {
3014 name: "hex_encode".to_string(),
3015 args: vec![arg],
3016 },
3017 "hex_decode" | "hxd" => ExprKind::FuncCall {
3018 name: "hex_decode".to_string(),
3019 args: vec![arg],
3020 },
3021 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
3022 name: "url_encode".to_string(),
3023 args: vec![arg],
3024 },
3025 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
3026 name: "url_decode".to_string(),
3027 args: vec![arg],
3028 },
3029 "gzip" | "gz" => ExprKind::FuncCall {
3030 name: "gzip".to_string(),
3031 args: vec![arg],
3032 },
3033 "gunzip" | "ugz" => ExprKind::FuncCall {
3034 name: "gunzip".to_string(),
3035 args: vec![arg],
3036 },
3037 "zstd" | "zst" => ExprKind::FuncCall {
3038 name: "zstd".to_string(),
3039 args: vec![arg],
3040 },
3041 "zstd_decode" | "uzst" => ExprKind::FuncCall {
3042 name: "zstd_decode".to_string(),
3043 args: vec![arg],
3044 },
3045 "sha256" | "s256" => ExprKind::FuncCall {
3047 name: "sha256".to_string(),
3048 args: vec![arg],
3049 },
3050 "sha1" | "s1" => ExprKind::FuncCall {
3051 name: "sha1".to_string(),
3052 args: vec![arg],
3053 },
3054 "md5" | "m5" => ExprKind::FuncCall {
3055 name: "md5".to_string(),
3056 args: vec![arg],
3057 },
3058 "uuid" | "uid" => ExprKind::FuncCall {
3059 name: "uuid".to_string(),
3060 args: vec![arg],
3061 },
3062 "datetime_utc" | "utc" => ExprKind::FuncCall {
3064 name: "datetime_utc".to_string(),
3065 args: vec![arg],
3066 },
3067 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
3070 block: vec![Statement {
3071 label: None,
3072 kind: StmtKind::Expression(Expr {
3073 kind: ExprKind::Say {
3074 handle: None,
3075 args: vec![Expr {
3076 kind: ExprKind::ScalarVar("_".into()),
3077 line,
3078 }],
3079 },
3080 line,
3081 }),
3082 line,
3083 }],
3084 list: Box::new(arg),
3085 },
3086 _ => ExprKind::FuncCall {
3088 name: name.to_string(),
3089 args: vec![arg],
3090 },
3091 };
3092 Ok(Expr { kind, line })
3093 }
3094
3095 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> PerlResult<Expr> {
3098 let block = self.parse_block()?;
3099 let placeholder = self.pipe_placeholder_list(line);
3101
3102 match name {
3103 "map" | "flat_map" | "maps" | "flat_maps" => {
3104 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
3105 let stream = matches!(name, "maps" | "flat_maps");
3106 Ok(Expr {
3107 kind: ExprKind::MapExpr {
3108 block,
3109 list: Box::new(placeholder),
3110 flatten_array_refs,
3111 stream,
3112 },
3113 line,
3114 })
3115 }
3116 "grep" | "greps" | "filter" | "fi" | "find_all" | "gr" => {
3117 let keyword = match name {
3118 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
3119 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
3120 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
3121 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
3122 _ => unreachable!(),
3123 };
3124 Ok(Expr {
3125 kind: ExprKind::GrepExpr {
3126 block,
3127 list: Box::new(placeholder),
3128 keyword,
3129 },
3130 line,
3131 })
3132 }
3133 "sort" | "so" => Ok(Expr {
3134 kind: ExprKind::SortExpr {
3135 cmp: Some(SortComparator::Block(block)),
3136 list: Box::new(placeholder),
3137 },
3138 line,
3139 }),
3140 "reduce" | "rd" => Ok(Expr {
3141 kind: ExprKind::ReduceExpr {
3142 block,
3143 list: Box::new(placeholder),
3144 },
3145 line,
3146 }),
3147 "fore" | "e" | "ep" => Ok(Expr {
3148 kind: ExprKind::ForEachExpr {
3149 block,
3150 list: Box::new(placeholder),
3151 },
3152 line,
3153 }),
3154 "pmap" | "pflat_map" | "pmaps" | "pflat_maps" => Ok(Expr {
3155 kind: ExprKind::PMapExpr {
3156 block,
3157 list: Box::new(placeholder),
3158 progress: None,
3159 flat_outputs: name == "pflat_map" || name == "pflat_maps",
3160 on_cluster: None,
3161 stream: name == "pmaps" || name == "pflat_maps",
3162 },
3163 line,
3164 }),
3165 "pgrep" | "pgreps" => Ok(Expr {
3166 kind: ExprKind::PGrepExpr {
3167 block,
3168 list: Box::new(placeholder),
3169 progress: None,
3170 stream: name == "pgreps",
3171 },
3172 line,
3173 }),
3174 "pfor" => Ok(Expr {
3175 kind: ExprKind::PForExpr {
3176 block,
3177 list: Box::new(placeholder),
3178 progress: None,
3179 },
3180 line,
3181 }),
3182 "preduce" => Ok(Expr {
3183 kind: ExprKind::PReduceExpr {
3184 block,
3185 list: Box::new(placeholder),
3186 progress: None,
3187 },
3188 line,
3189 }),
3190 "pcache" => Ok(Expr {
3191 kind: ExprKind::PcacheExpr {
3192 block,
3193 list: Box::new(placeholder),
3194 progress: None,
3195 },
3196 line,
3197 }),
3198 "psort" => Ok(Expr {
3199 kind: ExprKind::PSortExpr {
3200 cmp: Some(block),
3201 list: Box::new(placeholder),
3202 progress: None,
3203 },
3204 line,
3205 }),
3206 _ => {
3207 let code_ref = Expr {
3214 kind: ExprKind::CodeRef {
3215 params: vec![],
3216 body: block,
3217 },
3218 line,
3219 };
3220 let args = if Self::is_block_then_list_pipe_builtin(name) {
3221 vec![code_ref, placeholder]
3222 } else {
3223 vec![code_ref]
3224 };
3225 Ok(Expr {
3226 kind: ExprKind::FuncCall {
3227 name: name.to_string(),
3228 args,
3229 },
3230 line,
3231 })
3232 }
3233 }
3234 }
3235
3236 fn parse_tie_stmt(&mut self) -> PerlResult<Statement> {
3238 let line = self.peek_line();
3239 self.advance(); let mut implicit_decl: Option<Statement> = None;
3246 if let Token::Ident(kw) = self.peek().clone() {
3247 if matches!(kw.as_str(), "my" | "our") {
3248 let kw_line = self.peek_line();
3249 self.advance(); let (decl_sigil, decl_name) = match self.peek().clone() {
3252 Token::ScalarVar(s) => (Sigil::Scalar, s),
3253 Token::ArrayVar(a) => (Sigil::Array, a),
3254 Token::HashVar(h) => (Sigil::Hash, h),
3255 tok => {
3256 return Err(self.syntax_err(
3257 format!("expected variable after `tie {}`, got {:?}", kw, tok),
3258 self.peek_line(),
3259 ));
3260 }
3261 };
3262 let decls = vec![VarDecl {
3263 sigil: decl_sigil,
3264 name: decl_name.clone(),
3265 initializer: None,
3266 frozen: false,
3267 type_annotation: None,
3268 }];
3269 implicit_decl = Some(Statement {
3270 label: None,
3271 kind: if kw == "my" {
3272 StmtKind::My(decls)
3273 } else {
3274 StmtKind::Our(decls)
3275 },
3276 line: kw_line,
3277 });
3278 }
3283 }
3284 let target = match self.peek().clone() {
3285 Token::HashVar(h) => {
3286 self.advance();
3287 TieTarget::Hash(h)
3288 }
3289 Token::ArrayVar(a) => {
3290 self.advance();
3291 TieTarget::Array(a)
3292 }
3293 Token::ScalarVar(s) => {
3294 self.advance();
3295 TieTarget::Scalar(s)
3296 }
3297 tok => {
3298 return Err(self.syntax_err(
3299 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
3300 self.peek_line(),
3301 ));
3302 }
3303 };
3304 self.expect(&Token::Comma)?;
3305 let class = self.parse_assign_expr()?;
3306 let mut args = Vec::new();
3307 while self.eat(&Token::Comma) {
3308 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
3309 break;
3310 }
3311 args.push(self.parse_assign_expr()?);
3312 }
3313 self.eat(&Token::Semicolon);
3314 let tie_stmt = Statement {
3315 label: None,
3316 kind: StmtKind::Tie {
3317 target,
3318 class,
3319 args,
3320 },
3321 line,
3322 };
3323 if let Some(decl) = implicit_decl {
3324 Ok(Statement {
3329 label: None,
3330 kind: StmtKind::StmtGroup(vec![decl, tie_stmt]),
3331 line,
3332 })
3333 } else {
3334 Ok(tie_stmt)
3335 }
3336 }
3337
3338 fn parse_given(&mut self) -> PerlResult<Statement> {
3340 let line = self.peek_line();
3341 self.advance();
3342 self.expect(&Token::LParen)?;
3343 let topic = self.parse_expression()?;
3344 self.expect(&Token::RParen)?;
3345 let body = self.parse_block()?;
3346 self.eat(&Token::Semicolon);
3347 Ok(Statement {
3348 label: None,
3349 kind: StmtKind::Given { topic, body },
3350 line,
3351 })
3352 }
3353
3354 fn parse_when_stmt(&mut self) -> PerlResult<Statement> {
3356 let line = self.peek_line();
3357 self.advance();
3358 self.expect(&Token::LParen)?;
3359 let cond = self.parse_expression()?;
3360 self.expect(&Token::RParen)?;
3361 let body = self.parse_block()?;
3362 self.eat(&Token::Semicolon);
3363 Ok(Statement {
3364 label: None,
3365 kind: StmtKind::When { cond, body },
3366 line,
3367 })
3368 }
3369
3370 fn parse_default_stmt(&mut self) -> PerlResult<Statement> {
3372 let line = self.peek_line();
3373 self.advance();
3374 let body = self.parse_block()?;
3375 self.eat(&Token::Semicolon);
3376 Ok(Statement {
3377 label: None,
3378 kind: StmtKind::DefaultCase { body },
3379 line,
3380 })
3381 }
3382
3383 fn parse_cond_expr(&mut self, line: usize) -> PerlResult<Expr> {
3389 self.expect(&Token::LBrace)?;
3390
3391 let mut arms: Vec<(Expr, Block)> = Vec::new();
3392 let mut else_block: Option<Block> = None;
3393
3394 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3395 let arm_line = self.peek_line();
3396
3397 let is_default = matches!(self.peek(), Token::Ident(ref s) if s == "default")
3399 && matches!(self.peek_at(1), Token::FatArrow);
3400
3401 if is_default {
3402 self.advance(); self.advance(); let body = if matches!(self.peek(), Token::LBrace) {
3405 self.parse_block()?
3406 } else {
3407 let expr = self.parse_assign_expr()?;
3408 vec![Statement {
3409 label: None,
3410 kind: StmtKind::Expression(expr),
3411 line: arm_line,
3412 }]
3413 };
3414 else_block = Some(body);
3415 self.eat(&Token::Comma);
3416 break; }
3418
3419 let condition = self.parse_assign_expr()?;
3421 self.expect(&Token::FatArrow)?;
3422
3423 let body = if matches!(self.peek(), Token::LBrace) {
3424 self.parse_block()?
3425 } else {
3426 let expr = self.parse_assign_expr()?;
3427 vec![Statement {
3428 label: None,
3429 kind: StmtKind::Expression(expr),
3430 line: arm_line,
3431 }]
3432 };
3433
3434 arms.push((condition, body));
3435 self.eat(&Token::Comma);
3436 }
3437
3438 self.expect(&Token::RBrace)?;
3439
3440 if arms.is_empty() {
3441 return Err(self.syntax_err("cond requires at least one condition arm", line));
3442 }
3443
3444 let (first_cond, first_body) = arms.remove(0);
3446 let elsifs: Vec<(Expr, Block)> = arms;
3447
3448 let if_stmt = Statement {
3450 label: None,
3451 kind: StmtKind::If {
3452 condition: first_cond,
3453 body: first_body,
3454 elsifs,
3455 else_block,
3456 },
3457 line,
3458 };
3459 let inner = Expr {
3460 kind: ExprKind::CodeRef {
3461 params: vec![],
3462 body: vec![if_stmt],
3463 },
3464 line,
3465 };
3466 Ok(Expr {
3467 kind: ExprKind::Do(Box::new(inner)),
3468 line,
3469 })
3470 }
3471
3472 fn parse_algebraic_match_expr(&mut self, line: usize) -> PerlResult<Expr> {
3474 self.expect(&Token::LParen)?;
3475 let subject = self.parse_expression()?;
3476 self.expect(&Token::RParen)?;
3477 self.expect(&Token::LBrace)?;
3478 let mut arms = Vec::new();
3479 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3480 if self.eat(&Token::Semicolon) {
3481 continue;
3482 }
3483 let pattern = self.parse_match_pattern()?;
3484 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
3485 self.advance();
3486 Some(Box::new(self.parse_assign_expr()?))
3489 } else {
3490 None
3491 };
3492 self.expect(&Token::FatArrow)?;
3493 let body = self.parse_assign_expr()?;
3495 arms.push(MatchArm {
3496 pattern,
3497 guard,
3498 body,
3499 });
3500 self.eat(&Token::Comma);
3501 }
3502 self.expect(&Token::RBrace)?;
3503 Ok(Expr {
3504 kind: ExprKind::AlgebraicMatch {
3505 subject: Box::new(subject),
3506 arms,
3507 },
3508 line,
3509 })
3510 }
3511
3512 fn parse_match_pattern(&mut self) -> PerlResult<MatchPattern> {
3513 match self.peek().clone() {
3514 Token::Regex(pattern, flags, _delim) => {
3515 self.advance();
3516 Ok(MatchPattern::Regex { pattern, flags })
3517 }
3518 Token::Ident(ref s) if s == "_" => {
3519 self.advance();
3520 Ok(MatchPattern::Any)
3521 }
3522 Token::Ident(ref s) if s == "Some" => {
3523 self.advance();
3524 self.expect(&Token::LParen)?;
3525 let name = self.parse_scalar_var_name()?;
3526 self.expect(&Token::RParen)?;
3527 Ok(MatchPattern::OptionSome(name))
3528 }
3529 Token::LBracket => self.parse_match_array_pattern(),
3530 Token::LBrace => self.parse_match_hash_pattern(),
3531 Token::LParen => {
3532 self.advance();
3533 let e = self.parse_expression()?;
3534 self.expect(&Token::RParen)?;
3535 Ok(MatchPattern::Value(Box::new(e)))
3536 }
3537 _ => {
3538 let e = self.parse_assign_expr()?;
3539 Ok(MatchPattern::Value(Box::new(e)))
3540 }
3541 }
3542 }
3543
3544 fn parse_match_array_elems_until_rbracket(&mut self) -> PerlResult<Vec<MatchArrayElem>> {
3546 let mut elems = Vec::new();
3547 if self.eat(&Token::RBracket) {
3548 return Ok(vec![]);
3549 }
3550 loop {
3551 if matches!(self.peek(), Token::Star) {
3552 self.advance();
3553 elems.push(MatchArrayElem::Rest);
3554 self.eat(&Token::Comma);
3555 if !matches!(self.peek(), Token::RBracket) {
3556 return Err(self.syntax_err(
3557 "`*` must be the last element in an array match pattern",
3558 self.peek_line(),
3559 ));
3560 }
3561 self.expect(&Token::RBracket)?;
3562 return Ok(elems);
3563 }
3564 if let Token::ArrayVar(name) = self.peek().clone() {
3565 self.advance();
3566 elems.push(MatchArrayElem::RestBind(name));
3567 self.eat(&Token::Comma);
3568 if !matches!(self.peek(), Token::RBracket) {
3569 return Err(self.syntax_err(
3570 "`@name` rest bind must be the last element in an array match pattern",
3571 self.peek_line(),
3572 ));
3573 }
3574 self.expect(&Token::RBracket)?;
3575 return Ok(elems);
3576 }
3577 if let Token::ScalarVar(name) = self.peek().clone() {
3578 self.advance();
3579 elems.push(MatchArrayElem::CaptureScalar(name));
3580 if self.eat(&Token::Comma) {
3581 if matches!(self.peek(), Token::RBracket) {
3582 break;
3583 }
3584 continue;
3585 }
3586 break;
3587 }
3588 let e = self.parse_assign_expr()?;
3589 elems.push(MatchArrayElem::Expr(e));
3590 if self.eat(&Token::Comma) {
3591 if matches!(self.peek(), Token::RBracket) {
3592 break;
3593 }
3594 continue;
3595 }
3596 break;
3597 }
3598 self.expect(&Token::RBracket)?;
3599 Ok(elems)
3600 }
3601
3602 fn parse_match_array_pattern(&mut self) -> PerlResult<MatchPattern> {
3603 self.expect(&Token::LBracket)?;
3604 let elems = self.parse_match_array_elems_until_rbracket()?;
3605 Ok(MatchPattern::Array(elems))
3606 }
3607
3608 fn parse_match_hash_pattern(&mut self) -> PerlResult<MatchPattern> {
3609 self.expect(&Token::LBrace)?;
3610 let mut pairs = Vec::new();
3611 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3612 if self.eat(&Token::Semicolon) {
3613 continue;
3614 }
3615 let key = self.parse_assign_expr()?;
3616 self.expect(&Token::FatArrow)?;
3617 match self.advance().0 {
3618 Token::Ident(ref s) if s == "_" => {
3619 pairs.push(MatchHashPair::KeyOnly { key });
3620 }
3621 Token::ScalarVar(name) => {
3622 pairs.push(MatchHashPair::Capture { key, name });
3623 }
3624 tok => {
3625 return Err(self.syntax_err(
3626 format!(
3627 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
3628 tok
3629 ),
3630 self.peek_line(),
3631 ));
3632 }
3633 }
3634 self.eat(&Token::Comma);
3635 }
3636 self.expect(&Token::RBrace)?;
3637 Ok(MatchPattern::Hash(pairs))
3638 }
3639
3640 fn parse_eval_timeout(&mut self) -> PerlResult<Statement> {
3642 let line = self.peek_line();
3643 self.advance();
3644 let timeout = self.parse_postfix()?;
3645 let body = self.parse_block_or_bareword_block_no_args()?;
3646 self.eat(&Token::Semicolon);
3647 Ok(Statement {
3648 label: None,
3649 kind: StmtKind::EvalTimeout { timeout, body },
3650 line,
3651 })
3652 }
3653
3654 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
3655 match &mut cond.kind {
3656 ExprKind::Match {
3657 flags, scalar_g, ..
3658 } if flags.contains('g') => {
3659 *scalar_g = true;
3660 }
3661 ExprKind::UnaryOp {
3662 op: UnaryOp::LogNot,
3663 expr,
3664 } => {
3665 if let ExprKind::Match {
3666 flags, scalar_g, ..
3667 } = &mut expr.kind
3668 {
3669 if flags.contains('g') {
3670 *scalar_g = true;
3671 }
3672 }
3673 }
3674 _ => {}
3675 }
3676 }
3677
3678 fn parse_if(&mut self) -> PerlResult<Statement> {
3679 let line = self.peek_line();
3680 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
3682 if crate::compat_mode() {
3683 return Err(self.syntax_err(
3684 "`if let` is a stryke extension (disabled by --compat)",
3685 line,
3686 ));
3687 }
3688 return self.parse_if_let(line);
3689 }
3690 self.expect(&Token::LParen)?;
3691 let mut cond = self.parse_expression()?;
3692 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3693 self.expect(&Token::RParen)?;
3694 let body = self.parse_block()?;
3695
3696 let mut elsifs = Vec::new();
3697 let mut else_block = None;
3698
3699 loop {
3700 if let Token::Ident(ref kw) = self.peek().clone() {
3701 if kw == "elsif" {
3702 self.advance();
3703 self.expect(&Token::LParen)?;
3704 let mut c = self.parse_expression()?;
3705 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
3706 self.expect(&Token::RParen)?;
3707 let b = self.parse_block()?;
3708 elsifs.push((c, b));
3709 continue;
3710 }
3711 if kw == "else" {
3712 self.advance();
3713 else_block = Some(self.parse_block()?);
3714 }
3715 }
3716 break;
3717 }
3718
3719 Ok(Statement {
3720 label: None,
3721 kind: StmtKind::If {
3722 condition: cond,
3723 body,
3724 elsifs,
3725 else_block,
3726 },
3727 line,
3728 })
3729 }
3730
3731 fn parse_if_let(&mut self, line: usize) -> PerlResult<Statement> {
3733 self.advance(); let pattern = self.parse_match_pattern()?;
3735 self.expect(&Token::Assign)?;
3736 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
3738 let rhs = self.parse_assign_expr();
3739 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
3740 let rhs = rhs?;
3741 let then_block = self.parse_block()?;
3742 let else_block_opt = match self.peek().clone() {
3743 Token::Ident(ref kw) if kw == "else" => {
3744 self.advance();
3745 Some(self.parse_block()?)
3746 }
3747 Token::Ident(ref kw) if kw == "elsif" => {
3748 return Err(self.syntax_err(
3749 "`if let` does not support `elsif`; use `else { }` or a full `match`",
3750 self.peek_line(),
3751 ));
3752 }
3753 _ => None,
3754 };
3755 let then_expr = Self::expr_do_anon_block(then_block, line);
3756 let else_expr = if let Some(eb) = else_block_opt {
3757 Self::expr_do_anon_block(eb, line)
3758 } else {
3759 Expr {
3760 kind: ExprKind::Undef,
3761 line,
3762 }
3763 };
3764 let arms = vec![
3765 MatchArm {
3766 pattern,
3767 guard: None,
3768 body: then_expr,
3769 },
3770 MatchArm {
3771 pattern: MatchPattern::Any,
3772 guard: None,
3773 body: else_expr,
3774 },
3775 ];
3776 Ok(Statement {
3777 label: None,
3778 kind: StmtKind::Expression(Expr {
3779 kind: ExprKind::AlgebraicMatch {
3780 subject: Box::new(rhs),
3781 arms,
3782 },
3783 line,
3784 }),
3785 line,
3786 })
3787 }
3788
3789 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
3790 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
3791 Expr {
3792 kind: ExprKind::Do(Box::new(Expr {
3793 kind: ExprKind::CodeRef {
3794 params: vec![],
3795 body: block,
3796 },
3797 line: inner_line,
3798 })),
3799 line: outer_line,
3800 }
3801 }
3802
3803 fn parse_unless(&mut self) -> PerlResult<Statement> {
3804 let line = self.peek_line();
3805 self.advance(); self.expect(&Token::LParen)?;
3807 let mut cond = self.parse_expression()?;
3808 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3809 self.expect(&Token::RParen)?;
3810 let body = self.parse_block()?;
3811 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
3812 if kw == "else" {
3813 self.advance();
3814 Some(self.parse_block()?)
3815 } else {
3816 None
3817 }
3818 } else {
3819 None
3820 };
3821 Ok(Statement {
3822 label: None,
3823 kind: StmtKind::Unless {
3824 condition: cond,
3825 body,
3826 else_block,
3827 },
3828 line,
3829 })
3830 }
3831
3832 fn parse_while(&mut self) -> PerlResult<Statement> {
3833 let line = self.peek_line();
3834 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
3836 if crate::compat_mode() {
3837 return Err(self.syntax_err(
3838 "`while let` is a stryke extension (disabled by --compat)",
3839 line,
3840 ));
3841 }
3842 return self.parse_while_let(line);
3843 }
3844 self.expect(&Token::LParen)?;
3845 let mut cond = self.parse_expression()?;
3846 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3847 self.expect(&Token::RParen)?;
3848 let body = self.parse_block()?;
3849 let continue_block = self.parse_optional_continue_block()?;
3850 Ok(Statement {
3851 label: None,
3852 kind: StmtKind::While {
3853 condition: cond,
3854 body,
3855 label: None,
3856 continue_block,
3857 },
3858 line,
3859 })
3860 }
3861
3862 fn parse_while_let(&mut self, line: usize) -> PerlResult<Statement> {
3865 self.advance(); let pattern = self.parse_match_pattern()?;
3867 self.expect(&Token::Assign)?;
3868 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
3869 let rhs = self.parse_assign_expr();
3870 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
3871 let rhs = rhs?;
3872 let mut user_body = self.parse_block()?;
3873 let continue_block = self.parse_optional_continue_block()?;
3874 user_body.push(Statement::new(
3875 StmtKind::Expression(Expr {
3876 kind: ExprKind::Integer(1),
3877 line,
3878 }),
3879 line,
3880 ));
3881 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
3882 let match_expr = Expr {
3883 kind: ExprKind::AlgebraicMatch {
3884 subject: Box::new(rhs),
3885 arms: vec![
3886 MatchArm {
3887 pattern,
3888 guard: None,
3889 body: Self::expr_do_anon_block(user_body, line),
3890 },
3891 MatchArm {
3892 pattern: MatchPattern::Any,
3893 guard: None,
3894 body: Expr {
3895 kind: ExprKind::Integer(0),
3896 line,
3897 },
3898 },
3899 ],
3900 },
3901 line,
3902 };
3903 let my_stmt = Statement::new(
3904 StmtKind::My(vec![VarDecl {
3905 sigil: Sigil::Scalar,
3906 name: tmp.clone(),
3907 initializer: Some(match_expr),
3908 frozen: false,
3909 type_annotation: None,
3910 }]),
3911 line,
3912 );
3913 let unless_last = Statement::new(
3914 StmtKind::Unless {
3915 condition: Expr {
3916 kind: ExprKind::ScalarVar(tmp),
3917 line,
3918 },
3919 body: vec![Statement::new(StmtKind::Last(None), line)],
3920 else_block: None,
3921 },
3922 line,
3923 );
3924 Ok(Statement::new(
3925 StmtKind::While {
3926 condition: Expr {
3927 kind: ExprKind::Integer(1),
3928 line,
3929 },
3930 body: vec![my_stmt, unless_last],
3931 label: None,
3932 continue_block,
3933 },
3934 line,
3935 ))
3936 }
3937
3938 fn parse_until(&mut self) -> PerlResult<Statement> {
3939 let line = self.peek_line();
3940 self.advance(); self.expect(&Token::LParen)?;
3942 let mut cond = self.parse_expression()?;
3943 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3944 self.expect(&Token::RParen)?;
3945 let body = self.parse_block()?;
3946 let continue_block = self.parse_optional_continue_block()?;
3947 Ok(Statement {
3948 label: None,
3949 kind: StmtKind::Until {
3950 condition: cond,
3951 body,
3952 label: None,
3953 continue_block,
3954 },
3955 line,
3956 })
3957 }
3958
3959 fn parse_optional_continue_block(&mut self) -> PerlResult<Option<Block>> {
3961 if let Token::Ident(ref kw) = self.peek().clone() {
3962 if kw == "continue" {
3963 self.advance();
3964 return Ok(Some(self.parse_block()?));
3965 }
3966 }
3967 Ok(None)
3968 }
3969
3970 fn parse_for_or_foreach(&mut self) -> PerlResult<Statement> {
3971 let line = self.peek_line();
3972 self.advance(); match self.peek() {
3978 Token::LParen => {
3979 let saved = self.pos;
3984 self.advance(); let mut depth = 1;
3987 let mut has_semi = false;
3988 let mut scan = self.pos;
3989 while scan < self.tokens.len() {
3990 match &self.tokens[scan].0 {
3991 Token::LParen => depth += 1,
3992 Token::RParen => {
3993 depth -= 1;
3994 if depth == 0 {
3995 break;
3996 }
3997 }
3998 Token::Semicolon if depth == 1 => {
3999 has_semi = true;
4000 break;
4001 }
4002 _ => {}
4003 }
4004 scan += 1;
4005 }
4006 self.pos = saved;
4007
4008 if has_semi {
4009 self.parse_c_style_for(line)
4010 } else {
4011 self.expect(&Token::LParen)?;
4013 let list = self.parse_expression()?;
4014 self.expect(&Token::RParen)?;
4015 let body = self.parse_block()?;
4016 let continue_block = self.parse_optional_continue_block()?;
4017 Ok(Statement {
4018 label: None,
4019 kind: StmtKind::Foreach {
4020 var: "_".to_string(),
4021 list,
4022 body,
4023 label: None,
4024 continue_block,
4025 },
4026 line,
4027 })
4028 }
4029 }
4030 Token::Ident(ref kw) if kw == "my" => {
4031 self.advance(); let var = self.parse_scalar_var_name()?;
4033 self.expect(&Token::LParen)?;
4034 let list = self.parse_expression()?;
4035 self.expect(&Token::RParen)?;
4036 let body = self.parse_block()?;
4037 let continue_block = self.parse_optional_continue_block()?;
4038 Ok(Statement {
4039 label: None,
4040 kind: StmtKind::Foreach {
4041 var,
4042 list,
4043 body,
4044 label: None,
4045 continue_block,
4046 },
4047 line,
4048 })
4049 }
4050 Token::ScalarVar(_) => {
4051 let var = self.parse_scalar_var_name()?;
4052 self.expect(&Token::LParen)?;
4053 let list = self.parse_expression()?;
4054 self.expect(&Token::RParen)?;
4055 let body = self.parse_block()?;
4056 let continue_block = self.parse_optional_continue_block()?;
4057 Ok(Statement {
4058 label: None,
4059 kind: StmtKind::Foreach {
4060 var,
4061 list,
4062 body,
4063 label: None,
4064 continue_block,
4065 },
4066 line,
4067 })
4068 }
4069 _ => self.parse_c_style_for(line),
4070 }
4071 }
4072
4073 fn parse_c_style_for(&mut self, line: usize) -> PerlResult<Statement> {
4074 self.expect(&Token::LParen)?;
4075 let init = if self.eat(&Token::Semicolon) {
4076 None
4077 } else {
4078 let s = self.parse_statement()?;
4079 self.eat(&Token::Semicolon);
4080 Some(Box::new(s))
4081 };
4082 let mut condition = if matches!(self.peek(), Token::Semicolon) {
4083 None
4084 } else {
4085 Some(self.parse_expression()?)
4086 };
4087 if let Some(ref mut c) = condition {
4088 Self::mark_match_scalar_g_for_boolean_condition(c);
4089 }
4090 self.expect(&Token::Semicolon)?;
4091 let step = if matches!(self.peek(), Token::RParen) {
4092 None
4093 } else {
4094 Some(self.parse_expression()?)
4095 };
4096 self.expect(&Token::RParen)?;
4097 let body = self.parse_block()?;
4098 let continue_block = self.parse_optional_continue_block()?;
4099 Ok(Statement {
4100 label: None,
4101 kind: StmtKind::For {
4102 init,
4103 condition,
4104 step,
4105 body,
4106 label: None,
4107 continue_block,
4108 },
4109 line,
4110 })
4111 }
4112
4113 fn parse_foreach(&mut self) -> PerlResult<Statement> {
4114 let line = self.peek_line();
4115 self.advance(); let var = match self.peek() {
4117 Token::Ident(ref kw) if kw == "my" => {
4118 self.advance();
4119 self.parse_scalar_var_name()?
4120 }
4121 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
4122 _ => "_".to_string(),
4123 };
4124 self.expect(&Token::LParen)?;
4125 let list = self.parse_expression()?;
4126 self.expect(&Token::RParen)?;
4127 let body = self.parse_block()?;
4128 let continue_block = self.parse_optional_continue_block()?;
4129 Ok(Statement {
4130 label: None,
4131 kind: StmtKind::Foreach {
4132 var,
4133 list,
4134 body,
4135 label: None,
4136 continue_block,
4137 },
4138 line,
4139 })
4140 }
4141
4142 fn parse_scalar_var_name(&mut self) -> PerlResult<String> {
4143 match self.advance() {
4144 (Token::ScalarVar(name), _) => Ok(name),
4145 (tok, line) => {
4146 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
4147 }
4148 }
4149 }
4150
4151 fn parse_legacy_sub_prototype_tail(&mut self) -> PerlResult<String> {
4153 let mut s = String::new();
4154 loop {
4155 match self.peek().clone() {
4156 Token::RParen => {
4157 self.advance();
4158 break;
4159 }
4160 Token::Eof => {
4161 return Err(self.syntax_err(
4162 "Unterminated sub prototype (expected ')' before end of input)",
4163 self.peek_line(),
4164 ));
4165 }
4166 Token::ScalarVar(v) if v == ")" => {
4167 self.advance();
4170 s.push('$');
4171 if matches!(self.peek(), Token::LBrace) {
4172 break;
4173 }
4174 }
4175 Token::Ident(i) => {
4176 let i = i.clone();
4177 self.advance();
4178 s.push_str(&i);
4179 }
4180 Token::Semicolon => {
4181 self.advance();
4182 s.push(';');
4183 }
4184 Token::LParen => {
4185 self.advance();
4186 s.push('(');
4187 }
4188 Token::LBracket => {
4189 self.advance();
4190 s.push('[');
4191 }
4192 Token::RBracket => {
4193 self.advance();
4194 s.push(']');
4195 }
4196 Token::Backslash => {
4197 self.advance();
4198 s.push('\\');
4199 }
4200 Token::Comma => {
4201 self.advance();
4202 s.push(',');
4203 }
4204 Token::ScalarVar(v) => {
4205 let v = v.clone();
4206 self.advance();
4207 s.push('$');
4208 s.push_str(&v);
4209 }
4210 Token::ArrayVar(v) => {
4211 let v = v.clone();
4212 self.advance();
4213 s.push('@');
4214 s.push_str(&v);
4215 }
4216 Token::ArrayAt => {
4218 self.advance();
4219 s.push('@');
4220 }
4221 Token::HashVar(v) => {
4222 let v = v.clone();
4223 self.advance();
4224 s.push('%');
4225 s.push_str(&v);
4226 }
4227 Token::HashPercent => {
4228 self.advance();
4229 s.push('%');
4230 }
4231 Token::Plus => {
4232 self.advance();
4233 s.push('+');
4234 }
4235 Token::Minus => {
4236 self.advance();
4237 s.push('-');
4238 }
4239 Token::BitAnd => {
4240 self.advance();
4241 s.push('&');
4242 }
4243 tok => {
4244 return Err(self.syntax_err(
4245 format!("Unexpected token in sub prototype: {:?}", tok),
4246 self.peek_line(),
4247 ));
4248 }
4249 }
4250 }
4251 Ok(s)
4252 }
4253
4254 fn sub_signature_list_starts_here(&self) -> bool {
4255 match self.peek() {
4256 Token::LBrace | Token::LBracket => true,
4257 Token::ScalarVar(name) if name != "$$" && name != ")" => true,
4258 Token::ArrayVar(_) | Token::HashVar(_) => true,
4259 _ => false,
4260 }
4261 }
4262
4263 fn parse_sub_signature_hash_key(&mut self) -> PerlResult<String> {
4264 let (tok, line) = self.advance();
4265 match tok {
4266 Token::Ident(i) => Ok(i),
4267 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
4268 tok => Err(self.syntax_err(
4269 format!(
4270 "sub signature: expected hash key (identifier or string), got {:?}",
4271 tok
4272 ),
4273 line,
4274 )),
4275 }
4276 }
4277
4278 fn parse_sub_signature_param_list(&mut self) -> PerlResult<Vec<SubSigParam>> {
4279 let mut params = Vec::new();
4280 loop {
4281 if matches!(self.peek(), Token::RParen) {
4282 break;
4283 }
4284 match self.peek().clone() {
4285 Token::ScalarVar(name) => {
4286 if name == "$$" || name == ")" {
4287 return Err(self.syntax_err(
4288 format!(
4289 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
4290 ),
4291 self.peek_line(),
4292 ));
4293 }
4294 self.advance();
4295 let ty = if self.eat(&Token::Colon) {
4296 match self.peek() {
4297 Token::Ident(ref tname) => {
4298 let tname = tname.clone();
4299 self.advance();
4300 Some(match tname.as_str() {
4301 "Int" => PerlTypeName::Int,
4302 "Str" => PerlTypeName::Str,
4303 "Float" => PerlTypeName::Float,
4304 "Bool" => PerlTypeName::Bool,
4305 "Array" => PerlTypeName::Array,
4306 "Hash" => PerlTypeName::Hash,
4307 "Ref" => PerlTypeName::Ref,
4308 "Any" => PerlTypeName::Any,
4309 _ => PerlTypeName::Struct(tname),
4310 })
4311 }
4312 _ => {
4313 return Err(self.syntax_err(
4314 "expected type name after `:` in sub signature",
4315 self.peek_line(),
4316 ));
4317 }
4318 }
4319 } else {
4320 None
4321 };
4322 let default = if self.eat(&Token::Assign) {
4324 Some(Box::new(self.parse_ternary()?))
4325 } else {
4326 None
4327 };
4328 params.push(SubSigParam::Scalar(name, ty, default));
4329 }
4330 Token::ArrayVar(name) => {
4331 self.advance();
4332 let default = if self.eat(&Token::Assign) {
4333 Some(Box::new(self.parse_ternary()?))
4334 } else {
4335 None
4336 };
4337 params.push(SubSigParam::Array(name, default));
4338 }
4339 Token::HashVar(name) => {
4340 self.advance();
4341 let default = if self.eat(&Token::Assign) {
4342 Some(Box::new(self.parse_ternary()?))
4343 } else {
4344 None
4345 };
4346 params.push(SubSigParam::Hash(name, default));
4347 }
4348 Token::LBracket => {
4349 self.advance();
4350 let elems = self.parse_match_array_elems_until_rbracket()?;
4351 params.push(SubSigParam::ArrayDestruct(elems));
4352 }
4353 Token::LBrace => {
4354 self.advance();
4355 let mut pairs = Vec::new();
4356 loop {
4357 if matches!(self.peek(), Token::RBrace | Token::Eof) {
4358 break;
4359 }
4360 if self.eat(&Token::Comma) {
4361 continue;
4362 }
4363 let key = self.parse_sub_signature_hash_key()?;
4364 self.expect(&Token::FatArrow)?;
4365 let bind = self.parse_scalar_var_name()?;
4366 pairs.push((key, bind));
4367 self.eat(&Token::Comma);
4368 }
4369 self.expect(&Token::RBrace)?;
4370 params.push(SubSigParam::HashDestruct(pairs));
4371 }
4372 tok => {
4373 return Err(self.syntax_err(
4374 format!(
4375 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
4376 tok
4377 ),
4378 self.peek_line(),
4379 ));
4380 }
4381 }
4382 match self.peek() {
4383 Token::Comma => {
4384 self.advance();
4385 if matches!(self.peek(), Token::RParen) {
4386 return Err(self.syntax_err(
4387 "trailing `,` before `)` in sub signature",
4388 self.peek_line(),
4389 ));
4390 }
4391 }
4392 Token::RParen => break,
4393 _ => {
4394 return Err(self.syntax_err(
4395 format!(
4396 "expected `,` or `)` after sub signature parameter, got {:?}",
4397 self.peek()
4398 ),
4399 self.peek_line(),
4400 ));
4401 }
4402 }
4403 }
4404 Ok(params)
4405 }
4406
4407 fn parse_sub_sig_or_prototype_opt(&mut self) -> PerlResult<(Vec<SubSigParam>, Option<String>)> {
4409 if !matches!(self.peek(), Token::LParen) {
4410 return Ok((vec![], None));
4411 }
4412 self.advance();
4413 if matches!(self.peek(), Token::RParen) {
4414 self.advance();
4415 return Ok((vec![], Some(String::new())));
4416 }
4417 if self.sub_signature_list_starts_here() {
4418 let params = self.parse_sub_signature_param_list()?;
4419 self.expect(&Token::RParen)?;
4420 return Ok((params, None));
4421 }
4422 let proto = self.parse_legacy_sub_prototype_tail()?;
4423 Ok((vec![], Some(proto)))
4424 }
4425
4426 fn parse_sub_attributes(&mut self) -> PerlResult<()> {
4428 while self.eat(&Token::Colon) {
4429 match self.advance() {
4430 (Token::Ident(_), _) => {}
4431 (tok, line) => {
4432 return Err(self.syntax_err(
4433 format!("Expected attribute name after `:`, got {:?}", tok),
4434 line,
4435 ));
4436 }
4437 }
4438 if self.eat(&Token::LParen) {
4439 let mut depth = 1usize;
4440 while depth > 0 {
4441 match self.advance().0 {
4442 Token::LParen => depth += 1,
4443 Token::RParen => {
4444 depth -= 1;
4445 }
4446 Token::Eof => {
4447 return Err(self.syntax_err(
4448 "Unterminated sub attribute argument list",
4449 self.peek_line(),
4450 ));
4451 }
4452 _ => {}
4453 }
4454 }
4455 }
4456 }
4457 Ok(())
4458 }
4459
4460 fn parse_fn_eq_body_or_block(&mut self, is_sub_keyword: bool) -> PerlResult<Block> {
4463 if !is_sub_keyword && self.eat(&Token::Assign) {
4464 let expr = self.parse_assign_expr()?;
4465 if matches!(self.peek(), Token::Comma) {
4466 return Err(self.syntax_err(
4467 "`fn ... =` allows only a single expression; use `fn ... { ... }` for multiple statements",
4468 self.peek_line(),
4469 ));
4470 }
4471 let eline = expr.line;
4472 self.eat(&Token::Semicolon);
4473 let mut body = vec![Statement {
4474 label: None,
4475 kind: StmtKind::Expression(expr),
4476 line: eline,
4477 }];
4478 Self::default_topic_for_sole_bareword(&mut body);
4479 Ok(body)
4480 } else {
4481 self.parse_block()
4482 }
4483 }
4484
4485 fn parse_sub_decl(&mut self, is_sub_keyword: bool) -> PerlResult<Statement> {
4486 let line = self.peek_line();
4487 self.advance(); match self.peek().clone() {
4489 Token::Ident(_) => {
4490 let name = self.parse_package_qualified_identifier()?;
4491 let bare = name.rsplit("::").next().unwrap_or(&name);
4500 if Self::is_underscore_topic_slot(bare) {
4501 return Err(self.syntax_err(
4502 format!(
4503 "`fn {}` would shadow the topic-slot scalar; pick a different name",
4504 name
4505 ),
4506 line,
4507 ));
4508 }
4509 if Self::is_reserved_special_var_name(bare) {
4510 return Err(self.syntax_err(
4511 format!(
4512 "`fn {}` would shadow a Perl special variable / filehandle / compile-time token; pick a different name",
4513 name
4514 ),
4515 line,
4516 ));
4517 }
4518 let allow_shadow =
4525 crate::compat_mode() || (self.parsing_module && !crate::no_interop_mode());
4526 if !allow_shadow {
4527 self.check_udf_shadows_builtin(&name, line)?;
4528 }
4529 self.declared_subs.insert(name.clone());
4530 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
4531 self.parse_sub_attributes()?;
4532 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4533 Ok(Statement {
4534 label: None,
4535 kind: StmtKind::SubDecl {
4536 name,
4537 params,
4538 body,
4539 prototype,
4540 },
4541 line,
4542 })
4543 }
4544 Token::LParen | Token::LBrace | Token::Colon => {
4545 if is_sub_keyword && crate::no_interop_mode() {
4547 return Err(self.syntax_err(
4548 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
4549 line,
4550 ));
4551 }
4552 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
4554 self.parse_sub_attributes()?;
4555 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4556 Ok(Statement {
4557 label: None,
4558 kind: StmtKind::Expression(Expr {
4559 kind: ExprKind::CodeRef { params, body },
4560 line,
4561 }),
4562 line,
4563 })
4564 }
4565 tok => {
4566 let topic_name = match &tok {
4571 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n)
4572 if Self::is_underscore_topic_slot(n) =>
4573 {
4574 Some((
4575 match &tok {
4576 Token::ScalarVar(_) => '$',
4577 Token::ArrayVar(_) => '@',
4578 Token::HashVar(_) => '%',
4579 _ => unreachable!(),
4580 },
4581 n.clone(),
4582 ))
4583 }
4584 _ => None,
4585 };
4586 if let Some((sigil, n)) = topic_name {
4587 return Err(self.syntax_err(
4588 format!(
4589 "`fn {}{}` would shadow the topic-slot scalar; pick a different name",
4590 sigil, n
4591 ),
4592 self.peek_line(),
4593 ));
4594 }
4595 let special_var = match &tok {
4600 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n) => Some((
4601 match &tok {
4602 Token::ScalarVar(_) => '$',
4603 Token::ArrayVar(_) => '@',
4604 Token::HashVar(_) => '%',
4605 _ => unreachable!(),
4606 },
4607 n.clone(),
4608 )),
4609 _ => None,
4610 };
4611 if let Some((sigil, n)) = special_var {
4612 return Err(self.syntax_err(
4613 format!(
4614 "`fn {}{}` would shadow a Perl special variable / global; pick a different name",
4615 sigil, n
4616 ),
4617 self.peek_line(),
4618 ));
4619 }
4620 if matches!(tok, Token::Percent) {
4625 return Err(self.syntax_err(
4626 "`fn %NAME` is not a valid sub declaration — `%name` would refer to a hash variable, not a sub name. To define a sub, use `fn NAME { ... }`",
4627 self.peek_line(),
4628 ));
4629 }
4630 Err(self.syntax_err(
4631 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
4632 self.peek_line(),
4633 ))
4634 }
4635 }
4636 }
4637
4638 fn parse_advice_decl(&mut self, kind: crate::ast::AdviceKind) -> PerlResult<Statement> {
4641 let line = self.peek_line();
4642 self.advance(); let pattern = match self.advance() {
4644 (Token::SingleString(s), _) | (Token::DoubleString(s), _) => s,
4645 (tok, err_line) => {
4646 return Err(self.syntax_err(
4647 format!(
4648 "Expected string-literal pattern after `{}`, got {:?}",
4649 match kind {
4650 crate::ast::AdviceKind::Before => "before",
4651 crate::ast::AdviceKind::After => "after",
4652 crate::ast::AdviceKind::Around => "around",
4653 },
4654 tok
4655 ),
4656 err_line,
4657 ));
4658 }
4659 };
4660 let body = self.parse_block()?;
4661 Ok(Statement {
4662 label: None,
4663 kind: StmtKind::AdviceDecl {
4664 kind,
4665 pattern,
4666 body,
4667 },
4668 line,
4669 })
4670 }
4671
4672 fn parse_struct_decl(&mut self) -> PerlResult<Statement> {
4674 let line = self.peek_line();
4675 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4677 self.syntax_err(
4678 format!("Expected struct name, got {:?}", self.peek()),
4679 self.peek_line(),
4680 )
4681 })?;
4682 self.expect(&Token::LBrace)?;
4683 let mut fields = Vec::new();
4684 let mut methods = Vec::new();
4685 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4686 let is_method = match self.peek() {
4688 Token::Ident(s) => s == "fn" || s == "sub",
4689 _ => false,
4690 };
4691 if is_method {
4692 self.advance(); let method_name = match self.advance() {
4694 (Token::Ident(n), _) => n,
4695 (tok, err_line) => {
4696 return Err(self
4697 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
4698 }
4699 };
4700 let params = if self.eat(&Token::LParen) {
4702 let p = self.parse_sub_signature_param_list()?;
4703 self.expect(&Token::RParen)?;
4704 p
4705 } else {
4706 Vec::new()
4707 };
4708 let body = self.parse_block()?;
4710 methods.push(crate::ast::StructMethod {
4711 name: method_name,
4712 params,
4713 body,
4714 });
4715 self.eat(&Token::Comma);
4717 self.eat(&Token::Semicolon);
4718 continue;
4719 }
4720
4721 let field_name = match self.advance() {
4722 (Token::Ident(n), _) => n,
4723 (tok, err_line) => {
4724 return Err(
4725 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
4726 )
4727 }
4728 };
4729 let ty = if self.eat(&Token::FatArrow) {
4731 self.parse_type_name()?
4732 } else {
4733 crate::ast::PerlTypeName::Any
4734 };
4735 let default = if self.eat(&Token::Assign) {
4736 Some(self.parse_ternary()?)
4738 } else {
4739 None
4740 };
4741 fields.push(StructField {
4742 name: field_name,
4743 ty,
4744 default,
4745 });
4746 if !self.eat(&Token::Comma) {
4747 self.eat(&Token::Semicolon);
4749 }
4750 }
4751 self.expect(&Token::RBrace)?;
4752 self.eat(&Token::Semicolon);
4753 Ok(Statement {
4754 label: None,
4755 kind: StmtKind::StructDecl {
4756 def: StructDef {
4757 name,
4758 fields,
4759 methods,
4760 },
4761 },
4762 line,
4763 })
4764 }
4765
4766 fn parse_enum_decl(&mut self) -> PerlResult<Statement> {
4768 let line = self.peek_line();
4769 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4771 self.syntax_err(
4772 format!("Expected enum name, got {:?}", self.peek()),
4773 self.peek_line(),
4774 )
4775 })?;
4776 self.expect(&Token::LBrace)?;
4777 let mut variants = Vec::new();
4778 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4779 let variant_name = match self.advance() {
4780 (Token::Ident(n), _) => n,
4781 (tok, err_line) => {
4782 return Err(
4783 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
4784 )
4785 }
4786 };
4787 let ty = if self.eat(&Token::FatArrow) {
4788 Some(self.parse_type_name()?)
4789 } else {
4790 None
4791 };
4792 variants.push(EnumVariant {
4793 name: variant_name,
4794 ty,
4795 });
4796 if !self.eat(&Token::Comma) {
4797 self.eat(&Token::Semicolon);
4798 }
4799 }
4800 self.expect(&Token::RBrace)?;
4801 self.eat(&Token::Semicolon);
4802 Ok(Statement {
4803 label: None,
4804 kind: StmtKind::EnumDecl {
4805 def: EnumDef { name, variants },
4806 },
4807 line,
4808 })
4809 }
4810
4811 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> PerlResult<Statement> {
4813 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
4814 let line = self.peek_line();
4815 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4817 self.syntax_err(
4818 format!("Expected class name, got {:?}", self.peek()),
4819 self.peek_line(),
4820 )
4821 })?;
4822
4823 let mut extends = Vec::new();
4825 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
4826 self.advance(); loop {
4828 let parent = self.parse_package_qualified_identifier().map_err(|_| {
4829 self.syntax_err(
4830 format!(
4831 "Expected parent class name after `extends`, got {:?}",
4832 self.peek()
4833 ),
4834 self.peek_line(),
4835 )
4836 })?;
4837 extends.push(parent);
4838 if !self.eat(&Token::Comma) {
4839 break;
4840 }
4841 }
4842 }
4843
4844 let mut implements = Vec::new();
4846 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
4847 self.advance(); loop {
4849 let trait_name = self.parse_package_qualified_identifier().map_err(|_| {
4850 self.syntax_err(
4851 format!("Expected trait name after `impl`, got {:?}", self.peek()),
4852 self.peek_line(),
4853 )
4854 })?;
4855 implements.push(trait_name);
4856 if !self.eat(&Token::Comma) {
4857 break;
4858 }
4859 }
4860 }
4861
4862 self.expect(&Token::LBrace)?;
4863 let mut fields = Vec::new();
4864 let mut methods = Vec::new();
4865 let mut static_fields = Vec::new();
4866
4867 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4868 let visibility = match self.peek() {
4870 Token::Ident(ref s) if s == "pub" => {
4871 self.advance();
4872 Visibility::Public
4873 }
4874 Token::Ident(ref s) if s == "priv" => {
4875 self.advance();
4876 Visibility::Private
4877 }
4878 Token::Ident(ref s) if s == "prot" => {
4879 self.advance();
4880 Visibility::Protected
4881 }
4882 _ => Visibility::Public, };
4884
4885 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
4887 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
4891 return Err(self.syntax_err(
4893 "use `fn Self.name` for static methods, not `static fn`",
4894 self.peek_line(),
4895 ));
4896 }
4897
4898 let field_name = match self.advance() {
4899 (Token::Ident(n), _) => n,
4900 (tok, err_line) => {
4901 return Err(self.syntax_err(
4902 format!("Expected static field name, got {:?}", tok),
4903 err_line,
4904 ))
4905 }
4906 };
4907
4908 let ty = if self.eat(&Token::Colon) {
4909 self.parse_type_name()?
4910 } else {
4911 crate::ast::PerlTypeName::Any
4912 };
4913
4914 let default = if self.eat(&Token::Assign) {
4915 Some(self.parse_ternary()?)
4916 } else {
4917 None
4918 };
4919
4920 static_fields.push(ClassStaticField {
4921 name: field_name,
4922 ty,
4923 visibility,
4924 default,
4925 });
4926
4927 if !self.eat(&Token::Comma) {
4928 self.eat(&Token::Semicolon);
4929 }
4930 continue;
4931 }
4932
4933 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
4935 if method_is_final {
4936 self.advance(); }
4938
4939 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
4941 if is_method {
4942 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
4946 if is_static {
4947 self.advance(); self.expect(&Token::Dot)?;
4949 }
4950
4951 let method_name = match self.advance() {
4952 (Token::Ident(n), _) => n,
4953 (tok, err_line) => {
4954 return Err(self
4955 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
4956 }
4957 };
4958
4959 let params = if self.eat(&Token::LParen) {
4961 let p = self.parse_sub_signature_param_list()?;
4962 self.expect(&Token::RParen)?;
4963 p
4964 } else {
4965 Vec::new()
4966 };
4967
4968 let body = if matches!(self.peek(), Token::LBrace) {
4970 Some(self.parse_block()?)
4971 } else {
4972 None
4973 };
4974
4975 methods.push(ClassMethod {
4976 name: method_name,
4977 params,
4978 body,
4979 visibility,
4980 is_static,
4981 is_final: method_is_final,
4982 });
4983 self.eat(&Token::Comma);
4984 self.eat(&Token::Semicolon);
4985 continue;
4986 } else if method_is_final {
4987 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
4988 }
4989
4990 let field_name = match self.advance() {
4992 (Token::Ident(n), _) => n,
4993 (tok, err_line) => {
4994 return Err(
4995 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
4996 )
4997 }
4998 };
4999
5000 let ty = if self.eat(&Token::Colon) {
5002 self.parse_type_name()?
5003 } else {
5004 crate::ast::PerlTypeName::Any
5005 };
5006
5007 let default = if self.eat(&Token::Assign) {
5009 Some(self.parse_ternary()?)
5010 } else {
5011 None
5012 };
5013
5014 fields.push(ClassField {
5015 name: field_name,
5016 ty,
5017 visibility,
5018 default,
5019 });
5020
5021 if !self.eat(&Token::Comma) {
5022 self.eat(&Token::Semicolon);
5023 }
5024 }
5025
5026 self.expect(&Token::RBrace)?;
5027 self.eat(&Token::Semicolon);
5028
5029 Ok(Statement {
5030 label: None,
5031 kind: StmtKind::ClassDecl {
5032 def: ClassDef {
5033 name,
5034 is_abstract,
5035 is_final,
5036 extends,
5037 implements,
5038 fields,
5039 methods,
5040 static_fields,
5041 },
5042 },
5043 line,
5044 })
5045 }
5046
5047 fn parse_trait_decl(&mut self) -> PerlResult<Statement> {
5049 use crate::ast::{ClassMethod, TraitDef, Visibility};
5050 let line = self.peek_line();
5051 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
5053 self.syntax_err(
5054 format!("Expected trait name, got {:?}", self.peek()),
5055 self.peek_line(),
5056 )
5057 })?;
5058
5059 self.expect(&Token::LBrace)?;
5060 let mut methods = Vec::new();
5061
5062 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5063 let visibility = match self.peek() {
5065 Token::Ident(ref s) if s == "pub" => {
5066 self.advance();
5067 Visibility::Public
5068 }
5069 Token::Ident(ref s) if s == "priv" => {
5070 self.advance();
5071 Visibility::Private
5072 }
5073 Token::Ident(ref s) if s == "prot" => {
5074 self.advance();
5075 Visibility::Protected
5076 }
5077 _ => Visibility::Public,
5078 };
5079
5080 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5082 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
5083 }
5084 self.advance(); let method_name = match self.advance() {
5087 (Token::Ident(n), _) => n,
5088 (tok, err_line) => {
5089 return Err(
5090 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
5091 )
5092 }
5093 };
5094
5095 let params = if self.eat(&Token::LParen) {
5097 let p = self.parse_sub_signature_param_list()?;
5098 self.expect(&Token::RParen)?;
5099 p
5100 } else {
5101 Vec::new()
5102 };
5103
5104 let body = if matches!(self.peek(), Token::LBrace) {
5106 Some(self.parse_block()?)
5107 } else {
5108 None
5109 };
5110
5111 methods.push(ClassMethod {
5112 name: method_name,
5113 params,
5114 body,
5115 visibility,
5116 is_static: false,
5117 is_final: false,
5118 });
5119
5120 self.eat(&Token::Comma);
5121 self.eat(&Token::Semicolon);
5122 }
5123
5124 self.expect(&Token::RBrace)?;
5125 self.eat(&Token::Semicolon);
5126
5127 Ok(Statement {
5128 label: None,
5129 kind: StmtKind::TraitDecl {
5130 def: TraitDef { name, methods },
5131 },
5132 line,
5133 })
5134 }
5135
5136 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
5137 match &target.kind {
5138 ExprKind::ScalarVar(name) => Some(VarDecl {
5139 sigil: Sigil::Scalar,
5140 name: name.clone(),
5141 initializer: None,
5142 frozen: false,
5143 type_annotation: None,
5144 }),
5145 ExprKind::ArrayVar(name) => Some(VarDecl {
5146 sigil: Sigil::Array,
5147 name: name.clone(),
5148 initializer: None,
5149 frozen: false,
5150 type_annotation: None,
5151 }),
5152 ExprKind::HashVar(name) => Some(VarDecl {
5153 sigil: Sigil::Hash,
5154 name: name.clone(),
5155 initializer: None,
5156 frozen: false,
5157 type_annotation: None,
5158 }),
5159 ExprKind::Typeglob(name) => Some(VarDecl {
5160 sigil: Sigil::Typeglob,
5161 name: name.clone(),
5162 initializer: None,
5163 frozen: false,
5164 type_annotation: None,
5165 }),
5166 _ => None,
5167 }
5168 }
5169
5170 fn parse_decl_array_destructure(
5171 &mut self,
5172 keyword: &str,
5173 line: usize,
5174 ) -> PerlResult<Statement> {
5175 self.expect(&Token::LBracket)?;
5176 let elems = self.parse_match_array_elems_until_rbracket()?;
5177 self.expect(&Token::Assign)?;
5178 self.suppress_scalar_hash_brace += 1;
5179 let rhs = self.parse_expression()?;
5180 self.suppress_scalar_hash_brace -= 1;
5181 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
5182 self.parse_stmt_postfix_modifier(stmt)
5183 }
5184
5185 fn parse_decl_hash_destructure(&mut self, keyword: &str, line: usize) -> PerlResult<Statement> {
5186 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
5187 unreachable!("parse_match_hash_pattern returns Hash");
5188 };
5189 self.expect(&Token::Assign)?;
5190 self.suppress_scalar_hash_brace += 1;
5191 let rhs = self.parse_expression()?;
5192 self.suppress_scalar_hash_brace -= 1;
5193 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
5194 self.parse_stmt_postfix_modifier(stmt)
5195 }
5196
5197 fn desugar_array_destructure(
5198 &mut self,
5199 keyword: &str,
5200 line: usize,
5201 elems: Vec<MatchArrayElem>,
5202 rhs: Expr,
5203 ) -> PerlResult<Statement> {
5204 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5205 let mut stmts: Vec<Statement> = Vec::new();
5206 stmts.push(destructure_stmt_from_var_decls(
5207 keyword,
5208 vec![VarDecl {
5209 sigil: Sigil::Scalar,
5210 name: tmp.clone(),
5211 initializer: Some(rhs),
5212 frozen: false,
5213 type_annotation: None,
5214 }],
5215 line,
5216 ));
5217
5218 let has_rest = elems
5219 .iter()
5220 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
5221 let fixed_slots = elems
5222 .iter()
5223 .filter(|e| {
5224 matches!(
5225 e,
5226 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
5227 )
5228 })
5229 .count();
5230 if !has_rest {
5231 let cond = Expr {
5232 kind: ExprKind::BinOp {
5233 left: Box::new(destructure_expr_array_len(&tmp, line)),
5234 op: BinOp::NumEq,
5235 right: Box::new(Expr {
5236 kind: ExprKind::Integer(fixed_slots as i64),
5237 line,
5238 }),
5239 },
5240 line,
5241 };
5242 stmts.push(destructure_stmt_unless_die(
5243 line,
5244 cond,
5245 "array destructure: length mismatch",
5246 ));
5247 }
5248
5249 let mut idx: i64 = 0;
5250 for elem in elems {
5251 match elem {
5252 MatchArrayElem::Rest => break,
5253 MatchArrayElem::RestBind(name) => {
5254 let list_source = Expr {
5255 kind: ExprKind::Deref {
5256 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5257 kind: Sigil::Array,
5258 },
5259 line,
5260 };
5261 let last_ix = Expr {
5262 kind: ExprKind::BinOp {
5263 left: Box::new(destructure_expr_array_len(&tmp, line)),
5264 op: BinOp::Sub,
5265 right: Box::new(Expr {
5266 kind: ExprKind::Integer(1),
5267 line,
5268 }),
5269 },
5270 line,
5271 };
5272 let range = Expr {
5273 kind: ExprKind::Range {
5274 from: Box::new(Expr {
5275 kind: ExprKind::Integer(idx),
5276 line,
5277 }),
5278 to: Box::new(last_ix),
5279 exclusive: false,
5280 step: None,
5281 },
5282 line,
5283 };
5284 let slice = Expr {
5285 kind: ExprKind::AnonymousListSlice {
5286 source: Box::new(list_source),
5287 indices: vec![range],
5288 },
5289 line,
5290 };
5291 stmts.push(destructure_stmt_from_var_decls(
5292 keyword,
5293 vec![VarDecl {
5294 sigil: Sigil::Array,
5295 name,
5296 initializer: Some(slice),
5297 frozen: false,
5298 type_annotation: None,
5299 }],
5300 line,
5301 ));
5302 break;
5303 }
5304 MatchArrayElem::CaptureScalar(name) => {
5305 let arrow = Expr {
5306 kind: ExprKind::ArrowDeref {
5307 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5308 index: Box::new(Expr {
5309 kind: ExprKind::Integer(idx),
5310 line,
5311 }),
5312 kind: DerefKind::Array,
5313 },
5314 line,
5315 };
5316 stmts.push(destructure_stmt_from_var_decls(
5317 keyword,
5318 vec![VarDecl {
5319 sigil: Sigil::Scalar,
5320 name,
5321 initializer: Some(arrow),
5322 frozen: false,
5323 type_annotation: None,
5324 }],
5325 line,
5326 ));
5327 idx += 1;
5328 }
5329 MatchArrayElem::Expr(e) => {
5330 let elem_subj = Expr {
5331 kind: ExprKind::ArrowDeref {
5332 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5333 index: Box::new(Expr {
5334 kind: ExprKind::Integer(idx),
5335 line,
5336 }),
5337 kind: DerefKind::Array,
5338 },
5339 line,
5340 };
5341 let match_expr = Expr {
5342 kind: ExprKind::AlgebraicMatch {
5343 subject: Box::new(elem_subj),
5344 arms: vec![
5345 MatchArm {
5346 pattern: MatchPattern::Value(Box::new(e.clone())),
5347 guard: None,
5348 body: Expr {
5349 kind: ExprKind::Integer(0),
5350 line,
5351 },
5352 },
5353 MatchArm {
5354 pattern: MatchPattern::Any,
5355 guard: None,
5356 body: Expr {
5357 kind: ExprKind::Die(vec![Expr {
5358 kind: ExprKind::String(
5359 "array destructure: element pattern mismatch"
5360 .to_string(),
5361 ),
5362 line,
5363 }]),
5364 line,
5365 },
5366 },
5367 ],
5368 },
5369 line,
5370 };
5371 stmts.push(Statement {
5372 label: None,
5373 kind: StmtKind::Expression(match_expr),
5374 line,
5375 });
5376 idx += 1;
5377 }
5378 }
5379 }
5380
5381 Ok(Statement {
5382 label: None,
5383 kind: StmtKind::StmtGroup(stmts),
5384 line,
5385 })
5386 }
5387
5388 fn desugar_hash_destructure(
5389 &mut self,
5390 keyword: &str,
5391 line: usize,
5392 pairs: Vec<MatchHashPair>,
5393 rhs: Expr,
5394 ) -> PerlResult<Statement> {
5395 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5396 let mut stmts: Vec<Statement> = Vec::new();
5397 stmts.push(destructure_stmt_from_var_decls(
5398 keyword,
5399 vec![VarDecl {
5400 sigil: Sigil::Scalar,
5401 name: tmp.clone(),
5402 initializer: Some(rhs),
5403 frozen: false,
5404 type_annotation: None,
5405 }],
5406 line,
5407 ));
5408
5409 for pair in pairs {
5410 match pair {
5411 MatchHashPair::KeyOnly { key } => {
5412 let exists_op = Expr {
5413 kind: ExprKind::Exists(Box::new(Expr {
5414 kind: ExprKind::ArrowDeref {
5415 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5416 index: Box::new(key),
5417 kind: DerefKind::Hash,
5418 },
5419 line,
5420 })),
5421 line,
5422 };
5423 stmts.push(destructure_stmt_unless_die(
5424 line,
5425 exists_op,
5426 "hash destructure: missing required key",
5427 ));
5428 }
5429 MatchHashPair::Capture { key, name } => {
5430 let init = Expr {
5431 kind: ExprKind::ArrowDeref {
5432 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5433 index: Box::new(key),
5434 kind: DerefKind::Hash,
5435 },
5436 line,
5437 };
5438 stmts.push(destructure_stmt_from_var_decls(
5439 keyword,
5440 vec![VarDecl {
5441 sigil: Sigil::Scalar,
5442 name,
5443 initializer: Some(init),
5444 frozen: false,
5445 type_annotation: None,
5446 }],
5447 line,
5448 ));
5449 }
5450 }
5451 }
5452
5453 Ok(Statement {
5454 label: None,
5455 kind: StmtKind::StmtGroup(stmts),
5456 line,
5457 })
5458 }
5459
5460 fn parse_my_our_local(
5461 &mut self,
5462 keyword: &str,
5463 allow_type_annotation: bool,
5464 ) -> PerlResult<Statement> {
5465 let line = self.peek_line();
5466 self.advance(); if keyword == "local"
5469 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
5470 {
5471 let target = self.parse_postfix()?;
5472 let mut initializer: Option<Expr> = None;
5473 if self.eat(&Token::Assign) {
5474 initializer = Some(self.parse_expression()?);
5475 } else if matches!(
5476 self.peek(),
5477 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
5478 ) {
5479 if matches!(&target.kind, ExprKind::Typeglob(_)) {
5480 return Err(self.syntax_err(
5481 "compound assignment on typeglob declaration is not supported",
5482 self.peek_line(),
5483 ));
5484 }
5485 let op = match self.peek().clone() {
5486 Token::OrAssign => BinOp::LogOr,
5487 Token::DefinedOrAssign => BinOp::DefinedOr,
5488 Token::AndAssign => BinOp::LogAnd,
5489 _ => unreachable!(),
5490 };
5491 self.advance();
5492 let rhs = self.parse_assign_expr()?;
5493 let tgt_line = target.line;
5494 initializer = Some(Expr {
5495 kind: ExprKind::CompoundAssign {
5496 target: Box::new(target.clone()),
5497 op,
5498 value: Box::new(rhs),
5499 },
5500 line: tgt_line,
5501 });
5502 }
5503
5504 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
5505 decl.initializer = initializer;
5506 StmtKind::Local(vec![decl])
5507 } else {
5508 StmtKind::LocalExpr {
5509 target,
5510 initializer,
5511 }
5512 };
5513 let stmt = Statement {
5514 label: None,
5515 kind,
5516 line,
5517 };
5518 return self.parse_stmt_postfix_modifier(stmt);
5519 }
5520
5521 if matches!(self.peek(), Token::LBracket) {
5522 return self.parse_decl_array_destructure(keyword, line);
5523 }
5524 if matches!(self.peek(), Token::LBrace) {
5525 return self.parse_decl_hash_destructure(keyword, line);
5526 }
5527
5528 let mut decls = Vec::new();
5529
5530 if self.eat(&Token::LParen) {
5531 while !matches!(self.peek(), Token::RParen | Token::Eof) {
5533 let decl = self.parse_var_decl(allow_type_annotation)?;
5534 decls.push(decl);
5535 if !self.eat(&Token::Comma) {
5536 break;
5537 }
5538 }
5539 self.expect(&Token::RParen)?;
5540 } else {
5541 decls.push(self.parse_var_decl(allow_type_annotation)?);
5542 }
5543
5544 if self.eat(&Token::Assign) {
5546 if keyword == "our" && decls.len() == 1 {
5547 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
5548 self.advance();
5549 decls.push(self.parse_var_decl(allow_type_annotation)?);
5550 if !self.eat(&Token::Assign) {
5551 return Err(self.syntax_err(
5552 "expected `=` after `our` in chained our-declaration",
5553 self.peek_line(),
5554 ));
5555 }
5556 }
5557 }
5558 let rhs_start_pos = self.pos;
5559 let mut val = self.parse_expression()?;
5560 if !crate::compat_mode()
5569 && self.block_depth == 0
5570 && decls.len() == 1
5571 && matches!(decls[0].sigil, Sigil::Scalar)
5572 && !matches!(
5573 val.kind,
5574 ExprKind::CodeRef { .. }
5575 | ExprKind::SubroutineRef(_)
5576 | ExprKind::SubroutineCodeRef(_)
5577 | ExprKind::DynamicSubCodeRef(_)
5578 )
5579 {
5580 let rhs_end_pos = self.pos;
5581 let rhs_has_bare_positional = self.bare_positional_indices.contains(&rhs_start_pos)
5588 && rhs_start_pos < rhs_end_pos;
5589 if rhs_has_bare_positional {
5590 let val_line = val.line;
5591 val = Expr {
5592 kind: ExprKind::CodeRef {
5593 params: Vec::new(),
5594 body: vec![Statement {
5595 label: None,
5596 kind: StmtKind::Expression(val),
5597 line: val_line,
5598 }],
5599 },
5600 line: val_line,
5601 };
5602 }
5603 }
5604 if !crate::compat_mode() && decls.len() == 1 {
5607 let decl = &decls[0];
5608 let target_kind = match decl.sigil {
5609 Sigil::Scalar => ExprKind::ScalarVar(decl.name.clone()),
5610 Sigil::Array => ExprKind::ArrayVar(decl.name.clone()),
5611 Sigil::Hash => ExprKind::HashVar(decl.name.clone()),
5612 Sigil::Typeglob => {
5613 if decls.len() == 1 {
5615 decls[0].initializer = Some(val);
5616 } else {
5617 for d in &mut decls {
5618 d.initializer = Some(val.clone());
5619 }
5620 }
5621 return Ok(Statement {
5622 label: None,
5623 kind: match keyword {
5624 "my" => StmtKind::My(decls),
5625 "mysync" => StmtKind::MySync(decls),
5626 "our" => StmtKind::Our(decls),
5627 "oursync" => StmtKind::OurSync(decls),
5628 "local" => StmtKind::Local(decls),
5629 "state" => StmtKind::State(decls),
5630 _ => unreachable!(),
5631 },
5632 line,
5633 });
5634 }
5635 };
5636 let target = Expr {
5637 kind: target_kind,
5638 line,
5639 };
5640 self.validate_assignment(&target, &val, line)?;
5641 }
5642 if decls.len() == 1 {
5643 decls[0].initializer = Some(val);
5644 } else {
5645 for decl in &mut decls {
5646 decl.initializer = Some(val.clone());
5647 }
5648 }
5649 } else if decls.len() == 1 {
5650 let op = match self.peek().clone() {
5652 Token::OrAssign => Some(BinOp::LogOr),
5653 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
5654 Token::AndAssign => Some(BinOp::LogAnd),
5655 _ => None,
5656 };
5657 if let Some(op) = op {
5658 let d = &decls[0];
5659 if matches!(d.sigil, Sigil::Typeglob) {
5660 return Err(self.syntax_err(
5661 "compound assignment on typeglob declaration is not supported",
5662 self.peek_line(),
5663 ));
5664 }
5665 self.advance();
5666 let rhs = self.parse_assign_expr()?;
5667 let target = Expr {
5668 kind: match d.sigil {
5669 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
5670 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
5671 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
5672 Sigil::Typeglob => unreachable!(),
5673 },
5674 line,
5675 };
5676 decls[0].initializer = Some(Expr {
5677 kind: ExprKind::CompoundAssign {
5678 target: Box::new(target),
5679 op,
5680 value: Box::new(rhs),
5681 },
5682 line,
5683 });
5684 }
5685 }
5686
5687 let kind = match keyword {
5688 "my" => StmtKind::My(decls),
5689 "mysync" => StmtKind::MySync(decls),
5690 "our" => StmtKind::Our(decls),
5691 "oursync" => StmtKind::OurSync(decls),
5692 "local" => StmtKind::Local(decls),
5693 "state" => StmtKind::State(decls),
5694 _ => unreachable!(),
5695 };
5696 let stmt = Statement {
5697 label: None,
5698 kind,
5699 line,
5700 };
5701 self.parse_stmt_postfix_modifier(stmt)
5703 }
5704
5705 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> PerlResult<VarDecl> {
5706 let mut decl = match self.advance() {
5707 (Token::ScalarVar(name), _) => VarDecl {
5708 sigil: Sigil::Scalar,
5709 name,
5710 initializer: None,
5711 frozen: false,
5712 type_annotation: None,
5713 },
5714 (Token::ArrayVar(name), _) => VarDecl {
5715 sigil: Sigil::Array,
5716 name,
5717 initializer: None,
5718 frozen: false,
5719 type_annotation: None,
5720 },
5721 (Token::HashVar(name), line) => {
5722 if !crate::compat_mode() {
5723 self.check_hash_shadows_reserved(&name, line)?;
5724 }
5725 VarDecl {
5726 sigil: Sigil::Hash,
5727 name,
5728 initializer: None,
5729 frozen: false,
5730 type_annotation: None,
5731 }
5732 }
5733 (Token::Star, _line) => {
5734 let name = match self.advance() {
5735 (Token::Ident(n), _) => n,
5736 (tok, l) => {
5737 return Err(self
5738 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
5739 }
5740 };
5741 VarDecl {
5742 sigil: Sigil::Typeglob,
5743 name,
5744 initializer: None,
5745 frozen: false,
5746 type_annotation: None,
5747 }
5748 }
5749 (Token::Ident(ref kw), _) if kw == "undef" => VarDecl {
5754 sigil: Sigil::Scalar,
5755 name: format!("__undef_sink_{}", self.pos),
5759 initializer: None,
5760 frozen: false,
5761 type_annotation: None,
5762 },
5763 (tok, line) => {
5764 return Err(self.syntax_err(
5765 format!("Expected variable in declaration, got {:?}", tok),
5766 line,
5767 ));
5768 }
5769 };
5770 if allow_type_annotation && self.eat(&Token::Colon) {
5771 let ty = self.parse_type_name()?;
5772 if decl.sigil != Sigil::Scalar {
5773 return Err(self.syntax_err(
5774 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
5775 self.peek_line(),
5776 ));
5777 }
5778 decl.type_annotation = Some(ty);
5779 }
5780 Ok(decl)
5781 }
5782
5783 fn parse_type_name(&mut self) -> PerlResult<PerlTypeName> {
5784 match self.advance() {
5785 (Token::Ident(name), _) => match name.as_str() {
5786 "Int" => Ok(PerlTypeName::Int),
5787 "Str" => Ok(PerlTypeName::Str),
5788 "Float" => Ok(PerlTypeName::Float),
5789 "Bool" => Ok(PerlTypeName::Bool),
5790 "Array" => Ok(PerlTypeName::Array),
5791 "Hash" => Ok(PerlTypeName::Hash),
5792 "Ref" => Ok(PerlTypeName::Ref),
5793 "Any" => Ok(PerlTypeName::Any),
5794 _ => Ok(PerlTypeName::Struct(name)),
5795 },
5796 (tok, err_line) => Err(self.syntax_err(
5797 format!("Expected type name after `:`, got {:?}", tok),
5798 err_line,
5799 )),
5800 }
5801 }
5802
5803 fn parse_package(&mut self) -> PerlResult<Statement> {
5804 let line = self.peek_line();
5805 self.advance(); let name = match self.advance() {
5807 (Token::Ident(n), _) => n,
5808 (tok, line) => {
5809 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
5810 }
5811 };
5812 let mut full_name = name;
5814 while self.eat(&Token::PackageSep) {
5815 if let (Token::Ident(part), _) = self.advance() {
5816 full_name = format!("{}::{}", full_name, part);
5817 }
5818 }
5819 self.eat(&Token::Semicolon);
5820 Ok(Statement {
5821 label: None,
5822 kind: StmtKind::Package { name: full_name },
5823 line,
5824 })
5825 }
5826
5827 fn parse_use(&mut self) -> PerlResult<Statement> {
5828 let line = self.peek_line();
5829 self.advance(); let (tok, tok_line) = self.advance();
5831 match tok {
5832 Token::Float(v) => {
5833 self.eat(&Token::Semicolon);
5834 Ok(Statement {
5835 label: None,
5836 kind: StmtKind::UsePerlVersion { version: v },
5837 line,
5838 })
5839 }
5840 Token::Integer(n) => {
5841 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
5842 self.eat(&Token::Semicolon);
5843 Ok(Statement {
5844 label: None,
5845 kind: StmtKind::UsePerlVersion { version: n as f64 },
5846 line,
5847 })
5848 } else {
5849 Err(self.syntax_err(
5850 format!("Expected ';' after use VERSION (got {:?})", self.peek()),
5851 line,
5852 ))
5853 }
5854 }
5855 Token::Ident(n) => {
5856 let mut full_name = n;
5857 while self.eat(&Token::PackageSep) {
5858 if let (Token::Ident(part), _) = self.advance() {
5859 full_name = format!("{}::{}", full_name, part);
5860 }
5861 }
5862 if full_name == "overload" {
5863 let mut pairs = Vec::new();
5864 let mut parse_overload_pairs = |this: &mut Self| -> PerlResult<()> {
5865 loop {
5866 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
5867 {
5868 break;
5869 }
5870 let key_e = this.parse_assign_expr()?;
5871 this.expect(&Token::FatArrow)?;
5872 let val_e = this.parse_assign_expr()?;
5873 let key = this.expr_to_overload_key(&key_e)?;
5874 let val = this.expr_to_overload_sub(&val_e)?;
5875 pairs.push((key, val));
5876 if !this.eat(&Token::Comma) {
5877 break;
5878 }
5879 }
5880 Ok(())
5881 };
5882 if self.eat(&Token::LParen) {
5883 parse_overload_pairs(self)?;
5885 self.expect(&Token::RParen)?;
5886 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
5887 parse_overload_pairs(self)?;
5888 }
5889 self.eat(&Token::Semicolon);
5890 return Ok(Statement {
5891 label: None,
5892 kind: StmtKind::UseOverload { pairs },
5893 line,
5894 });
5895 }
5896 let mut imports = Vec::new();
5897 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
5898 && !self.next_is_new_statement_start(tok_line)
5899 {
5900 loop {
5901 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
5902 break;
5903 }
5904 imports.push(self.parse_expression()?);
5905 if !self.eat(&Token::Comma) {
5906 break;
5907 }
5908 }
5909 }
5910 self.eat(&Token::Semicolon);
5911 Ok(Statement {
5912 label: None,
5913 kind: StmtKind::Use {
5914 module: full_name,
5915 imports,
5916 },
5917 line,
5918 })
5919 }
5920 other => Err(self.syntax_err(
5921 format!("Expected module name or version after use, got {:?}", other),
5922 tok_line,
5923 )),
5924 }
5925 }
5926
5927 fn parse_no(&mut self) -> PerlResult<Statement> {
5928 let line = self.peek_line();
5929 self.advance(); let module = match self.advance() {
5931 (Token::Ident(n), tok_line) => (n, tok_line),
5932 (tok, line) => {
5933 return Err(self.syntax_err(
5934 format!("Expected module name after no, got {:?}", tok),
5935 line,
5936 ))
5937 }
5938 };
5939 let (module_name, tok_line) = module;
5940 let mut full_name = module_name;
5941 while self.eat(&Token::PackageSep) {
5942 if let (Token::Ident(part), _) = self.advance() {
5943 full_name = format!("{}::{}", full_name, part);
5944 }
5945 }
5946 let mut imports = Vec::new();
5947 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
5948 && !self.next_is_new_statement_start(tok_line)
5949 {
5950 loop {
5951 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
5952 break;
5953 }
5954 imports.push(self.parse_expression()?);
5955 if !self.eat(&Token::Comma) {
5956 break;
5957 }
5958 }
5959 }
5960 self.eat(&Token::Semicolon);
5961 Ok(Statement {
5962 label: None,
5963 kind: StmtKind::No {
5964 module: full_name,
5965 imports,
5966 },
5967 line,
5968 })
5969 }
5970
5971 fn parse_return(&mut self) -> PerlResult<Statement> {
5972 let line = self.peek_line();
5973 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
5980 || self.peek_is_postfix_stmt_modifier_keyword()
5981 {
5982 None
5983 } else {
5984 let first = self.parse_assign_expr()?;
5989 if matches!(self.peek(), Token::Comma | Token::FatArrow) {
5990 let mut items = vec![first];
5991 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
5992 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
5993 || self.peek_is_postfix_stmt_modifier_keyword()
5994 {
5995 break;
5996 }
5997 items.push(self.parse_assign_expr()?);
5998 }
5999 let line = items.first().map(|e| e.line).unwrap_or(line);
6000 Some(Expr {
6001 kind: ExprKind::List(items),
6002 line,
6003 })
6004 } else {
6005 Some(first)
6006 }
6007 };
6008 let stmt = Statement {
6010 label: None,
6011 kind: StmtKind::Return(val),
6012 line,
6013 };
6014 if let Token::Ident(ref kw) = self.peek().clone() {
6015 match kw.as_str() {
6016 "if" => {
6017 self.advance();
6018 let cond = self.parse_expression()?;
6019 self.eat(&Token::Semicolon);
6020 return Ok(Statement {
6021 label: None,
6022 kind: StmtKind::If {
6023 condition: cond,
6024 body: vec![stmt],
6025 elsifs: vec![],
6026 else_block: None,
6027 },
6028 line,
6029 });
6030 }
6031 "unless" => {
6032 self.advance();
6033 let cond = self.parse_expression()?;
6034 self.eat(&Token::Semicolon);
6035 return Ok(Statement {
6036 label: None,
6037 kind: StmtKind::Unless {
6038 condition: cond,
6039 body: vec![stmt],
6040 else_block: None,
6041 },
6042 line,
6043 });
6044 }
6045 _ => {}
6046 }
6047 }
6048 self.eat(&Token::Semicolon);
6049 Ok(stmt)
6050 }
6051
6052 fn parse_expression(&mut self) -> PerlResult<Expr> {
6055 self.parse_comma_expr()
6056 }
6057
6058 fn parse_comma_expr(&mut self) -> PerlResult<Expr> {
6059 let expr = self.parse_or_word()?;
6067 let mut exprs = vec![expr];
6068 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6069 if matches!(
6070 self.peek(),
6071 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
6072 ) {
6073 break; }
6075 exprs.push(self.parse_or_word()?);
6076 }
6077 if exprs.len() == 1 {
6078 return Ok(exprs.pop().unwrap());
6079 }
6080 let line = exprs[0].line;
6081 Ok(Expr {
6082 kind: ExprKind::List(exprs),
6083 line,
6084 })
6085 }
6086
6087 fn parse_assign_expr(&mut self) -> PerlResult<Expr> {
6088 let expr = self.parse_ternary()?;
6089 let line = expr.line;
6090
6091 match self.peek().clone() {
6092 Token::Assign => {
6093 self.advance();
6094 let right = self.parse_assign_expr()?;
6095 if let ExprKind::MethodCall { ref args, .. } = expr.kind {
6097 if args.is_empty() {
6098 let ExprKind::MethodCall {
6100 object,
6101 method,
6102 super_call,
6103 ..
6104 } = expr.kind
6105 else {
6106 unreachable!()
6107 };
6108 return Ok(Expr {
6109 kind: ExprKind::MethodCall {
6110 object,
6111 method,
6112 args: vec![right],
6113 super_call,
6114 },
6115 line,
6116 });
6117 }
6118 }
6119 self.validate_assignment(&expr, &right, line)?;
6120 Ok(Expr {
6121 kind: ExprKind::Assign {
6122 target: Box::new(expr),
6123 value: Box::new(right),
6124 },
6125 line,
6126 })
6127 }
6128 Token::PlusAssign => {
6129 self.advance();
6130 let r = self.parse_assign_expr()?;
6131 Ok(Expr {
6132 kind: ExprKind::CompoundAssign {
6133 target: Box::new(expr),
6134 op: BinOp::Add,
6135 value: Box::new(r),
6136 },
6137 line,
6138 })
6139 }
6140 Token::MinusAssign => {
6141 self.advance();
6142 let r = self.parse_assign_expr()?;
6143 Ok(Expr {
6144 kind: ExprKind::CompoundAssign {
6145 target: Box::new(expr),
6146 op: BinOp::Sub,
6147 value: Box::new(r),
6148 },
6149 line,
6150 })
6151 }
6152 Token::MulAssign => {
6153 self.advance();
6154 let r = self.parse_assign_expr()?;
6155 Ok(Expr {
6156 kind: ExprKind::CompoundAssign {
6157 target: Box::new(expr),
6158 op: BinOp::Mul,
6159 value: Box::new(r),
6160 },
6161 line,
6162 })
6163 }
6164 Token::DivAssign => {
6165 self.advance();
6166 let r = self.parse_assign_expr()?;
6167 Ok(Expr {
6168 kind: ExprKind::CompoundAssign {
6169 target: Box::new(expr),
6170 op: BinOp::Div,
6171 value: Box::new(r),
6172 },
6173 line,
6174 })
6175 }
6176 Token::ModAssign => {
6177 self.advance();
6178 let r = self.parse_assign_expr()?;
6179 Ok(Expr {
6180 kind: ExprKind::CompoundAssign {
6181 target: Box::new(expr),
6182 op: BinOp::Mod,
6183 value: Box::new(r),
6184 },
6185 line,
6186 })
6187 }
6188 Token::PowAssign => {
6189 self.advance();
6190 let r = self.parse_assign_expr()?;
6191 Ok(Expr {
6192 kind: ExprKind::CompoundAssign {
6193 target: Box::new(expr),
6194 op: BinOp::Pow,
6195 value: Box::new(r),
6196 },
6197 line,
6198 })
6199 }
6200 Token::DotAssign => {
6201 self.advance();
6202 let r = self.parse_assign_expr()?;
6203 Ok(Expr {
6204 kind: ExprKind::CompoundAssign {
6205 target: Box::new(expr),
6206 op: BinOp::Concat,
6207 value: Box::new(r),
6208 },
6209 line,
6210 })
6211 }
6212 Token::BitAndAssign => {
6213 self.advance();
6214 let r = self.parse_assign_expr()?;
6215 Ok(Expr {
6216 kind: ExprKind::CompoundAssign {
6217 target: Box::new(expr),
6218 op: BinOp::BitAnd,
6219 value: Box::new(r),
6220 },
6221 line,
6222 })
6223 }
6224 Token::BitOrAssign => {
6225 self.advance();
6226 let r = self.parse_assign_expr()?;
6227 Ok(Expr {
6228 kind: ExprKind::CompoundAssign {
6229 target: Box::new(expr),
6230 op: BinOp::BitOr,
6231 value: Box::new(r),
6232 },
6233 line,
6234 })
6235 }
6236 Token::XorAssign => {
6237 self.advance();
6238 let r = self.parse_assign_expr()?;
6239 Ok(Expr {
6240 kind: ExprKind::CompoundAssign {
6241 target: Box::new(expr),
6242 op: BinOp::BitXor,
6243 value: Box::new(r),
6244 },
6245 line,
6246 })
6247 }
6248 Token::ShiftLeftAssign => {
6249 self.advance();
6250 let r = self.parse_assign_expr()?;
6251 Ok(Expr {
6252 kind: ExprKind::CompoundAssign {
6253 target: Box::new(expr),
6254 op: BinOp::ShiftLeft,
6255 value: Box::new(r),
6256 },
6257 line,
6258 })
6259 }
6260 Token::ShiftRightAssign => {
6261 self.advance();
6262 let r = self.parse_assign_expr()?;
6263 Ok(Expr {
6264 kind: ExprKind::CompoundAssign {
6265 target: Box::new(expr),
6266 op: BinOp::ShiftRight,
6267 value: Box::new(r),
6268 },
6269 line,
6270 })
6271 }
6272 Token::OrAssign => {
6273 self.advance();
6274 let r = self.parse_assign_expr()?;
6275 Ok(Expr {
6276 kind: ExprKind::CompoundAssign {
6277 target: Box::new(expr),
6278 op: BinOp::LogOr,
6279 value: Box::new(r),
6280 },
6281 line,
6282 })
6283 }
6284 Token::DefinedOrAssign => {
6285 self.advance();
6286 let r = self.parse_assign_expr()?;
6287 Ok(Expr {
6288 kind: ExprKind::CompoundAssign {
6289 target: Box::new(expr),
6290 op: BinOp::DefinedOr,
6291 value: Box::new(r),
6292 },
6293 line,
6294 })
6295 }
6296 Token::AndAssign => {
6297 self.advance();
6298 let r = self.parse_assign_expr()?;
6299 Ok(Expr {
6300 kind: ExprKind::CompoundAssign {
6301 target: Box::new(expr),
6302 op: BinOp::LogAnd,
6303 value: Box::new(r),
6304 },
6305 line,
6306 })
6307 }
6308 _ => Ok(expr),
6309 }
6310 }
6311
6312 fn parse_ternary(&mut self) -> PerlResult<Expr> {
6313 let expr = self.parse_pipe_forward()?;
6314 if self.eat(&Token::Question) {
6315 let line = expr.line;
6316 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
6317 let then_expr = self.parse_assign_expr();
6318 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
6319 let then_expr = then_expr?;
6320 self.expect(&Token::Colon)?;
6321 let else_expr = self.parse_assign_expr()?;
6322 return Ok(Expr {
6323 kind: ExprKind::Ternary {
6324 condition: Box::new(expr),
6325 then_expr: Box::new(then_expr),
6326 else_expr: Box::new(else_expr),
6327 },
6328 line,
6329 });
6330 }
6331 Ok(expr)
6332 }
6333
6334 fn parse_pipe_forward(&mut self) -> PerlResult<Expr> {
6340 let mut left = self.parse_range()?;
6347 if self.no_pipe_forward_depth > 0 {
6353 return Ok(left);
6354 }
6355 while matches!(self.peek(), Token::PipeForward) {
6356 if crate::compat_mode() {
6357 return Err(self.syntax_err(
6358 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
6359 left.line,
6360 ));
6361 }
6362 let line = left.line;
6363 self.advance();
6364 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
6367 let right_result = self.parse_range();
6371 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
6372 let right = right_result?;
6373 left = self.pipe_forward_apply(left, right, line)?;
6374 }
6375 Ok(left)
6376 }
6377
6378 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> PerlResult<Expr> {
6400 let Expr { kind, line: rline } = rhs;
6401 let new_kind = match kind {
6402 ExprKind::FuncCall { name, mut args } => {
6404 let dispatch_name: &str = name.strip_prefix("CORE::").unwrap_or(name.as_str());
6407 match dispatch_name {
6408 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
6409 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
6410 | "shuffled" | "frequencies" | "freq" | "interleave" | "ddump"
6411 | "stringify" | "str" | "lines" | "words" | "chars" | "digits" | "letters"
6412 | "letters_uc" | "letters_lc" | "punctuation" | "numbers" | "graphemes"
6413 | "columns" | "sentences" | "paragraphs" | "sections" | "trim" | "avg"
6414 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml" | "to_html"
6415 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
6416 | "to_markdown" | "to_table" | "xopen" | "clip" | "sparkline" | "bar_chart"
6417 | "flame" | "stddev" | "squared" | "sq" | "square" | "cubed" | "cb"
6418 | "cube" | "normalize" | "snake_case" | "camel_case" | "kebab_case" => {
6419 if args.is_empty() {
6420 args.push(lhs);
6421 } else {
6422 args[0] = lhs;
6423 }
6424 }
6425 "chunked" | "windowed" => {
6426 if args.is_empty() {
6427 return Err(self.syntax_err(
6428 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
6429 line,
6430 ));
6431 }
6432 args.insert(0, lhs);
6433 }
6434 "reduce" | "fold" => {
6435 args.push(lhs);
6436 }
6437 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
6438 args.push(lhs);
6444 }
6445 "enumerate" | "dedup" => {
6446 args.insert(0, lhs);
6449 }
6450 "clamp" => {
6451 args.push(lhs);
6453 }
6454 n if Self::is_block_then_list_pipe_builtin(n) => {
6455 if args.len() < 2 {
6456 return Err(self.syntax_err(
6457 format!(
6458 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
6459 ),
6460 line,
6461 ));
6462 }
6463 args[1] = lhs;
6464 }
6465 "take" | "head" | "tail" | "drop" => {
6466 if args.is_empty() {
6467 return Err(self.syntax_err(
6468 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
6469 line,
6470 ));
6471 }
6472 args.insert(0, lhs);
6474 }
6475 _ => {
6476 if self.thread_last_mode {
6477 args.push(lhs);
6478 } else {
6479 args.insert(0, lhs);
6480 }
6481 }
6482 }
6483 ExprKind::FuncCall { name, args }
6484 }
6485 ExprKind::MethodCall {
6486 object,
6487 method,
6488 mut args,
6489 super_call,
6490 } => {
6491 if self.thread_last_mode {
6492 args.push(lhs);
6493 } else {
6494 args.insert(0, lhs);
6495 }
6496 ExprKind::MethodCall {
6497 object,
6498 method,
6499 args,
6500 super_call,
6501 }
6502 }
6503 ExprKind::IndirectCall {
6504 target,
6505 mut args,
6506 ampersand,
6507 pass_caller_arglist: _,
6508 } => {
6509 if self.thread_last_mode {
6510 args.push(lhs);
6511 } else {
6512 args.insert(0, lhs);
6513 }
6514 ExprKind::IndirectCall {
6515 target,
6516 args,
6517 ampersand,
6518 pass_caller_arglist: false,
6521 }
6522 }
6523
6524 ExprKind::Print { handle, mut args } => {
6526 if self.thread_last_mode {
6527 args.push(lhs);
6528 } else {
6529 args.insert(0, lhs);
6530 }
6531 ExprKind::Print { handle, args }
6532 }
6533 ExprKind::Say { handle, mut args } => {
6534 if self.thread_last_mode {
6535 args.push(lhs);
6536 } else {
6537 args.insert(0, lhs);
6538 }
6539 ExprKind::Say { handle, args }
6540 }
6541 ExprKind::Printf { handle, mut args } => {
6542 if self.thread_last_mode {
6543 args.push(lhs);
6544 } else {
6545 args.insert(0, lhs);
6546 }
6547 ExprKind::Printf { handle, args }
6548 }
6549 ExprKind::Die(mut args) => {
6550 if self.thread_last_mode {
6551 args.push(lhs);
6552 } else {
6553 args.insert(0, lhs);
6554 }
6555 ExprKind::Die(args)
6556 }
6557 ExprKind::Warn(mut args) => {
6558 if self.thread_last_mode {
6559 args.push(lhs);
6560 } else {
6561 args.insert(0, lhs);
6562 }
6563 ExprKind::Warn(args)
6564 }
6565
6566 ExprKind::Sprintf { format, mut args } => {
6572 if self.thread_last_mode {
6573 args.push(lhs);
6574 } else {
6575 args.insert(0, lhs);
6576 }
6577 ExprKind::Sprintf { format, args }
6578 }
6579
6580 ExprKind::System(mut args) => {
6582 if self.thread_last_mode {
6583 args.push(lhs);
6584 } else {
6585 args.insert(0, lhs);
6586 }
6587 ExprKind::System(args)
6588 }
6589 ExprKind::Exec(mut args) => {
6590 if self.thread_last_mode {
6591 args.push(lhs);
6592 } else {
6593 args.insert(0, lhs);
6594 }
6595 ExprKind::Exec(args)
6596 }
6597 ExprKind::Unlink(mut args) => {
6598 if self.thread_last_mode {
6599 args.push(lhs);
6600 } else {
6601 args.insert(0, lhs);
6602 }
6603 ExprKind::Unlink(args)
6604 }
6605 ExprKind::Chmod(mut args) => {
6606 if self.thread_last_mode {
6607 args.push(lhs);
6608 } else {
6609 args.insert(0, lhs);
6610 }
6611 ExprKind::Chmod(args)
6612 }
6613 ExprKind::Chown(mut args) => {
6614 if self.thread_last_mode {
6615 args.push(lhs);
6616 } else {
6617 args.insert(0, lhs);
6618 }
6619 ExprKind::Chown(args)
6620 }
6621 ExprKind::Glob(mut args) => {
6622 if self.thread_last_mode {
6623 args.push(lhs);
6624 } else {
6625 args.insert(0, lhs);
6626 }
6627 ExprKind::Glob(args)
6628 }
6629 ExprKind::Files(mut args) => {
6630 if self.thread_last_mode {
6631 args.push(lhs);
6632 } else {
6633 args.insert(0, lhs);
6634 }
6635 ExprKind::Files(args)
6636 }
6637 ExprKind::Filesf(mut args) => {
6638 if self.thread_last_mode {
6639 args.push(lhs);
6640 } else {
6641 args.insert(0, lhs);
6642 }
6643 ExprKind::Filesf(args)
6644 }
6645 ExprKind::FilesfRecursive(mut args) => {
6646 if self.thread_last_mode {
6647 args.push(lhs);
6648 } else {
6649 args.insert(0, lhs);
6650 }
6651 ExprKind::FilesfRecursive(args)
6652 }
6653 ExprKind::Dirs(mut args) => {
6654 if self.thread_last_mode {
6655 args.push(lhs);
6656 } else {
6657 args.insert(0, lhs);
6658 }
6659 ExprKind::Dirs(args)
6660 }
6661 ExprKind::DirsRecursive(mut args) => {
6662 if self.thread_last_mode {
6663 args.push(lhs);
6664 } else {
6665 args.insert(0, lhs);
6666 }
6667 ExprKind::DirsRecursive(args)
6668 }
6669 ExprKind::SymLinks(mut args) => {
6670 if self.thread_last_mode {
6671 args.push(lhs);
6672 } else {
6673 args.insert(0, lhs);
6674 }
6675 ExprKind::SymLinks(args)
6676 }
6677 ExprKind::Sockets(mut args) => {
6678 if self.thread_last_mode {
6679 args.push(lhs);
6680 } else {
6681 args.insert(0, lhs);
6682 }
6683 ExprKind::Sockets(args)
6684 }
6685 ExprKind::Pipes(mut args) => {
6686 if self.thread_last_mode {
6687 args.push(lhs);
6688 } else {
6689 args.insert(0, lhs);
6690 }
6691 ExprKind::Pipes(args)
6692 }
6693 ExprKind::BlockDevices(mut args) => {
6694 if self.thread_last_mode {
6695 args.push(lhs);
6696 } else {
6697 args.insert(0, lhs);
6698 }
6699 ExprKind::BlockDevices(args)
6700 }
6701 ExprKind::CharDevices(mut args) => {
6702 if self.thread_last_mode {
6703 args.push(lhs);
6704 } else {
6705 args.insert(0, lhs);
6706 }
6707 ExprKind::CharDevices(args)
6708 }
6709 ExprKind::GlobPar { mut args, progress } => {
6710 if self.thread_last_mode {
6711 args.push(lhs);
6712 } else {
6713 args.insert(0, lhs);
6714 }
6715 ExprKind::GlobPar { args, progress }
6716 }
6717 ExprKind::ParSed { mut args, progress } => {
6718 if self.thread_last_mode {
6719 args.push(lhs);
6720 } else {
6721 args.insert(0, lhs);
6722 }
6723 ExprKind::ParSed { args, progress }
6724 }
6725
6726 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
6728 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
6729 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
6730 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
6731 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
6732 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
6733 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
6734 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
6735 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
6736 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
6737 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
6738 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
6739 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
6740 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
6741 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
6742 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
6743 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
6744 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
6745 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
6746 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
6747 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
6748 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
6749 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
6750 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
6751 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
6752 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
6753 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
6754 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
6755 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
6756 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
6757 ExprKind::Rev(_) => ExprKind::Rev(Box::new(lhs)),
6758 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
6759 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
6760 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
6761 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
6762 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
6763 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
6764 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
6765 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
6766 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
6767 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
6768 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
6769 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
6770 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
6771 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
6772 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
6773 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
6774 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
6775 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
6776 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
6777 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
6778
6779 ExprKind::MapExpr {
6781 block,
6782 list: _,
6783 flatten_array_refs,
6784 stream,
6785 } => ExprKind::MapExpr {
6786 block,
6787 list: Box::new(lhs),
6788 flatten_array_refs,
6789 stream,
6790 },
6791 ExprKind::MapExprComma {
6792 expr,
6793 list: _,
6794 flatten_array_refs,
6795 stream,
6796 } => ExprKind::MapExprComma {
6797 expr,
6798 list: Box::new(lhs),
6799 flatten_array_refs,
6800 stream,
6801 },
6802 ExprKind::GrepExpr {
6803 block,
6804 list: _,
6805 keyword,
6806 } => ExprKind::GrepExpr {
6807 block,
6808 list: Box::new(lhs),
6809 keyword,
6810 },
6811 ExprKind::GrepExprComma {
6812 expr,
6813 list: _,
6814 keyword,
6815 } => ExprKind::GrepExprComma {
6816 expr,
6817 list: Box::new(lhs),
6818 keyword,
6819 },
6820 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
6821 block,
6822 list: Box::new(lhs),
6823 },
6824 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
6825 cmp,
6826 list: Box::new(lhs),
6827 },
6828 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
6829 separator,
6830 list: Box::new(lhs),
6831 },
6832 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
6833 block,
6834 list: Box::new(lhs),
6835 },
6836 ExprKind::PMapExpr {
6837 block,
6838 list: _,
6839 progress,
6840 flat_outputs,
6841 on_cluster,
6842 stream,
6843 } => ExprKind::PMapExpr {
6844 block,
6845 list: Box::new(lhs),
6846 progress,
6847 flat_outputs,
6848 on_cluster,
6849 stream,
6850 },
6851 ExprKind::PMapChunkedExpr {
6852 chunk_size,
6853 block,
6854 list: _,
6855 progress,
6856 } => ExprKind::PMapChunkedExpr {
6857 chunk_size,
6858 block,
6859 list: Box::new(lhs),
6860 progress,
6861 },
6862 ExprKind::PGrepExpr {
6863 block,
6864 list: _,
6865 progress,
6866 stream,
6867 } => ExprKind::PGrepExpr {
6868 block,
6869 list: Box::new(lhs),
6870 progress,
6871 stream,
6872 },
6873 ExprKind::PForExpr {
6874 block,
6875 list: _,
6876 progress,
6877 } => ExprKind::PForExpr {
6878 block,
6879 list: Box::new(lhs),
6880 progress,
6881 },
6882 ExprKind::PSortExpr {
6883 cmp,
6884 list: _,
6885 progress,
6886 } => ExprKind::PSortExpr {
6887 cmp,
6888 list: Box::new(lhs),
6889 progress,
6890 },
6891 ExprKind::PReduceExpr {
6892 block,
6893 list: _,
6894 progress,
6895 } => ExprKind::PReduceExpr {
6896 block,
6897 list: Box::new(lhs),
6898 progress,
6899 },
6900 ExprKind::PcacheExpr {
6901 block,
6902 list: _,
6903 progress,
6904 } => ExprKind::PcacheExpr {
6905 block,
6906 list: Box::new(lhs),
6907 progress,
6908 },
6909 ExprKind::PReduceInitExpr {
6910 init,
6911 block,
6912 list: _,
6913 progress,
6914 } => ExprKind::PReduceInitExpr {
6915 init,
6916 block,
6917 list: Box::new(lhs),
6918 progress,
6919 },
6920 ExprKind::PMapReduceExpr {
6921 map_block,
6922 reduce_block,
6923 list: _,
6924 progress,
6925 } => ExprKind::PMapReduceExpr {
6926 map_block,
6927 reduce_block,
6928 list: Box::new(lhs),
6929 progress,
6930 },
6931
6932 ExprKind::Push { array, mut values } => {
6937 values.insert(0, lhs);
6938 ExprKind::Push { array, values }
6939 }
6940 ExprKind::Unshift { array, mut values } => {
6941 values.insert(0, lhs);
6942 ExprKind::Unshift { array, values }
6943 }
6944
6945 ExprKind::SplitExpr {
6947 pattern,
6948 string: _,
6949 limit,
6950 } => ExprKind::SplitExpr {
6951 pattern,
6952 string: Box::new(lhs),
6953 limit,
6954 },
6955
6956 ExprKind::Substitution {
6960 pattern,
6961 replacement,
6962 mut flags,
6963 expr: _,
6964 delim,
6965 } => {
6966 if !flags.contains('r') {
6967 flags.push('r');
6968 }
6969 ExprKind::Substitution {
6970 expr: Box::new(lhs),
6971 pattern,
6972 replacement,
6973 flags,
6974 delim,
6975 }
6976 }
6977 ExprKind::Transliterate {
6978 from,
6979 to,
6980 mut flags,
6981 expr: _,
6982 delim,
6983 } => {
6984 if !flags.contains('r') {
6985 flags.push('r');
6986 }
6987 ExprKind::Transliterate {
6988 expr: Box::new(lhs),
6989 from,
6990 to,
6991 flags,
6992 delim,
6993 }
6994 }
6995 ExprKind::Match {
6996 pattern,
6997 flags,
6998 scalar_g,
6999 expr: _,
7000 delim,
7001 } => ExprKind::Match {
7002 expr: Box::new(lhs),
7003 pattern,
7004 flags,
7005 scalar_g,
7006 delim,
7007 },
7008 ExprKind::Regex(pattern, flags) => ExprKind::Match {
7010 expr: Box::new(lhs),
7011 pattern,
7012 flags,
7013 scalar_g: false,
7014 delim: '/',
7015 },
7016
7017 ExprKind::Bareword(name) => match name.as_str() {
7019 "reverse" => {
7020 if crate::no_interop_mode() {
7021 return Err(self.syntax_err(
7022 "stryke uses `rev` instead of `reverse` (--no-interop)",
7023 line,
7024 ));
7025 }
7026 ExprKind::ReverseExpr(Box::new(lhs))
7027 }
7028 "rv" | "reversed" | "rev" => ExprKind::Rev(Box::new(lhs)),
7029 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
7030 name: "uniq".to_string(),
7031 args: vec![lhs],
7032 },
7033 "fl" | "flatten" => ExprKind::FuncCall {
7034 name: "flatten".to_string(),
7035 args: vec![lhs],
7036 },
7037 _ => ExprKind::FuncCall {
7038 name,
7039 args: vec![lhs],
7040 },
7041 },
7042
7043 kind @ (ExprKind::ScalarVar(_)
7045 | ExprKind::ArrayElement { .. }
7046 | ExprKind::HashElement { .. }
7047 | ExprKind::Deref { .. }
7048 | ExprKind::ArrowDeref { .. }
7049 | ExprKind::CodeRef { .. }
7050 | ExprKind::SubroutineRef(_)
7051 | ExprKind::SubroutineCodeRef(_)
7052 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
7053 target: Box::new(Expr { kind, line: rline }),
7054 args: vec![lhs],
7055 ampersand: false,
7056 pass_caller_arglist: false,
7057 },
7058
7059 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
7063 ExprKind::IndirectCall {
7064 target: inner,
7065 args: vec![lhs],
7066 ampersand: false,
7067 pass_caller_arglist: false,
7068 }
7069 }
7070
7071 other => {
7072 return Err(self.syntax_err(
7073 format!(
7074 "right-hand side of `|>` must be a call, builtin, or coderef \
7075 expression (got {})",
7076 Self::expr_kind_name(&other)
7077 ),
7078 line,
7079 ));
7080 }
7081 };
7082 Ok(Expr {
7083 kind: new_kind,
7084 line,
7085 })
7086 }
7087
7088 fn expr_kind_name(kind: &ExprKind) -> &'static str {
7090 match kind {
7091 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
7092 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
7093 ExprKind::BinOp { .. } => "binary expression",
7094 ExprKind::UnaryOp { .. } => "unary expression",
7095 ExprKind::Ternary { .. } => "ternary expression",
7096 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
7097 ExprKind::List(_) => "list expression",
7098 ExprKind::Range { .. } => "range expression",
7099 _ => "expression",
7100 }
7101 }
7102
7103 fn parse_or_word(&mut self) -> PerlResult<Expr> {
7105 let mut left = self.parse_and_word()?;
7106 while matches!(self.peek(), Token::LogOrWord) {
7107 let line = left.line;
7108 self.advance();
7109 let right = self.parse_and_word()?;
7110 left = Expr {
7111 kind: ExprKind::BinOp {
7112 left: Box::new(left),
7113 op: BinOp::LogOrWord,
7114 right: Box::new(right),
7115 },
7116 line,
7117 };
7118 }
7119 Ok(left)
7120 }
7121
7122 fn parse_and_word(&mut self) -> PerlResult<Expr> {
7123 let mut left = self.parse_not_word()?;
7124 while matches!(self.peek(), Token::LogAndWord) {
7125 let line = left.line;
7126 self.advance();
7127 let right = self.parse_not_word()?;
7128 left = Expr {
7129 kind: ExprKind::BinOp {
7130 left: Box::new(left),
7131 op: BinOp::LogAndWord,
7132 right: Box::new(right),
7133 },
7134 line,
7135 };
7136 }
7137 Ok(left)
7138 }
7139
7140 fn parse_not_word(&mut self) -> PerlResult<Expr> {
7141 if matches!(self.peek(), Token::LogNotWord) {
7142 let line = self.peek_line();
7143 self.advance();
7144 let expr = self.parse_not_word()?;
7145 return Ok(Expr {
7146 kind: ExprKind::UnaryOp {
7147 op: UnaryOp::LogNotWord,
7148 expr: Box::new(expr),
7149 },
7150 line,
7151 });
7152 }
7153 self.parse_assign_expr()
7156 }
7157
7158 fn parse_log_or(&mut self) -> PerlResult<Expr> {
7159 let mut left = self.parse_log_and()?;
7160 loop {
7161 let op = match self.peek() {
7162 Token::LogOr => BinOp::LogOr,
7163 Token::DefinedOr => BinOp::DefinedOr,
7164 _ => break,
7165 };
7166 let line = left.line;
7167 self.advance();
7168 let right = self.parse_log_and()?;
7169 left = Expr {
7170 kind: ExprKind::BinOp {
7171 left: Box::new(left),
7172 op,
7173 right: Box::new(right),
7174 },
7175 line,
7176 };
7177 }
7178 Ok(left)
7179 }
7180
7181 fn parse_log_and(&mut self) -> PerlResult<Expr> {
7182 let mut left = self.parse_bit_or()?;
7183 while matches!(self.peek(), Token::LogAnd) {
7184 let line = left.line;
7185 self.advance();
7186 let right = self.parse_bit_or()?;
7187 left = Expr {
7188 kind: ExprKind::BinOp {
7189 left: Box::new(left),
7190 op: BinOp::LogAnd,
7191 right: Box::new(right),
7192 },
7193 line,
7194 };
7195 }
7196 Ok(left)
7197 }
7198
7199 fn parse_bit_or(&mut self) -> PerlResult<Expr> {
7200 let mut left = self.parse_bit_xor()?;
7201 while matches!(self.peek(), Token::BitOr) {
7202 let line = left.line;
7203 self.advance();
7204 let right = self.parse_bit_xor()?;
7205 left = Expr {
7206 kind: ExprKind::BinOp {
7207 left: Box::new(left),
7208 op: BinOp::BitOr,
7209 right: Box::new(right),
7210 },
7211 line,
7212 };
7213 }
7214 Ok(left)
7215 }
7216
7217 fn parse_bit_xor(&mut self) -> PerlResult<Expr> {
7218 let mut left = self.parse_bit_and()?;
7219 while matches!(self.peek(), Token::BitXor) {
7220 let line = left.line;
7221 self.advance();
7222 let right = self.parse_bit_and()?;
7223 left = Expr {
7224 kind: ExprKind::BinOp {
7225 left: Box::new(left),
7226 op: BinOp::BitXor,
7227 right: Box::new(right),
7228 },
7229 line,
7230 };
7231 }
7232 Ok(left)
7233 }
7234
7235 fn parse_bit_and(&mut self) -> PerlResult<Expr> {
7236 let mut left = self.parse_equality()?;
7237 while matches!(self.peek(), Token::BitAnd) {
7238 let line = left.line;
7239 self.advance();
7240 let right = self.parse_equality()?;
7241 left = Expr {
7242 kind: ExprKind::BinOp {
7243 left: Box::new(left),
7244 op: BinOp::BitAnd,
7245 right: Box::new(right),
7246 },
7247 line,
7248 };
7249 }
7250 Ok(left)
7251 }
7252
7253 fn parse_equality(&mut self) -> PerlResult<Expr> {
7254 let mut left = self.parse_comparison()?;
7255 loop {
7256 let op = match self.peek() {
7257 Token::NumEq => BinOp::NumEq,
7258 Token::NumNe => BinOp::NumNe,
7259 Token::StrEq => BinOp::StrEq,
7260 Token::StrNe => BinOp::StrNe,
7261 Token::Spaceship => BinOp::Spaceship,
7262 Token::StrCmp => BinOp::StrCmp,
7263 _ => break,
7264 };
7265 let line = left.line;
7266 self.advance();
7267 let right = self.parse_comparison()?;
7268 left = Expr {
7269 kind: ExprKind::BinOp {
7270 left: Box::new(left),
7271 op,
7272 right: Box::new(right),
7273 },
7274 line,
7275 };
7276 }
7277 Ok(left)
7278 }
7279
7280 fn parse_comparison(&mut self) -> PerlResult<Expr> {
7281 let left = self.parse_shift()?;
7282 let first_op = match self.peek() {
7283 Token::NumLt => BinOp::NumLt,
7284 Token::NumGt => BinOp::NumGt,
7285 Token::NumLe => BinOp::NumLe,
7286 Token::NumGe => BinOp::NumGe,
7287 Token::StrLt => BinOp::StrLt,
7288 Token::StrGt => BinOp::StrGt,
7289 Token::StrLe => BinOp::StrLe,
7290 Token::StrGe => BinOp::StrGe,
7291 _ => return Ok(left),
7292 };
7293 let line = left.line;
7294 self.advance();
7295 let middle = self.parse_shift()?;
7296
7297 let second_op = match self.peek() {
7298 Token::NumLt => Some(BinOp::NumLt),
7299 Token::NumGt => Some(BinOp::NumGt),
7300 Token::NumLe => Some(BinOp::NumLe),
7301 Token::NumGe => Some(BinOp::NumGe),
7302 Token::StrLt => Some(BinOp::StrLt),
7303 Token::StrGt => Some(BinOp::StrGt),
7304 Token::StrLe => Some(BinOp::StrLe),
7305 Token::StrGe => Some(BinOp::StrGe),
7306 _ => None,
7307 };
7308
7309 if second_op.is_none() {
7310 return Ok(Expr {
7311 kind: ExprKind::BinOp {
7312 left: Box::new(left),
7313 op: first_op,
7314 right: Box::new(middle),
7315 },
7316 line,
7317 });
7318 }
7319
7320 let mut operands = vec![left, middle];
7323 let mut ops = vec![first_op];
7324
7325 loop {
7326 let op = match self.peek() {
7327 Token::NumLt => BinOp::NumLt,
7328 Token::NumGt => BinOp::NumGt,
7329 Token::NumLe => BinOp::NumLe,
7330 Token::NumGe => BinOp::NumGe,
7331 Token::StrLt => BinOp::StrLt,
7332 Token::StrGt => BinOp::StrGt,
7333 Token::StrLe => BinOp::StrLe,
7334 Token::StrGe => BinOp::StrGe,
7335 _ => break,
7336 };
7337 self.advance();
7338 ops.push(op);
7339 operands.push(self.parse_shift()?);
7340 }
7341
7342 let mut result = Expr {
7344 kind: ExprKind::BinOp {
7345 left: Box::new(operands[0].clone()),
7346 op: ops[0],
7347 right: Box::new(operands[1].clone()),
7348 },
7349 line,
7350 };
7351
7352 for i in 1..ops.len() {
7353 let cmp = Expr {
7354 kind: ExprKind::BinOp {
7355 left: Box::new(operands[i].clone()),
7356 op: ops[i],
7357 right: Box::new(operands[i + 1].clone()),
7358 },
7359 line,
7360 };
7361 result = Expr {
7362 kind: ExprKind::BinOp {
7363 left: Box::new(result),
7364 op: BinOp::LogAnd,
7365 right: Box::new(cmp),
7366 },
7367 line,
7368 };
7369 }
7370
7371 Ok(result)
7372 }
7373
7374 fn parse_shift(&mut self) -> PerlResult<Expr> {
7375 let mut left = self.parse_addition()?;
7376 loop {
7377 let op = match self.peek() {
7378 Token::ShiftLeft => BinOp::ShiftLeft,
7379 Token::ShiftRight => BinOp::ShiftRight,
7380 _ => break,
7381 };
7382 let line = left.line;
7383 self.advance();
7384 let right = self.parse_addition()?;
7385 left = Expr {
7386 kind: ExprKind::BinOp {
7387 left: Box::new(left),
7388 op,
7389 right: Box::new(right),
7390 },
7391 line,
7392 };
7393 }
7394 Ok(left)
7395 }
7396
7397 fn parse_addition(&mut self) -> PerlResult<Expr> {
7398 let mut left = self.parse_multiplication()?;
7399 loop {
7400 let op = match self.peek() {
7403 Token::Plus if self.peek_line() == self.prev_line() => BinOp::Add,
7404 Token::Minus if self.peek_line() == self.prev_line() => BinOp::Sub,
7405 Token::Dot => BinOp::Concat,
7406 _ => break,
7407 };
7408 let line = left.line;
7409 self.advance();
7410 let right = self.parse_multiplication()?;
7411 left = Expr {
7412 kind: ExprKind::BinOp {
7413 left: Box::new(left),
7414 op,
7415 right: Box::new(right),
7416 },
7417 line,
7418 };
7419 }
7420 Ok(left)
7421 }
7422
7423 fn parse_multiplication(&mut self) -> PerlResult<Expr> {
7424 let mut left = self.parse_regex_bind()?;
7425 loop {
7426 let op = match self.peek() {
7427 Token::Star => BinOp::Mul,
7428 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
7429 Token::Percent if self.peek_line() == self.prev_line() => BinOp::Mod,
7432 Token::X => {
7433 let line = left.line;
7434 let list_repeat = self.list_construct_close_pos == Some(self.pos);
7441 self.advance();
7442 let right = self.parse_regex_bind()?;
7443 left = Expr {
7444 kind: ExprKind::Repeat {
7445 expr: Box::new(left),
7446 count: Box::new(right),
7447 list_repeat,
7448 },
7449 line,
7450 };
7451 continue;
7452 }
7453 _ => break,
7454 };
7455 let line = left.line;
7456 self.advance();
7457 let right = self.parse_regex_bind()?;
7458 left = Expr {
7459 kind: ExprKind::BinOp {
7460 left: Box::new(left),
7461 op,
7462 right: Box::new(right),
7463 },
7464 line,
7465 };
7466 }
7467 Ok(left)
7468 }
7469
7470 fn parse_regex_bind(&mut self) -> PerlResult<Expr> {
7471 let left = self.parse_unary()?;
7472 match self.peek() {
7473 Token::BindMatch => {
7474 let line = left.line;
7475 self.advance();
7476 match self.peek().clone() {
7477 Token::Regex(pattern, flags, delim) => {
7478 self.advance();
7479 Ok(Expr {
7480 kind: ExprKind::Match {
7481 expr: Box::new(left),
7482 pattern,
7483 flags,
7484 scalar_g: false,
7485 delim,
7486 },
7487 line,
7488 })
7489 }
7490 Token::Ident(ref s) if s.starts_with('\x00') => {
7491 let (Token::Ident(encoded), _) = self.advance() else {
7492 unreachable!()
7493 };
7494 let parts: Vec<&str> = encoded.split('\x00').collect();
7495 if parts.len() >= 4 && parts[1] == "s" {
7496 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7497 Ok(Expr {
7498 kind: ExprKind::Substitution {
7499 expr: Box::new(left),
7500 pattern: parts[2].to_string(),
7501 replacement: parts[3].to_string(),
7502 flags: parts.get(4).unwrap_or(&"").to_string(),
7503 delim,
7504 },
7505 line,
7506 })
7507 } else if parts.len() >= 4 && parts[1] == "tr" {
7508 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7509 Ok(Expr {
7510 kind: ExprKind::Transliterate {
7511 expr: Box::new(left),
7512 from: parts[2].to_string(),
7513 to: parts[3].to_string(),
7514 flags: parts.get(4).unwrap_or(&"").to_string(),
7515 delim,
7516 },
7517 line,
7518 })
7519 } else {
7520 Err(self.syntax_err("Invalid regex binding", line))
7521 }
7522 }
7523 _ => {
7524 let rhs = self.parse_unary()?;
7525 Ok(Expr {
7526 kind: ExprKind::BinOp {
7527 left: Box::new(left),
7528 op: BinOp::BindMatch,
7529 right: Box::new(rhs),
7530 },
7531 line,
7532 })
7533 }
7534 }
7535 }
7536 Token::BindNotMatch => {
7537 let line = left.line;
7538 self.advance();
7539 match self.peek().clone() {
7540 Token::Regex(pattern, flags, delim) => {
7541 self.advance();
7542 Ok(Expr {
7543 kind: ExprKind::UnaryOp {
7544 op: UnaryOp::LogNot,
7545 expr: Box::new(Expr {
7546 kind: ExprKind::Match {
7547 expr: Box::new(left),
7548 pattern,
7549 flags,
7550 scalar_g: false,
7551 delim,
7552 },
7553 line,
7554 }),
7555 },
7556 line,
7557 })
7558 }
7559 Token::Ident(ref s) if s.starts_with('\x00') => {
7560 let (Token::Ident(encoded), _) = self.advance() else {
7561 unreachable!()
7562 };
7563 let parts: Vec<&str> = encoded.split('\x00').collect();
7564 if parts.len() >= 4 && parts[1] == "s" {
7565 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7566 Ok(Expr {
7567 kind: ExprKind::UnaryOp {
7568 op: UnaryOp::LogNot,
7569 expr: Box::new(Expr {
7570 kind: ExprKind::Substitution {
7571 expr: Box::new(left),
7572 pattern: parts[2].to_string(),
7573 replacement: parts[3].to_string(),
7574 flags: parts.get(4).unwrap_or(&"").to_string(),
7575 delim,
7576 },
7577 line,
7578 }),
7579 },
7580 line,
7581 })
7582 } else if parts.len() >= 4 && parts[1] == "tr" {
7583 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7584 Ok(Expr {
7585 kind: ExprKind::UnaryOp {
7586 op: UnaryOp::LogNot,
7587 expr: Box::new(Expr {
7588 kind: ExprKind::Transliterate {
7589 expr: Box::new(left),
7590 from: parts[2].to_string(),
7591 to: parts[3].to_string(),
7592 flags: parts.get(4).unwrap_or(&"").to_string(),
7593 delim,
7594 },
7595 line,
7596 }),
7597 },
7598 line,
7599 })
7600 } else {
7601 Err(self.syntax_err("Invalid regex binding after !~", line))
7602 }
7603 }
7604 _ => {
7605 let rhs = self.parse_unary()?;
7606 Ok(Expr {
7607 kind: ExprKind::BinOp {
7608 left: Box::new(left),
7609 op: BinOp::BindNotMatch,
7610 right: Box::new(rhs),
7611 },
7612 line,
7613 })
7614 }
7615 }
7616 }
7617 _ => Ok(left),
7618 }
7619 }
7620
7621 fn parse_thread_input(&mut self) -> PerlResult<Expr> {
7624 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
7625 let result = self.parse_range();
7626 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
7627 result
7628 }
7629
7630 fn parse_range(&mut self) -> PerlResult<Expr> {
7637 let left = self.parse_log_or()?;
7638 let line = left.line;
7639 let (exclusive, _colon_style) = if self.eat(&Token::RangeExclusive) {
7646 (true, false)
7647 } else if self.eat(&Token::Range) {
7648 (false, false)
7649 } else if self.suppress_colon_range == 0 && self.eat(&Token::Colon) {
7650 (false, true)
7653 } else if self.suppress_tilde_range == 0 && self.eat(&Token::BitNot) {
7654 (false, true)
7655 } else {
7656 return Ok(left);
7657 };
7658 let right = self.parse_log_or()?;
7659 let step = if self.eat(&Token::Colon)
7663 || (self.suppress_tilde_range == 0 && self.eat(&Token::BitNot))
7664 {
7665 Some(Box::new(self.parse_unary()?))
7666 } else {
7667 None
7668 };
7669 Ok(Expr {
7670 kind: ExprKind::Range {
7671 from: Box::new(left),
7672 to: Box::new(right),
7673 exclusive,
7674 step,
7675 },
7676 line,
7677 })
7678 }
7679
7680 fn parse_package_qualified_identifier(&mut self) -> PerlResult<String> {
7682 let mut name = match self.advance() {
7683 (Token::Ident(n), _) => n,
7684 (tok, l) => {
7685 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
7686 }
7687 };
7688 while self.eat(&Token::PackageSep) {
7689 match self.advance() {
7690 (Token::Ident(part), _) => {
7691 name.push_str("::");
7692 name.push_str(&part);
7693 }
7694 (Token::ScalarVar(part), _) if Self::is_underscore_topic_slot(&part) => {
7702 name.push_str("::");
7703 name.push_str(&part);
7704 }
7705 (tok, l) => {
7706 return Err(self
7707 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
7708 }
7709 }
7710 }
7711 Ok(name)
7712 }
7713
7714 fn parse_qualified_subroutine_name(&mut self) -> PerlResult<String> {
7716 self.parse_package_qualified_identifier()
7717 }
7718
7719 fn parse_unary(&mut self) -> PerlResult<Expr> {
7720 let line = self.peek_line();
7721 match self.peek().clone() {
7722 Token::Minus => {
7723 self.advance();
7724 let expr = self.parse_power()?;
7725 Ok(Expr {
7726 kind: ExprKind::UnaryOp {
7727 op: UnaryOp::Negate,
7728 expr: Box::new(expr),
7729 },
7730 line,
7731 })
7732 }
7733 Token::Plus => {
7740 self.advance();
7741 if matches!(self.peek(), Token::LBrace) {
7742 let line = self.peek_line();
7743 self.advance(); return self.parse_forced_hashref_body(line);
7745 }
7746 self.parse_unary()
7747 }
7748 Token::LogNot => {
7749 self.advance();
7750 let expr = self.parse_unary()?;
7751 Ok(Expr {
7752 kind: ExprKind::UnaryOp {
7753 op: UnaryOp::LogNot,
7754 expr: Box::new(expr),
7755 },
7756 line,
7757 })
7758 }
7759 Token::BitNot => {
7760 self.advance();
7761 let expr = self.parse_unary()?;
7762 Ok(Expr {
7763 kind: ExprKind::UnaryOp {
7764 op: UnaryOp::BitNot,
7765 expr: Box::new(expr),
7766 },
7767 line,
7768 })
7769 }
7770 Token::Increment => {
7771 self.advance();
7772 let expr = self.parse_postfix()?;
7773 Ok(Expr {
7774 kind: ExprKind::UnaryOp {
7775 op: UnaryOp::PreIncrement,
7776 expr: Box::new(expr),
7777 },
7778 line,
7779 })
7780 }
7781 Token::Decrement => {
7782 self.advance();
7783 let expr = self.parse_postfix()?;
7784 Ok(Expr {
7785 kind: ExprKind::UnaryOp {
7786 op: UnaryOp::PreDecrement,
7787 expr: Box::new(expr),
7788 },
7789 line,
7790 })
7791 }
7792 Token::BitAnd => {
7793 self.advance();
7796 if matches!(self.peek(), Token::LBrace) {
7797 self.advance();
7798 let inner = self.parse_expression()?;
7799 self.expect(&Token::RBrace)?;
7800 return Ok(Expr {
7801 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
7802 line,
7803 });
7804 }
7805 if matches!(self.peek(), Token::Ident(_)) {
7806 let name = self.parse_qualified_subroutine_name()?;
7807 return Ok(Expr {
7808 kind: ExprKind::SubroutineRef(name),
7809 line,
7810 });
7811 }
7812 let target = self.parse_primary()?;
7813 if matches!(self.peek(), Token::LParen) {
7814 self.advance();
7815 let args = self.parse_arg_list()?;
7816 self.expect(&Token::RParen)?;
7817 return Ok(Expr {
7818 kind: ExprKind::IndirectCall {
7819 target: Box::new(target),
7820 args,
7821 ampersand: true,
7822 pass_caller_arglist: false,
7823 },
7824 line,
7825 });
7826 }
7827 Ok(Expr {
7829 kind: ExprKind::IndirectCall {
7830 target: Box::new(target),
7831 args: vec![],
7832 ampersand: true,
7833 pass_caller_arglist: true,
7834 },
7835 line,
7836 })
7837 }
7838 Token::Backslash => {
7839 self.advance();
7840 let expr = self.parse_unary()?;
7841 if let ExprKind::SubroutineRef(name) = expr.kind {
7842 return Ok(Expr {
7843 kind: ExprKind::SubroutineCodeRef(name),
7844 line,
7845 });
7846 }
7847 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
7848 return Ok(expr);
7849 }
7850 Ok(Expr {
7852 kind: ExprKind::ScalarRef(Box::new(expr)),
7853 line,
7854 })
7855 }
7856 Token::FileTest(op) => {
7857 self.advance();
7858 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
7860 Expr {
7861 kind: ExprKind::ScalarVar("_".into()),
7862 line: self.peek_line(),
7863 }
7864 } else {
7865 self.parse_unary()?
7866 };
7867 Ok(Expr {
7868 kind: ExprKind::FileTest {
7869 op,
7870 expr: Box::new(expr),
7871 },
7872 line,
7873 })
7874 }
7875 _ => self.parse_power(),
7876 }
7877 }
7878
7879 fn parse_power(&mut self) -> PerlResult<Expr> {
7880 let left = self.parse_postfix()?;
7881 if matches!(self.peek(), Token::Power) {
7882 let line = left.line;
7883 self.advance();
7884 let right = self.parse_unary()?; return Ok(Expr {
7886 kind: ExprKind::BinOp {
7887 left: Box::new(left),
7888 op: BinOp::Pow,
7889 right: Box::new(right),
7890 },
7891 line,
7892 });
7893 }
7894 Ok(left)
7895 }
7896
7897 fn parse_postfix(&mut self) -> PerlResult<Expr> {
7898 let mut expr = self.parse_primary()?;
7899 loop {
7900 match self.peek().clone() {
7901 Token::Increment => {
7902 if self.peek_line() > self.prev_line() {
7905 break;
7906 }
7907 let line = expr.line;
7908 self.advance();
7909 expr = Expr {
7910 kind: ExprKind::PostfixOp {
7911 expr: Box::new(expr),
7912 op: PostfixOp::Increment,
7913 },
7914 line,
7915 };
7916 }
7917 Token::Decrement => {
7918 if self.peek_line() > self.prev_line() {
7921 break;
7922 }
7923 let line = expr.line;
7924 self.advance();
7925 expr = Expr {
7926 kind: ExprKind::PostfixOp {
7927 expr: Box::new(expr),
7928 op: PostfixOp::Decrement,
7929 },
7930 line,
7931 };
7932 }
7933 Token::LParen => {
7934 if self.suppress_indirect_paren_call > 0 {
7935 break;
7936 }
7937 if self.peek_line() > self.prev_line() {
7941 break;
7942 }
7943 let line = expr.line;
7944 self.advance();
7945 let args = self.parse_arg_list()?;
7946 self.expect(&Token::RParen)?;
7947 expr = Expr {
7948 kind: ExprKind::IndirectCall {
7949 target: Box::new(expr),
7950 args,
7951 ampersand: false,
7952 pass_caller_arglist: false,
7953 },
7954 line,
7955 };
7956 }
7957 Token::Arrow => {
7958 let line = expr.line;
7959 self.advance();
7960 match self.peek().clone() {
7961 Token::LBracket => {
7962 self.advance();
7963 let index = self.parse_expression()?;
7964 self.expect(&Token::RBracket)?;
7965 expr = Expr {
7966 kind: ExprKind::ArrowDeref {
7967 expr: Box::new(expr),
7968 index: Box::new(index),
7969 kind: DerefKind::Array,
7970 },
7971 line,
7972 };
7973 }
7974 Token::LBrace => {
7975 self.advance();
7976 let key = self.parse_hash_subscript_key()?;
7977 self.expect(&Token::RBrace)?;
7978 expr = Expr {
7979 kind: ExprKind::ArrowDeref {
7980 expr: Box::new(expr),
7981 index: Box::new(key),
7982 kind: DerefKind::Hash,
7983 },
7984 line,
7985 };
7986 }
7987 Token::LParen => {
7988 self.advance();
7989 let args = self.parse_arg_list()?;
7990 self.expect(&Token::RParen)?;
7991 expr = Expr {
7992 kind: ExprKind::ArrowDeref {
7993 expr: Box::new(expr),
7994 index: Box::new(Expr {
7995 kind: ExprKind::List(args),
7996 line,
7997 }),
7998 kind: DerefKind::Call,
7999 },
8000 line,
8001 };
8002 }
8003 Token::Ident(method) => {
8004 self.advance();
8005 if method == "SUPER" {
8006 self.expect(&Token::PackageSep)?;
8007 let real_method = match self.advance() {
8008 (Token::Ident(n), _) => n,
8009 (tok, l) => {
8010 return Err(self.syntax_err(
8011 format!(
8012 "Expected method name after SUPER::, got {:?}",
8013 tok
8014 ),
8015 l,
8016 ));
8017 }
8018 };
8019 let args = if self.eat(&Token::LParen) {
8020 let a = self.parse_arg_list()?;
8021 self.expect(&Token::RParen)?;
8022 a
8023 } else {
8024 self.parse_method_arg_list_no_paren()?
8025 };
8026 expr = Expr {
8027 kind: ExprKind::MethodCall {
8028 object: Box::new(expr),
8029 method: real_method,
8030 args,
8031 super_call: true,
8032 },
8033 line,
8034 };
8035 } else {
8036 let mut method_name = method;
8037 while self.eat(&Token::PackageSep) {
8038 match self.advance() {
8039 (Token::Ident(part), _) => {
8040 method_name.push_str("::");
8041 method_name.push_str(&part);
8042 }
8043 (tok, l) => {
8044 return Err(self.syntax_err(
8045 format!(
8046 "Expected identifier after :: in method name, got {:?}",
8047 tok
8048 ),
8049 l,
8050 ));
8051 }
8052 }
8053 }
8054 let args = if self.eat(&Token::LParen) {
8055 let a = self.parse_arg_list()?;
8056 self.expect(&Token::RParen)?;
8057 a
8058 } else {
8059 self.parse_method_arg_list_no_paren()?
8060 };
8061 expr = Expr {
8062 kind: ExprKind::MethodCall {
8063 object: Box::new(expr),
8064 method: method_name,
8065 args,
8066 super_call: false,
8067 },
8068 line,
8069 };
8070 }
8071 }
8072 Token::ArrayAt => {
8078 self.advance(); match self.peek().clone() {
8080 Token::Star => {
8081 self.advance();
8082 expr = Expr {
8083 kind: ExprKind::Deref {
8084 expr: Box::new(expr),
8085 kind: Sigil::Array,
8086 },
8087 line,
8088 };
8089 }
8090 Token::LBracket => {
8091 self.advance();
8092 let indices = self.parse_slice_arg_list(false)?;
8093 self.expect(&Token::RBracket)?;
8094 let source = Expr {
8095 kind: ExprKind::Deref {
8096 expr: Box::new(expr),
8097 kind: Sigil::Array,
8098 },
8099 line,
8100 };
8101 expr = Expr {
8102 kind: ExprKind::AnonymousListSlice {
8103 source: Box::new(source),
8104 indices,
8105 },
8106 line,
8107 };
8108 }
8109 Token::LBrace => {
8110 self.advance();
8111 let keys = self.parse_slice_arg_list(true)?;
8112 self.expect(&Token::RBrace)?;
8113 expr = Expr {
8114 kind: ExprKind::HashSliceDeref {
8115 container: Box::new(expr),
8116 keys,
8117 },
8118 line,
8119 };
8120 }
8121 tok => {
8122 return Err(self.syntax_err(
8123 format!(
8124 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
8125 tok
8126 ),
8127 line,
8128 ));
8129 }
8130 }
8131 }
8132 Token::HashPercent => {
8133 self.advance(); match self.peek().clone() {
8135 Token::Star => {
8136 self.advance();
8137 expr = Expr {
8138 kind: ExprKind::Deref {
8139 expr: Box::new(expr),
8140 kind: Sigil::Hash,
8141 },
8142 line,
8143 };
8144 }
8145 tok => {
8146 return Err(self.syntax_err(
8147 format!("Expected `*` after `->%`, got {:?}", tok),
8148 line,
8149 ));
8150 }
8151 }
8152 }
8153 Token::X => {
8155 self.advance();
8156 let args = if self.eat(&Token::LParen) {
8157 let a = self.parse_arg_list()?;
8158 self.expect(&Token::RParen)?;
8159 a
8160 } else {
8161 self.parse_method_arg_list_no_paren()?
8162 };
8163 expr = Expr {
8164 kind: ExprKind::MethodCall {
8165 object: Box::new(expr),
8166 method: "x".to_string(),
8167 args,
8168 super_call: false,
8169 },
8170 line,
8171 };
8172 }
8173 _ => break,
8174 }
8175 }
8176 Token::LBracket => {
8177 if self.peek_line() > self.prev_line() {
8180 break;
8181 }
8182 let line = expr.line;
8184 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
8185 if let ExprKind::ScalarVar(ref name) = expr.kind {
8186 let name = name.clone();
8187 self.advance();
8188 let index = self.parse_expression()?;
8189 self.expect(&Token::RBracket)?;
8190 expr = Expr {
8191 kind: ExprKind::ArrayElement {
8192 array: name,
8193 index: Box::new(index),
8194 },
8195 line,
8196 };
8197 }
8198 } else if postfix_lbracket_is_arrow_container(&expr) {
8199 self.advance();
8200 let indices = self.parse_arg_list()?;
8201 self.expect(&Token::RBracket)?;
8202 expr = Expr {
8203 kind: ExprKind::ArrowDeref {
8204 expr: Box::new(expr),
8205 index: Box::new(Expr {
8206 kind: ExprKind::List(indices),
8207 line,
8208 }),
8209 kind: DerefKind::Array,
8210 },
8211 line,
8212 };
8213 } else {
8214 self.advance();
8215 let indices = self.parse_arg_list()?;
8216 self.expect(&Token::RBracket)?;
8217 expr = Expr {
8218 kind: ExprKind::AnonymousListSlice {
8219 source: Box::new(expr),
8220 indices,
8221 },
8222 line,
8223 };
8224 }
8225 }
8226 Token::LBrace => {
8227 if self.suppress_scalar_hash_brace > 0 {
8228 break;
8229 }
8230 if self.peek_line() > self.prev_line() {
8233 break;
8234 }
8235 let line = expr.line;
8238 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
8239 let is_chainable_hash_subscript = is_scalar_named_hash
8240 || matches!(
8241 expr.kind,
8242 ExprKind::HashElement { .. }
8243 | ExprKind::ArrayElement { .. }
8244 | ExprKind::ArrowDeref { .. }
8245 | ExprKind::Deref {
8246 kind: Sigil::Scalar,
8247 ..
8248 }
8249 );
8250 if !is_chainable_hash_subscript {
8251 break;
8252 }
8253 self.advance();
8254 let key = self.parse_hash_subscript_key()?;
8255 self.expect(&Token::RBrace)?;
8256 expr = if is_scalar_named_hash {
8257 if let ExprKind::ScalarVar(ref name) = expr.kind {
8258 let name = name.clone();
8259 if name == "_" {
8261 Expr {
8262 kind: ExprKind::ArrowDeref {
8263 expr: Box::new(Expr {
8264 kind: ExprKind::ScalarVar("_".into()),
8265 line,
8266 }),
8267 index: Box::new(key),
8268 kind: DerefKind::Hash,
8269 },
8270 line,
8271 }
8272 } else {
8273 Expr {
8274 kind: ExprKind::HashElement {
8275 hash: name,
8276 key: Box::new(key),
8277 },
8278 line,
8279 }
8280 }
8281 } else {
8282 unreachable!("is_scalar_named_hash implies ScalarVar");
8283 }
8284 } else {
8285 Expr {
8286 kind: ExprKind::ArrowDeref {
8287 expr: Box::new(expr),
8288 index: Box::new(key),
8289 kind: DerefKind::Hash,
8290 },
8291 line,
8292 }
8293 };
8294 }
8295 Token::LogNot | Token::BitNot => {
8296 if !matches!(expr.kind, ExprKind::ScalarVar(_)) {
8311 break;
8312 }
8313 if self.peek_line() > self.prev_line() {
8314 break;
8315 }
8316 let opener = self.peek().clone();
8317 let line = expr.line;
8318 let name = if let ExprKind::ScalarVar(ref n) = expr.kind {
8319 n.clone()
8320 } else {
8321 unreachable!()
8322 };
8323 self.advance(); self.suppress_tilde_range = self.suppress_tilde_range.saturating_add(1);
8329 let index_result = self.parse_expression();
8330 self.suppress_tilde_range = self.suppress_tilde_range.saturating_sub(1);
8331 let index = index_result?;
8332 let close_match = matches!(
8333 (&opener, self.peek()),
8334 (Token::LogNot, Token::LogNot) | (Token::BitNot, Token::BitNot)
8335 );
8336 if !close_match {
8337 let want = if matches!(opener, Token::LogNot) {
8338 "!"
8339 } else {
8340 "~"
8341 };
8342 return Err(self.syntax_err(
8343 format!("expected closing `{}` for string subscript", want),
8344 self.peek_line(),
8345 ));
8346 }
8347 self.advance(); expr = Expr {
8349 kind: ExprKind::ArrayElement {
8350 array: format!("__topicstr__{}", name),
8351 index: Box::new(index),
8352 },
8353 line,
8354 };
8355 }
8356 _ => break,
8357 }
8358 }
8359 Ok(expr)
8360 }
8361
8362 fn parse_primary(&mut self) -> PerlResult<Expr> {
8363 let line = self.peek_line();
8364 if let Token::Ident(ref kw) = self.peek().clone() {
8369 if matches!(kw.as_str(), "my" | "our" | "state" | "local") {
8370 let kw_owned = kw.clone();
8371 let saved_pos = self.pos;
8376 let stmt = self.parse_my_our_local(&kw_owned, false)?;
8377 let decls = match stmt.kind {
8378 StmtKind::My(d)
8379 | StmtKind::Our(d)
8380 | StmtKind::State(d)
8381 | StmtKind::Local(d) => d,
8382 _ => {
8383 self.pos = saved_pos;
8388 return Err(self.syntax_err(
8389 "`my`/`our`/`local` in expression must declare variables",
8390 line,
8391 ));
8392 }
8393 };
8394 return Ok(Expr {
8395 kind: ExprKind::MyExpr {
8396 keyword: kw_owned,
8397 decls,
8398 },
8399 line,
8400 });
8401 }
8402 }
8403 match self.peek().clone() {
8404 Token::Integer(n) => {
8405 self.advance();
8406 Ok(Expr {
8407 kind: ExprKind::Integer(n),
8408 line,
8409 })
8410 }
8411 Token::Float(f) => {
8412 self.advance();
8413 Ok(Expr {
8414 kind: ExprKind::Float(f),
8415 line,
8416 })
8417 }
8418 Token::ArrowBrace => {
8424 self.advance();
8425 let mut stmts = Vec::new();
8426 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
8427 if self.eat(&Token::Semicolon) {
8428 continue;
8429 }
8430 stmts.push(self.parse_statement()?);
8431 }
8432 self.expect(&Token::RBrace)?;
8433 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
8434 let inner = Expr {
8435 kind: ExprKind::CodeRef {
8436 params: vec![],
8437 body: stmts,
8438 },
8439 line: inner_line,
8440 };
8441 Ok(Expr {
8442 kind: ExprKind::Do(Box::new(inner)),
8443 line,
8444 })
8445 }
8446 Token::Star => {
8447 self.advance();
8448 if matches!(self.peek(), Token::LBrace) {
8449 self.advance();
8450 let inner = self.parse_expression()?;
8451 self.expect(&Token::RBrace)?;
8452 return Ok(Expr {
8453 kind: ExprKind::Deref {
8454 expr: Box::new(inner),
8455 kind: Sigil::Typeglob,
8456 },
8457 line,
8458 });
8459 }
8460 if matches!(
8462 self.peek(),
8463 Token::ScalarVar(_)
8464 | Token::ArrayVar(_)
8465 | Token::HashVar(_)
8466 | Token::DerefScalarVar(_)
8467 | Token::HashPercent
8468 ) {
8469 let inner = self.parse_postfix()?;
8470 return Ok(Expr {
8471 kind: ExprKind::TypeglobExpr(Box::new(inner)),
8472 line,
8473 });
8474 }
8475 let mut full_name = match self.advance() {
8477 (Token::Ident(n), _) => n,
8478 (Token::X, _) => "x".to_string(),
8479 (tok, l) => {
8480 return Err(self
8481 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
8482 }
8483 };
8484 while self.eat(&Token::PackageSep) {
8485 match self.advance() {
8486 (Token::Ident(part), _) => {
8487 full_name = format!("{}::{}", full_name, part);
8488 }
8489 (Token::X, _) => {
8490 full_name = format!("{}::x", full_name);
8491 }
8492 (tok, l) => {
8493 return Err(self.syntax_err(
8494 format!("Expected identifier after :: in typeglob, got {:?}", tok),
8495 l,
8496 ));
8497 }
8498 }
8499 }
8500 Ok(Expr {
8501 kind: ExprKind::Typeglob(full_name),
8502 line,
8503 })
8504 }
8505 Token::SingleString(s) => {
8506 self.advance();
8507 Ok(Expr {
8508 kind: ExprKind::String(s),
8509 line,
8510 })
8511 }
8512 Token::DoubleString(s) => {
8513 self.advance();
8514 self.parse_interpolated_string(&s, line)
8515 }
8516 Token::BacktickString(s) => {
8517 self.advance();
8518 let inner = self.parse_interpolated_string(&s, line)?;
8519 Ok(Expr {
8520 kind: ExprKind::Qx(Box::new(inner)),
8521 line,
8522 })
8523 }
8524 Token::HereDoc(_, body, interpolate) => {
8525 self.advance();
8526 if interpolate {
8527 self.parse_interpolated_string(&body, line)
8528 } else {
8529 Ok(Expr {
8530 kind: ExprKind::String(body),
8531 line,
8532 })
8533 }
8534 }
8535 Token::Regex(pattern, flags, _delim) => {
8536 self.advance();
8537 Ok(Expr {
8538 kind: ExprKind::Regex(pattern, flags),
8539 line,
8540 })
8541 }
8542 Token::QW(words) => {
8543 self.advance();
8544 self.list_construct_close_pos = Some(self.pos);
8547 Ok(Expr {
8548 kind: ExprKind::QW(words),
8549 line,
8550 })
8551 }
8552 Token::DerefScalarVar(name) => {
8553 self.advance();
8554 Ok(Expr {
8555 kind: ExprKind::Deref {
8556 expr: Box::new(Expr {
8557 kind: ExprKind::ScalarVar(name),
8558 line,
8559 }),
8560 kind: Sigil::Scalar,
8561 },
8562 line,
8563 })
8564 }
8565 Token::ScalarVar(name) => {
8566 self.advance();
8567 Ok(Expr {
8568 kind: ExprKind::ScalarVar(name),
8569 line,
8570 })
8571 }
8572 Token::ArrayVar(name) => {
8573 self.advance();
8574 match self.peek() {
8576 Token::LBracket => {
8577 self.advance();
8578 let indices = self.parse_slice_arg_list(false)?;
8579 self.expect(&Token::RBracket)?;
8580 Ok(Expr {
8581 kind: ExprKind::ArraySlice {
8582 array: name,
8583 indices,
8584 },
8585 line,
8586 })
8587 }
8588 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
8589 self.advance();
8590 let keys = self.parse_slice_arg_list(true)?;
8591 self.expect(&Token::RBrace)?;
8592 Ok(Expr {
8593 kind: ExprKind::HashSlice { hash: name, keys },
8594 line,
8595 })
8596 }
8597 _ => Ok(Expr {
8598 kind: ExprKind::ArrayVar(name),
8599 line,
8600 }),
8601 }
8602 }
8603 Token::HashVar(name) => {
8604 self.advance();
8605 if matches!(self.peek(), Token::LBrace) && self.suppress_scalar_hash_brace == 0 {
8610 self.advance(); let keys = self.parse_slice_arg_list(true)?;
8612 self.expect(&Token::RBrace)?;
8613 return Ok(Expr {
8614 kind: ExprKind::HashKvSlice { hash: name, keys },
8615 line,
8616 });
8617 }
8618 Ok(Expr {
8619 kind: ExprKind::HashVar(name),
8620 line,
8621 })
8622 }
8623 Token::HashPercent => {
8624 self.advance();
8626 if matches!(self.peek(), Token::ScalarVar(_)) {
8627 let n = match self.advance() {
8628 (Token::ScalarVar(n), _) => n,
8629 (tok, l) => {
8630 return Err(self.syntax_err(
8631 format!("Expected scalar variable after %%, got {:?}", tok),
8632 l,
8633 ));
8634 }
8635 };
8636 return Ok(Expr {
8637 kind: ExprKind::Deref {
8638 expr: Box::new(Expr {
8639 kind: ExprKind::ScalarVar(n),
8640 line,
8641 }),
8642 kind: Sigil::Hash,
8643 },
8644 line,
8645 });
8646 }
8647 if matches!(self.peek(), Token::LBracket) {
8652 self.advance();
8653 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
8654 self.expect(&Token::RBracket)?;
8655 let href = Expr {
8656 kind: ExprKind::HashRef(pairs),
8657 line,
8658 };
8659 return Ok(Expr {
8660 kind: ExprKind::Deref {
8661 expr: Box::new(href),
8662 kind: Sigil::Hash,
8663 },
8664 line,
8665 });
8666 }
8667 self.expect(&Token::LBrace)?;
8668 let looks_like_pair = matches!(
8674 self.peek(),
8675 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
8676 ) && matches!(self.peek_at(1), Token::FatArrow);
8677 let inner = if looks_like_pair {
8678 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
8679 Expr {
8680 kind: ExprKind::HashRef(pairs),
8681 line,
8682 }
8683 } else {
8684 self.parse_expression()?
8685 };
8686 self.expect(&Token::RBrace)?;
8687 Ok(Expr {
8688 kind: ExprKind::Deref {
8689 expr: Box::new(inner),
8690 kind: Sigil::Hash,
8691 },
8692 line,
8693 })
8694 }
8695 Token::ArrayAt => {
8696 self.advance();
8697 if matches!(self.peek(), Token::LBrace) {
8699 self.advance();
8700 let inner = self.parse_expression()?;
8701 self.expect(&Token::RBrace)?;
8702 return Ok(Expr {
8703 kind: ExprKind::Deref {
8704 expr: Box::new(inner),
8705 kind: Sigil::Array,
8706 },
8707 line,
8708 });
8709 }
8710 if matches!(self.peek(), Token::LBracket) {
8714 self.advance();
8715 let mut elems = Vec::new();
8716 if !matches!(self.peek(), Token::RBracket) {
8717 elems.push(self.parse_assign_expr()?);
8718 while self.eat(&Token::Comma) {
8719 if matches!(self.peek(), Token::RBracket) {
8720 break;
8721 }
8722 elems.push(self.parse_assign_expr()?);
8723 }
8724 }
8725 self.expect(&Token::RBracket)?;
8726 let aref = Expr {
8727 kind: ExprKind::ArrayRef(elems),
8728 line,
8729 };
8730 return Ok(Expr {
8731 kind: ExprKind::Deref {
8732 expr: Box::new(aref),
8733 kind: Sigil::Array,
8734 },
8735 line,
8736 });
8737 }
8738 let container = match self.peek().clone() {
8740 Token::ScalarVar(n) => {
8741 self.advance();
8742 Expr {
8743 kind: ExprKind::ScalarVar(n),
8744 line,
8745 }
8746 }
8747 _ => {
8748 return Err(self.syntax_err(
8749 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
8750 line,
8751 ));
8752 }
8753 };
8754 if matches!(self.peek(), Token::LBrace) {
8755 self.advance();
8756 let keys = self.parse_slice_arg_list(true)?;
8757 self.expect(&Token::RBrace)?;
8758 return Ok(Expr {
8759 kind: ExprKind::HashSliceDeref {
8760 container: Box::new(container),
8761 keys,
8762 },
8763 line,
8764 });
8765 }
8766 Ok(Expr {
8767 kind: ExprKind::Deref {
8768 expr: Box::new(container),
8769 kind: Sigil::Array,
8770 },
8771 line,
8772 })
8773 }
8774 Token::LParen => {
8775 self.advance();
8776 if matches!(self.peek(), Token::RParen) {
8777 self.advance();
8778 self.list_construct_close_pos = Some(self.pos);
8781 return Ok(Expr {
8782 kind: ExprKind::List(vec![]),
8783 line,
8784 });
8785 }
8786 let saved_no_pipe = self.no_pipe_forward_depth;
8789 self.no_pipe_forward_depth = 0;
8790 let expr = self.parse_expression();
8791 self.no_pipe_forward_depth = saved_no_pipe;
8792 let expr = expr?;
8793 self.expect(&Token::RParen)?;
8794 self.list_construct_close_pos = Some(self.pos);
8799 Ok(expr)
8800 }
8801 Token::LBracket => {
8802 self.advance();
8803 let elems = self.parse_arg_list()?;
8804 self.expect(&Token::RBracket)?;
8805 Ok(Expr {
8806 kind: ExprKind::ArrayRef(elems),
8807 line,
8808 })
8809 }
8810 Token::LBrace => {
8811 self.advance();
8813 let saved = self.pos;
8815 match self.try_parse_hash_ref() {
8816 Ok(pairs) => Ok(Expr {
8817 kind: ExprKind::HashRef(pairs),
8818 line,
8819 }),
8820 Err(_) => {
8821 self.pos = saved;
8822 let mut stmts = Vec::new();
8824 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
8825 if self.eat(&Token::Semicolon) {
8826 continue;
8827 }
8828 stmts.push(self.parse_statement()?);
8829 }
8830 self.expect(&Token::RBrace)?;
8831 Ok(Expr {
8832 kind: ExprKind::CodeRef {
8833 params: vec![],
8834 body: stmts,
8835 },
8836 line,
8837 })
8838 }
8839 }
8840 }
8841 Token::Diamond => {
8842 self.advance();
8843 Ok(Expr {
8844 kind: ExprKind::ReadLine(None),
8845 line,
8846 })
8847 }
8848 Token::ReadLine(handle) => {
8849 self.advance();
8850 Ok(Expr {
8851 kind: ExprKind::ReadLine(Some(handle)),
8852 line,
8853 })
8854 }
8855
8856 Token::ThreadArrow => {
8858 self.advance();
8859 self.parse_thread_macro(line, false)
8860 }
8861 Token::ThreadArrowLast => {
8862 self.advance();
8863 self.parse_thread_macro(line, true)
8864 }
8865 Token::Ident(ref name) => {
8866 let name = name.clone();
8867 if name.starts_with('\x00') {
8869 self.advance();
8870 let parts: Vec<&str> = name.split('\x00').collect();
8871 if parts.len() >= 4 && parts[1] == "s" {
8872 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8873 return Ok(Expr {
8874 kind: ExprKind::Substitution {
8875 expr: Box::new(Expr {
8876 kind: ExprKind::ScalarVar("_".into()),
8877 line,
8878 }),
8879 pattern: parts[2].to_string(),
8880 replacement: parts[3].to_string(),
8881 flags: parts.get(4).unwrap_or(&"").to_string(),
8882 delim,
8883 },
8884 line,
8885 });
8886 }
8887 if parts.len() >= 4 && parts[1] == "tr" {
8888 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8889 return Ok(Expr {
8890 kind: ExprKind::Transliterate {
8891 expr: Box::new(Expr {
8892 kind: ExprKind::ScalarVar("_".into()),
8893 line,
8894 }),
8895 from: parts[2].to_string(),
8896 to: parts[3].to_string(),
8897 flags: parts.get(4).unwrap_or(&"").to_string(),
8898 delim,
8899 },
8900 line,
8901 });
8902 }
8903 return Err(self.syntax_err("Unexpected encoded token", line));
8904 }
8905 self.parse_named_expr(name)
8906 }
8907
8908 Token::Percent => {
8911 self.advance();
8912 match self.peek().clone() {
8913 Token::Ident(name) => {
8914 self.advance();
8915 Ok(Expr {
8916 kind: ExprKind::HashVar(name),
8917 line,
8918 })
8919 }
8920 Token::ScalarVar(n) => {
8921 self.advance();
8922 Ok(Expr {
8923 kind: ExprKind::Deref {
8924 expr: Box::new(Expr {
8925 kind: ExprKind::ScalarVar(n),
8926 line,
8927 }),
8928 kind: Sigil::Hash,
8929 },
8930 line,
8931 })
8932 }
8933 Token::LBrace => {
8934 self.advance();
8935 let looks_like_pair = matches!(
8936 self.peek(),
8937 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
8938 ) && matches!(self.peek_at(1), Token::FatArrow);
8939 let inner = if looks_like_pair {
8940 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
8941 Expr {
8942 kind: ExprKind::HashRef(pairs),
8943 line,
8944 }
8945 } else {
8946 self.parse_expression()?
8947 };
8948 self.expect(&Token::RBrace)?;
8949 Ok(Expr {
8950 kind: ExprKind::Deref {
8951 expr: Box::new(inner),
8952 kind: Sigil::Hash,
8953 },
8954 line,
8955 })
8956 }
8957 Token::LBracket => {
8958 self.advance();
8959 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
8960 self.expect(&Token::RBracket)?;
8961 let href = Expr {
8962 kind: ExprKind::HashRef(pairs),
8963 line,
8964 };
8965 Ok(Expr {
8966 kind: ExprKind::Deref {
8967 expr: Box::new(href),
8968 kind: Sigil::Hash,
8969 },
8970 line,
8971 })
8972 }
8973 tok => Err(self.syntax_err(
8974 format!(
8975 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
8976 tok
8977 ),
8978 line,
8979 )),
8980 }
8981 }
8982
8983 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
8984 }
8985 }
8986
8987 fn parse_named_expr(&mut self, mut name: String) -> PerlResult<Expr> {
8988 let line = self.peek_line();
8989 self.advance(); while self.eat(&Token::PackageSep) {
8991 match self.advance() {
8992 (Token::Ident(part), _) => {
8993 name = format!("{}::{}", name, part);
8994 }
8995 (tok, err_line) => {
8996 return Err(self.syntax_err(
8997 format!("Expected identifier after `::`, got {:?}", tok),
8998 err_line,
8999 ));
9000 }
9001 }
9002 }
9003
9004 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name) {
9011 return Ok(Expr {
9012 kind: ExprKind::String(name),
9013 line,
9014 });
9015 }
9016
9017 if crate::compat_mode() {
9018 if let Some(ext) = Self::stryke_extension_name(&name) {
9019 if !self.declared_subs.contains(&name) {
9020 return Err(self.syntax_err(
9021 format!("`{ext}` is a stryke extension (disabled by --compat)"),
9022 line,
9023 ));
9024 }
9025 }
9026 }
9027
9028 if let Some(rest) = name.strip_prefix("CORE::") {
9035 name = rest.to_string();
9036 }
9037
9038 match name.as_str() {
9039 "__FILE__" => Ok(Expr {
9040 kind: ExprKind::MagicConst(MagicConstKind::File),
9041 line,
9042 }),
9043 "__LINE__" => Ok(Expr {
9044 kind: ExprKind::MagicConst(MagicConstKind::Line),
9045 line,
9046 }),
9047 "__SUB__" => Ok(Expr {
9048 kind: ExprKind::MagicConst(MagicConstKind::Sub),
9049 line,
9050 }),
9051 "stdin" => Ok(Expr {
9052 kind: ExprKind::FuncCall {
9053 name: "stdin".into(),
9054 args: vec![],
9055 },
9056 line,
9057 }),
9058 "range" => {
9059 let args = self.parse_builtin_args()?;
9060 Ok(Expr {
9061 kind: ExprKind::FuncCall {
9062 name: "range".into(),
9063 args,
9064 },
9065 line,
9066 })
9067 }
9068 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
9069 "say" => {
9070 if crate::no_interop_mode() {
9071 return Err(
9072 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
9073 );
9074 }
9075 self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a })
9076 }
9077 "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
9078 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
9079 "die" => {
9080 let args = self.parse_list_until_terminator()?;
9081 Ok(Expr {
9082 kind: ExprKind::Die(args),
9083 line,
9084 })
9085 }
9086 "warn" => {
9087 let args = self.parse_list_until_terminator()?;
9088 Ok(Expr {
9089 kind: ExprKind::Warn(args),
9090 line,
9091 })
9092 }
9093 "croak" | "confess" => {
9098 let args = self.parse_list_until_terminator()?;
9099 Ok(Expr {
9100 kind: ExprKind::Die(args),
9101 line,
9102 })
9103 }
9104 "carp" | "cluck" => {
9106 let args = self.parse_list_until_terminator()?;
9107 Ok(Expr {
9108 kind: ExprKind::Warn(args),
9109 line,
9110 })
9111 }
9112 "chomp" => {
9113 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9114 return Ok(e);
9115 }
9116 let a = self.parse_one_arg_or_default()?;
9117 Ok(Expr {
9118 kind: ExprKind::Chomp(Box::new(a)),
9119 line,
9120 })
9121 }
9122 "chop" => {
9123 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9124 return Ok(e);
9125 }
9126 let a = self.parse_one_arg_or_default()?;
9127 Ok(Expr {
9128 kind: ExprKind::Chop(Box::new(a)),
9129 line,
9130 })
9131 }
9132 "length" => {
9133 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9134 return Ok(e);
9135 }
9136 let a = self.parse_one_arg_or_default()?;
9137 Ok(Expr {
9138 kind: ExprKind::Length(Box::new(a)),
9139 line,
9140 })
9141 }
9142 "defined" => {
9143 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9144 return Ok(e);
9145 }
9146 let a = if matches!(
9154 self.peek(),
9155 Token::Semicolon
9156 | Token::RBrace
9157 | Token::RParen
9158 | Token::RBracket
9159 | Token::Eof
9160 | Token::Comma
9161 | Token::FatArrow
9162 | Token::PipeForward
9163 | Token::Question
9164 | Token::Colon
9165 | Token::NumEq
9166 | Token::NumNe
9167 | Token::NumLt
9168 | Token::NumGt
9169 | Token::NumLe
9170 | Token::NumGe
9171 | Token::Spaceship
9172 | Token::StrEq
9173 | Token::StrNe
9174 | Token::StrLt
9175 | Token::StrGt
9176 | Token::StrLe
9177 | Token::StrGe
9178 | Token::StrCmp
9179 | Token::LogAnd
9180 | Token::LogOr
9181 | Token::LogNot
9182 | Token::LogAndWord
9183 | Token::LogOrWord
9184 | Token::LogNotWord
9185 | Token::DefinedOr
9186 | Token::Range
9187 | Token::RangeExclusive
9188 | Token::Assign
9189 | Token::PlusAssign
9190 | Token::MinusAssign
9191 | Token::MulAssign
9192 | Token::DivAssign
9193 | Token::ModAssign
9194 | Token::PowAssign
9195 | Token::DotAssign
9196 | Token::AndAssign
9197 | Token::OrAssign
9198 | Token::XorAssign
9199 | Token::DefinedOrAssign
9200 | Token::ShiftLeftAssign
9201 | Token::ShiftRightAssign
9202 | Token::BitAndAssign
9203 | Token::BitOrAssign
9204 ) {
9205 Expr {
9206 kind: ExprKind::ScalarVar("_".into()),
9207 line: self.peek_line(),
9208 }
9209 } else if matches!(self.peek(), Token::LParen)
9210 && matches!(self.peek_at(1), Token::RParen)
9211 {
9212 let pl = self.peek_line();
9213 self.advance();
9214 self.advance();
9215 Expr {
9216 kind: ExprKind::ScalarVar("_".into()),
9217 line: pl,
9218 }
9219 } else {
9220 self.parse_named_unary_arg()?
9221 };
9222 Ok(Expr {
9223 kind: ExprKind::Defined(Box::new(a)),
9224 line,
9225 })
9226 }
9227 "ref" => {
9228 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9229 return Ok(e);
9230 }
9231 let a = self.parse_one_arg_or_default()?;
9232 Ok(Expr {
9233 kind: ExprKind::Ref(Box::new(a)),
9234 line,
9235 })
9236 }
9237 "undef" => {
9238 if self.peek_line() == self.prev_line()
9241 && matches!(
9242 self.peek(),
9243 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
9244 )
9245 {
9246 let target = self.parse_primary()?;
9247 return Ok(Expr {
9248 kind: ExprKind::Assign {
9249 target: Box::new(target),
9250 value: Box::new(Expr {
9251 kind: ExprKind::Undef,
9252 line,
9253 }),
9254 },
9255 line,
9256 });
9257 }
9258 Ok(Expr {
9259 kind: ExprKind::Undef,
9260 line,
9261 })
9262 }
9263 "scalar" => {
9264 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9265 return Ok(e);
9266 }
9267 if crate::no_interop_mode() {
9268 return Err(self.syntax_err(
9269 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
9270 line,
9271 ));
9272 }
9273 let a = self.parse_one_arg_or_default()?;
9274 Ok(Expr {
9275 kind: ExprKind::ScalarContext(Box::new(a)),
9276 line,
9277 })
9278 }
9279 "abs" => {
9280 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9281 return Ok(e);
9282 }
9283 let a = self.parse_one_arg_or_default()?;
9284 Ok(Expr {
9285 kind: ExprKind::Abs(Box::new(a)),
9286 line,
9287 })
9288 }
9289 "inc" | "dec" => {
9294 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9295 return Ok(e);
9296 }
9297 let a = self.parse_one_arg_or_default()?;
9298 Ok(Expr {
9299 kind: ExprKind::FuncCall {
9300 name,
9301 args: vec![a],
9302 },
9303 line,
9304 })
9305 }
9306 "int" => {
9307 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9308 return Ok(e);
9309 }
9310 let a = self.parse_one_arg_or_default()?;
9311 Ok(Expr {
9312 kind: ExprKind::Int(Box::new(a)),
9313 line,
9314 })
9315 }
9316 "sqrt" => {
9317 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9318 return Ok(e);
9319 }
9320 let a = self.parse_one_arg_or_default()?;
9321 Ok(Expr {
9322 kind: ExprKind::Sqrt(Box::new(a)),
9323 line,
9324 })
9325 }
9326 "sin" => {
9327 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9328 return Ok(e);
9329 }
9330 let a = self.parse_one_arg_or_default()?;
9331 Ok(Expr {
9332 kind: ExprKind::Sin(Box::new(a)),
9333 line,
9334 })
9335 }
9336 "cos" => {
9337 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9338 return Ok(e);
9339 }
9340 let a = self.parse_one_arg_or_default()?;
9341 Ok(Expr {
9342 kind: ExprKind::Cos(Box::new(a)),
9343 line,
9344 })
9345 }
9346 "atan2" => {
9347 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9348 return Ok(e);
9349 }
9350 let args = self.parse_builtin_args()?;
9351 if args.len() != 2 {
9352 return Err(self.syntax_err("atan2 requires two arguments", line));
9353 }
9354 Ok(Expr {
9355 kind: ExprKind::Atan2 {
9356 y: Box::new(args[0].clone()),
9357 x: Box::new(args[1].clone()),
9358 },
9359 line,
9360 })
9361 }
9362 "exp" => {
9363 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9364 return Ok(e);
9365 }
9366 let a = self.parse_one_arg_or_default()?;
9367 Ok(Expr {
9368 kind: ExprKind::Exp(Box::new(a)),
9369 line,
9370 })
9371 }
9372 "log" => {
9373 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9374 return Ok(e);
9375 }
9376 let a = self.parse_one_arg_or_default()?;
9377 Ok(Expr {
9378 kind: ExprKind::Log(Box::new(a)),
9379 line,
9380 })
9381 }
9382 "input" => {
9383 let args = if matches!(
9384 self.peek(),
9385 Token::Semicolon
9386 | Token::RBrace
9387 | Token::RParen
9388 | Token::Eof
9389 | Token::Comma
9390 | Token::PipeForward
9391 ) {
9392 vec![]
9393 } else if matches!(self.peek(), Token::LParen) {
9394 self.advance();
9395 if matches!(self.peek(), Token::RParen) {
9396 self.advance();
9397 vec![]
9398 } else {
9399 let a = self.parse_expression()?;
9400 self.expect(&Token::RParen)?;
9401 vec![a]
9402 }
9403 } else {
9404 let a = self.parse_one_arg()?;
9405 vec![a]
9406 };
9407 Ok(Expr {
9408 kind: ExprKind::FuncCall {
9409 name: "input".to_string(),
9410 args,
9411 },
9412 line,
9413 })
9414 }
9415 "rand" => {
9416 if matches!(
9417 self.peek(),
9418 Token::Semicolon
9419 | Token::RBrace
9420 | Token::RParen
9421 | Token::Eof
9422 | Token::Comma
9423 | Token::PipeForward
9424 ) {
9425 Ok(Expr {
9426 kind: ExprKind::Rand(None),
9427 line,
9428 })
9429 } else if matches!(self.peek(), Token::LParen) {
9430 self.advance();
9431 if matches!(self.peek(), Token::RParen) {
9432 self.advance();
9433 Ok(Expr {
9434 kind: ExprKind::Rand(None),
9435 line,
9436 })
9437 } else {
9438 let a = self.parse_expression()?;
9439 self.expect(&Token::RParen)?;
9440 Ok(Expr {
9441 kind: ExprKind::Rand(Some(Box::new(a))),
9442 line,
9443 })
9444 }
9445 } else {
9446 let a = self.parse_one_arg()?;
9447 Ok(Expr {
9448 kind: ExprKind::Rand(Some(Box::new(a))),
9449 line,
9450 })
9451 }
9452 }
9453 "srand" => {
9454 if matches!(
9455 self.peek(),
9456 Token::Semicolon
9457 | Token::RBrace
9458 | Token::RParen
9459 | Token::Eof
9460 | Token::Comma
9461 | Token::PipeForward
9462 ) {
9463 Ok(Expr {
9464 kind: ExprKind::Srand(None),
9465 line,
9466 })
9467 } else if matches!(self.peek(), Token::LParen) {
9468 self.advance();
9469 if matches!(self.peek(), Token::RParen) {
9470 self.advance();
9471 Ok(Expr {
9472 kind: ExprKind::Srand(None),
9473 line,
9474 })
9475 } else {
9476 let a = self.parse_expression()?;
9477 self.expect(&Token::RParen)?;
9478 Ok(Expr {
9479 kind: ExprKind::Srand(Some(Box::new(a))),
9480 line,
9481 })
9482 }
9483 } else {
9484 let a = self.parse_one_arg()?;
9485 Ok(Expr {
9486 kind: ExprKind::Srand(Some(Box::new(a))),
9487 line,
9488 })
9489 }
9490 }
9491 "hex" => {
9492 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9493 return Ok(e);
9494 }
9495 let a = self.parse_one_arg_or_default()?;
9496 Ok(Expr {
9497 kind: ExprKind::Hex(Box::new(a)),
9498 line,
9499 })
9500 }
9501 "oct" => {
9502 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9503 return Ok(e);
9504 }
9505 let a = self.parse_one_arg_or_default()?;
9506 Ok(Expr {
9507 kind: ExprKind::Oct(Box::new(a)),
9508 line,
9509 })
9510 }
9511 "chr" => {
9512 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9513 return Ok(e);
9514 }
9515 let a = self.parse_one_arg_or_default()?;
9516 Ok(Expr {
9517 kind: ExprKind::Chr(Box::new(a)),
9518 line,
9519 })
9520 }
9521 "ord" => {
9522 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9523 return Ok(e);
9524 }
9525 let a = self.parse_one_arg_or_default()?;
9526 Ok(Expr {
9527 kind: ExprKind::Ord(Box::new(a)),
9528 line,
9529 })
9530 }
9531 "lc" => {
9532 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9533 return Ok(e);
9534 }
9535 let a = self.parse_one_arg_or_default()?;
9536 Ok(Expr {
9537 kind: ExprKind::Lc(Box::new(a)),
9538 line,
9539 })
9540 }
9541 "uc" => {
9542 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9543 return Ok(e);
9544 }
9545 let a = self.parse_one_arg_or_default()?;
9546 Ok(Expr {
9547 kind: ExprKind::Uc(Box::new(a)),
9548 line,
9549 })
9550 }
9551 "lcfirst" => {
9552 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9553 return Ok(e);
9554 }
9555 let a = self.parse_one_arg_or_default()?;
9556 Ok(Expr {
9557 kind: ExprKind::Lcfirst(Box::new(a)),
9558 line,
9559 })
9560 }
9561 "ucfirst" => {
9562 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9563 return Ok(e);
9564 }
9565 let a = self.parse_one_arg_or_default()?;
9566 Ok(Expr {
9567 kind: ExprKind::Ucfirst(Box::new(a)),
9568 line,
9569 })
9570 }
9571 "fc" => {
9572 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9573 return Ok(e);
9574 }
9575 let a = self.parse_one_arg_or_default()?;
9576 Ok(Expr {
9577 kind: ExprKind::Fc(Box::new(a)),
9578 line,
9579 })
9580 }
9581 "crypt" => {
9582 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9583 return Ok(e);
9584 }
9585 let args = self.parse_builtin_args()?;
9586 if args.len() != 2 {
9587 return Err(self.syntax_err("crypt requires two arguments", line));
9588 }
9589 Ok(Expr {
9590 kind: ExprKind::Crypt {
9591 plaintext: Box::new(args[0].clone()),
9592 salt: Box::new(args[1].clone()),
9593 },
9594 line,
9595 })
9596 }
9597 "pos" => {
9598 if matches!(
9599 self.peek(),
9600 Token::Semicolon
9601 | Token::RBrace
9602 | Token::RParen
9603 | Token::Eof
9604 | Token::Comma
9605 | Token::PipeForward
9606 ) {
9607 Ok(Expr {
9608 kind: ExprKind::Pos(None),
9609 line,
9610 })
9611 } else if matches!(self.peek(), Token::Assign) {
9612 self.advance();
9614 let rhs = self.parse_assign_expr()?;
9615 Ok(Expr {
9616 kind: ExprKind::Assign {
9617 target: Box::new(Expr {
9618 kind: ExprKind::Pos(Some(Box::new(Expr {
9619 kind: ExprKind::ScalarVar("_".into()),
9620 line,
9621 }))),
9622 line,
9623 }),
9624 value: Box::new(rhs),
9625 },
9626 line,
9627 })
9628 } else if matches!(self.peek(), Token::LParen) {
9629 self.advance();
9630 if matches!(self.peek(), Token::RParen) {
9631 self.advance();
9632 Ok(Expr {
9633 kind: ExprKind::Pos(None),
9634 line,
9635 })
9636 } else {
9637 let a = self.parse_expression()?;
9638 self.expect(&Token::RParen)?;
9639 Ok(Expr {
9640 kind: ExprKind::Pos(Some(Box::new(a))),
9641 line,
9642 })
9643 }
9644 } else {
9645 let saved = self.pos;
9646 let subj = self.parse_unary()?;
9647 if matches!(self.peek(), Token::Assign) {
9648 self.advance();
9649 let rhs = self.parse_assign_expr()?;
9650 Ok(Expr {
9651 kind: ExprKind::Assign {
9652 target: Box::new(Expr {
9653 kind: ExprKind::Pos(Some(Box::new(subj))),
9654 line,
9655 }),
9656 value: Box::new(rhs),
9657 },
9658 line,
9659 })
9660 } else {
9661 self.pos = saved;
9662 let a = self.parse_one_arg()?;
9663 Ok(Expr {
9664 kind: ExprKind::Pos(Some(Box::new(a))),
9665 line,
9666 })
9667 }
9668 }
9669 }
9670 "study" => {
9671 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9672 return Ok(e);
9673 }
9674 let a = self.parse_one_arg_or_default()?;
9675 Ok(Expr {
9676 kind: ExprKind::Study(Box::new(a)),
9677 line,
9678 })
9679 }
9680 "push" => {
9681 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9682 return Ok(e);
9683 }
9684 let args = self.parse_builtin_args()?;
9685 let (first, rest) = args
9686 .split_first()
9687 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
9688 Ok(Expr {
9689 kind: ExprKind::Push {
9690 array: Box::new(first.clone()),
9691 values: rest.to_vec(),
9692 },
9693 line,
9694 })
9695 }
9696 "pop" => {
9697 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9698 return Ok(e);
9699 }
9700 let a = self.parse_one_arg_or_argv()?;
9701 Ok(Expr {
9702 kind: ExprKind::Pop(Box::new(a)),
9703 line,
9704 })
9705 }
9706 "shift" => {
9707 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9708 return Ok(e);
9709 }
9710 let a = self.parse_one_arg_or_argv()?;
9711 Ok(Expr {
9712 kind: ExprKind::Shift(Box::new(a)),
9713 line,
9714 })
9715 }
9716 "unshift" => {
9717 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9718 return Ok(e);
9719 }
9720 let args = self.parse_builtin_args()?;
9721 let (first, rest) = args
9722 .split_first()
9723 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
9724 Ok(Expr {
9725 kind: ExprKind::Unshift {
9726 array: Box::new(first.clone()),
9727 values: rest.to_vec(),
9728 },
9729 line,
9730 })
9731 }
9732 "splice" => {
9733 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9734 return Ok(e);
9735 }
9736 let args = self.parse_builtin_args()?;
9737 let mut iter = args.into_iter();
9738 let array = Box::new(
9739 iter.next()
9740 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
9741 );
9742 let offset = iter.next().map(Box::new);
9743 let length = iter.next().map(Box::new);
9744 let replacement: Vec<Expr> = iter.collect();
9745 Ok(Expr {
9746 kind: ExprKind::Splice {
9747 array,
9748 offset,
9749 length,
9750 replacement,
9751 },
9752 line,
9753 })
9754 }
9755 "splice_last" | "splice1" | "spl_last" => {
9760 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9761 return Ok(e);
9762 }
9763 let args = self.parse_builtin_args()?;
9764 let mut iter = args.into_iter();
9765 let array = Box::new(
9766 iter.next()
9767 .ok_or_else(|| self.syntax_err("splice_last requires arguments", line))?,
9768 );
9769 let offset = iter.next().map(Box::new);
9770 let length = iter.next().map(Box::new);
9771 let replacement: Vec<Expr> = iter.collect();
9772 let splice_expr = Expr {
9773 kind: ExprKind::Splice {
9774 array,
9775 offset,
9776 length,
9777 replacement,
9778 },
9779 line,
9780 };
9781 Ok(Expr {
9782 kind: ExprKind::FuncCall {
9783 name: "tail".to_string(),
9784 args: vec![splice_expr],
9785 },
9786 line,
9787 })
9788 }
9789 "delete" => {
9790 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9791 return Ok(e);
9792 }
9793 let a = self.parse_postfix()?;
9794 Ok(Expr {
9795 kind: ExprKind::Delete(Box::new(a)),
9796 line,
9797 })
9798 }
9799 "exists" => {
9800 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9801 return Ok(e);
9802 }
9803 let a = self.parse_postfix()?;
9804 Ok(Expr {
9805 kind: ExprKind::Exists(Box::new(a)),
9806 line,
9807 })
9808 }
9809 "keys" => {
9810 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9811 return Ok(e);
9812 }
9813 let a = self.parse_one_arg_or_default()?;
9814 Ok(Expr {
9815 kind: ExprKind::Keys(Box::new(a)),
9816 line,
9817 })
9818 }
9819 "values" => {
9820 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9821 return Ok(e);
9822 }
9823 let a = self.parse_one_arg_or_default()?;
9824 Ok(Expr {
9825 kind: ExprKind::Values(Box::new(a)),
9826 line,
9827 })
9828 }
9829 "each" => {
9830 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9831 return Ok(e);
9832 }
9833 let a = self.parse_one_arg_or_default()?;
9834 Ok(Expr {
9835 kind: ExprKind::Each(Box::new(a)),
9836 line,
9837 })
9838 }
9839 "fore" | "e" | "ep" => {
9840 if matches!(self.peek(), Token::LBrace) {
9842 let (block, list) = self.parse_block_list()?;
9843 Ok(Expr {
9844 kind: ExprKind::ForEachExpr {
9845 block,
9846 list: Box::new(list),
9847 },
9848 line,
9849 })
9850 } else if self.in_pipe_rhs() {
9851 let is_terminal = matches!(
9854 self.peek(),
9855 Token::Semicolon
9856 | Token::RParen
9857 | Token::Eof
9858 | Token::PipeForward
9859 | Token::RBrace
9860 );
9861 let block = if name == "ep" && is_terminal {
9862 vec![Statement {
9863 label: None,
9864 kind: StmtKind::Expression(Expr {
9865 kind: ExprKind::Say {
9866 handle: None,
9867 args: vec![Expr {
9868 kind: ExprKind::ScalarVar("_".into()),
9869 line,
9870 }],
9871 },
9872 line,
9873 }),
9874 line,
9875 }]
9876 } else {
9877 let expr = self.parse_assign_expr_stop_at_pipe()?;
9878 let expr = Self::lift_bareword_to_topic_call(expr);
9879 vec![Statement {
9880 label: None,
9881 kind: StmtKind::Expression(expr),
9882 line,
9883 }]
9884 };
9885 let list = self.pipe_placeholder_list(line);
9886 Ok(Expr {
9887 kind: ExprKind::ForEachExpr {
9888 block,
9889 list: Box::new(list),
9890 },
9891 line,
9892 })
9893 } else {
9894 let expr = self.parse_assign_expr()?;
9903 let expr = Self::lift_bareword_to_topic_call(expr);
9904 if !matches!(self.peek(), Token::Comma) && name == "ep" {
9905 let block = vec![Statement {
9906 label: None,
9907 kind: StmtKind::Expression(Expr {
9908 kind: ExprKind::Say {
9909 handle: None,
9910 args: vec![Expr {
9911 kind: ExprKind::ScalarVar("_".into()),
9912 line,
9913 }],
9914 },
9915 line,
9916 }),
9917 line,
9918 }];
9919 return Ok(Expr {
9920 kind: ExprKind::ForEachExpr {
9921 block,
9922 list: Box::new(expr),
9923 },
9924 line,
9925 });
9926 }
9927 self.expect(&Token::Comma)?;
9928 let list_parts = self.parse_list_until_terminator()?;
9929 let list_expr = if list_parts.len() == 1 {
9930 list_parts.into_iter().next().unwrap()
9931 } else {
9932 Expr {
9933 kind: ExprKind::List(list_parts),
9934 line,
9935 }
9936 };
9937 let block = vec![Statement {
9938 label: None,
9939 kind: StmtKind::Expression(expr),
9940 line,
9941 }];
9942 Ok(Expr {
9943 kind: ExprKind::ForEachExpr {
9944 block,
9945 list: Box::new(list_expr),
9946 },
9947 line,
9948 })
9949 }
9950 }
9951 "rev" => {
9952 let a = if self.in_pipe_rhs()
9958 && matches!(
9959 self.peek(),
9960 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
9961 ) {
9962 self.pipe_placeholder_list(line)
9963 } else if matches!(
9964 self.peek(),
9965 Token::Semicolon
9966 | Token::RBrace
9967 | Token::RParen
9968 | Token::RBracket
9969 | Token::Eof
9970 | Token::Comma
9971 | Token::FatArrow
9972 | Token::PipeForward
9973 ) {
9974 Expr {
9975 kind: ExprKind::ScalarVar("_".into()),
9976 line: self.peek_line(),
9977 }
9978 } else if matches!(self.peek(), Token::LParen)
9979 && matches!(self.peek_at(1), Token::RParen)
9980 {
9981 let pl = self.peek_line();
9984 self.advance(); self.advance(); Expr {
9987 kind: ExprKind::ScalarVar("_".into()),
9988 line: pl,
9989 }
9990 } else {
9991 self.parse_one_arg()?
9992 };
9993 Ok(Expr {
9994 kind: ExprKind::Rev(Box::new(a)),
9995 line,
9996 })
9997 }
9998 "reverse" => {
9999 if crate::no_interop_mode() {
10000 return Err(self.syntax_err(
10001 "stryke uses `rev` instead of `reverse` (--no-interop)",
10002 line,
10003 ));
10004 }
10005 let a = if self.in_pipe_rhs()
10007 && matches!(
10008 self.peek(),
10009 Token::Semicolon
10010 | Token::RBrace
10011 | Token::RParen
10012 | Token::Eof
10013 | Token::PipeForward
10014 ) {
10015 self.pipe_placeholder_list(line)
10016 } else {
10017 self.parse_one_arg()?
10018 };
10019 Ok(Expr {
10020 kind: ExprKind::ReverseExpr(Box::new(a)),
10021 line,
10022 })
10023 }
10024 "reversed" | "rv" => {
10025 let a = if self.in_pipe_rhs()
10027 && matches!(
10028 self.peek(),
10029 Token::Semicolon
10030 | Token::RBrace
10031 | Token::RParen
10032 | Token::Eof
10033 | Token::PipeForward
10034 ) {
10035 self.pipe_placeholder_list(line)
10036 } else {
10037 self.parse_one_arg()?
10038 };
10039 Ok(Expr {
10040 kind: ExprKind::Rev(Box::new(a)),
10041 line,
10042 })
10043 }
10044 "join" => {
10045 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10046 return Ok(e);
10047 }
10048 let args = self.parse_builtin_args()?;
10049 if args.is_empty() {
10050 return Err(self.syntax_err("join requires separator and list", line));
10051 }
10052 if args.len() < 2 && !self.in_pipe_rhs() {
10054 return Err(self.syntax_err("join requires separator and list", line));
10055 }
10056 Ok(Expr {
10057 kind: ExprKind::JoinExpr {
10058 separator: Box::new(args[0].clone()),
10059 list: Box::new(Expr {
10060 kind: ExprKind::List(args[1..].to_vec()),
10061 line,
10062 }),
10063 },
10064 line,
10065 })
10066 }
10067 "split" => {
10068 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10069 return Ok(e);
10070 }
10071 let args = self.parse_builtin_args()?;
10072 let pattern = args.first().cloned().unwrap_or(Expr {
10073 kind: ExprKind::String(" ".into()),
10074 line,
10075 });
10076 let string = args.get(1).cloned().unwrap_or(Expr {
10077 kind: ExprKind::ScalarVar("_".into()),
10078 line,
10079 });
10080 let limit = args.get(2).cloned().map(Box::new);
10081 Ok(Expr {
10082 kind: ExprKind::SplitExpr {
10083 pattern: Box::new(pattern),
10084 string: Box::new(string),
10085 limit,
10086 },
10087 line,
10088 })
10089 }
10090 "substr" => {
10091 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10092 return Ok(e);
10093 }
10094 let args = self.parse_builtin_args()?;
10095 Ok(Expr {
10096 kind: ExprKind::Substr {
10097 string: Box::new(args[0].clone()),
10098 offset: Box::new(args[1].clone()),
10099 length: args.get(2).cloned().map(Box::new),
10100 replacement: args.get(3).cloned().map(Box::new),
10101 },
10102 line,
10103 })
10104 }
10105 "index" => {
10106 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10107 return Ok(e);
10108 }
10109 let args = self.parse_builtin_args()?;
10110 Ok(Expr {
10111 kind: ExprKind::Index {
10112 string: Box::new(args[0].clone()),
10113 substr: Box::new(args[1].clone()),
10114 position: args.get(2).cloned().map(Box::new),
10115 },
10116 line,
10117 })
10118 }
10119 "rindex" => {
10120 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10121 return Ok(e);
10122 }
10123 let args = self.parse_builtin_args()?;
10124 Ok(Expr {
10125 kind: ExprKind::Rindex {
10126 string: Box::new(args[0].clone()),
10127 substr: Box::new(args[1].clone()),
10128 position: args.get(2).cloned().map(Box::new),
10129 },
10130 line,
10131 })
10132 }
10133 "sprintf" => {
10134 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10135 return Ok(e);
10136 }
10137 let args = self.parse_builtin_args()?;
10138 let (first, rest) = args
10139 .split_first()
10140 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
10141 Ok(Expr {
10142 kind: ExprKind::Sprintf {
10143 format: Box::new(first.clone()),
10144 args: rest.to_vec(),
10145 },
10146 line,
10147 })
10148 }
10149 "map" | "flat_map" | "maps" | "flat_maps" => {
10150 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
10151 let stream = matches!(name.as_str(), "maps" | "flat_maps");
10152 if matches!(self.peek(), Token::LBrace) {
10153 let (block, list) = self.parse_block_list()?;
10154 Ok(Expr {
10155 kind: ExprKind::MapExpr {
10156 block,
10157 list: Box::new(list),
10158 flatten_array_refs,
10159 stream,
10160 },
10161 line,
10162 })
10163 } else {
10164 let expr = self.parse_assign_expr_stop_at_pipe()?;
10165 let expr = Self::lift_bareword_to_topic_call(expr);
10168 let list_expr = if self.pipe_supplies_slurped_list_operand() {
10169 self.pipe_placeholder_list(line)
10170 } else {
10171 self.expect(&Token::Comma)?;
10172 let list_parts = self.parse_list_until_terminator()?;
10173 if list_parts.len() == 1 {
10174 list_parts.into_iter().next().unwrap()
10175 } else {
10176 Expr {
10177 kind: ExprKind::List(list_parts),
10178 line,
10179 }
10180 }
10181 };
10182 Ok(Expr {
10183 kind: ExprKind::MapExprComma {
10184 expr: Box::new(expr),
10185 list: Box::new(list_expr),
10186 flatten_array_refs,
10187 stream,
10188 },
10189 line,
10190 })
10191 }
10192 }
10193 "cond" => {
10194 if crate::compat_mode() {
10195 return Err(self
10196 .syntax_err("`cond` is a stryke extension (disabled by --compat)", line));
10197 }
10198 self.parse_cond_expr(line)
10199 }
10200 "match" => {
10201 if crate::compat_mode() {
10202 return Err(self.syntax_err(
10203 "algebraic `match` is a stryke extension (disabled by --compat)",
10204 line,
10205 ));
10206 }
10207 self.parse_algebraic_match_expr(line)
10208 }
10209 "grep" | "greps" | "filter" | "fi" | "find_all" => {
10210 let keyword = match name.as_str() {
10211 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
10212 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
10213 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
10214 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
10215 _ => unreachable!(),
10216 };
10217 if matches!(self.peek(), Token::LBrace) {
10218 let (block, list) = self.parse_block_list()?;
10219 Ok(Expr {
10220 kind: ExprKind::GrepExpr {
10221 block,
10222 list: Box::new(list),
10223 keyword,
10224 },
10225 line,
10226 })
10227 } else {
10228 let expr = self.parse_assign_expr_stop_at_pipe()?;
10229 if self.pipe_supplies_slurped_list_operand() {
10230 let list = self.pipe_placeholder_list(line);
10235 let topic = Expr {
10236 kind: ExprKind::ScalarVar("_".into()),
10237 line,
10238 };
10239 let test = match &expr.kind {
10240 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
10241 kind: ExprKind::BinOp {
10242 op: BinOp::NumEq,
10243 left: Box::new(topic),
10244 right: Box::new(expr),
10245 },
10246 line,
10247 },
10248 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
10249 kind: ExprKind::BinOp {
10250 op: BinOp::StrEq,
10251 left: Box::new(topic),
10252 right: Box::new(expr),
10253 },
10254 line,
10255 },
10256 ExprKind::Regex { .. } => Expr {
10257 kind: ExprKind::BinOp {
10258 op: BinOp::BindMatch,
10259 left: Box::new(topic),
10260 right: Box::new(expr),
10261 },
10262 line,
10263 },
10264 _ => {
10265 let expr = Self::lift_bareword_to_topic_call(expr);
10271 return Ok(Expr {
10272 kind: ExprKind::GrepExprComma {
10273 expr: Box::new(expr),
10274 list: Box::new(list),
10275 keyword,
10276 },
10277 line,
10278 });
10279 }
10280 };
10281 let block = vec![Statement {
10282 label: None,
10283 kind: StmtKind::Expression(test),
10284 line,
10285 }];
10286 Ok(Expr {
10287 kind: ExprKind::GrepExpr {
10288 block,
10289 list: Box::new(list),
10290 keyword,
10291 },
10292 line,
10293 })
10294 } else {
10295 let expr = Self::lift_bareword_to_topic_call(expr);
10296 self.expect(&Token::Comma)?;
10297 let list_parts = self.parse_list_until_terminator()?;
10298 let list_expr = if list_parts.len() == 1 {
10299 list_parts.into_iter().next().unwrap()
10300 } else {
10301 Expr {
10302 kind: ExprKind::List(list_parts),
10303 line,
10304 }
10305 };
10306 Ok(Expr {
10307 kind: ExprKind::GrepExprComma {
10308 expr: Box::new(expr),
10309 list: Box::new(list_expr),
10310 keyword,
10311 },
10312 line,
10313 })
10314 }
10315 }
10316 }
10317 "sort" => {
10318 use crate::ast::SortComparator;
10319 if matches!(self.peek(), Token::LBrace) {
10320 let block = self.parse_block()?;
10321 let block_end_line = self.prev_line();
10322 let _ = self.eat(&Token::Comma);
10323 let list = if self.in_pipe_rhs()
10324 && (matches!(
10325 self.peek(),
10326 Token::Semicolon
10327 | Token::RBrace
10328 | Token::RParen
10329 | Token::Eof
10330 | Token::PipeForward
10331 ) || self.peek_line() > block_end_line)
10332 {
10333 self.pipe_placeholder_list(line)
10334 } else {
10335 self.parse_expression()?
10336 };
10337 Ok(Expr {
10338 kind: ExprKind::SortExpr {
10339 cmp: Some(SortComparator::Block(block)),
10340 list: Box::new(list),
10341 },
10342 line,
10343 })
10344 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
10345 let block = self.parse_block_or_bareword_cmp_block()?;
10347 let _ = self.eat(&Token::Comma);
10348 let list = if self.in_pipe_rhs()
10349 && matches!(
10350 self.peek(),
10351 Token::Semicolon
10352 | Token::RBrace
10353 | Token::RParen
10354 | Token::Eof
10355 | Token::PipeForward
10356 ) {
10357 self.pipe_placeholder_list(line)
10358 } else {
10359 self.parse_expression()?
10360 };
10361 Ok(Expr {
10362 kind: ExprKind::SortExpr {
10363 cmp: Some(SortComparator::Block(block)),
10364 list: Box::new(list),
10365 },
10366 line,
10367 })
10368 } else if matches!(self.peek(), Token::ScalarVar(_)) {
10369 self.suppress_indirect_paren_call =
10372 self.suppress_indirect_paren_call.saturating_add(1);
10373 let code = self.parse_assign_expr()?;
10374 self.suppress_indirect_paren_call =
10375 self.suppress_indirect_paren_call.saturating_sub(1);
10376 let _ = self.eat(&Token::Comma);
10377 let list = if self.in_pipe_rhs()
10378 && matches!(
10379 self.peek(),
10380 Token::Semicolon
10381 | Token::RBrace
10382 | Token::RParen
10383 | Token::Eof
10384 | Token::PipeForward
10385 ) {
10386 self.pipe_placeholder_list(line)
10387 } else if matches!(self.peek(), Token::LParen) {
10388 self.advance();
10389 let e = self.parse_expression()?;
10390 self.expect(&Token::RParen)?;
10391 e
10392 } else {
10393 self.parse_expression()?
10394 };
10395 Ok(Expr {
10396 kind: ExprKind::SortExpr {
10397 cmp: Some(SortComparator::Code(Box::new(code))),
10398 list: Box::new(list),
10399 },
10400 line,
10401 })
10402 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
10403 {
10404 let block = self.parse_block_or_bareword_cmp_block()?;
10406 let _ = self.eat(&Token::Comma);
10407 let list = if self.in_pipe_rhs()
10408 && matches!(
10409 self.peek(),
10410 Token::Semicolon
10411 | Token::RBrace
10412 | Token::RParen
10413 | Token::Eof
10414 | Token::PipeForward
10415 ) {
10416 self.pipe_placeholder_list(line)
10417 } else {
10418 self.parse_expression()?
10419 };
10420 Ok(Expr {
10421 kind: ExprKind::SortExpr {
10422 cmp: Some(SortComparator::Block(block)),
10423 list: Box::new(list),
10424 },
10425 line,
10426 })
10427 } else {
10428 let list = if self.in_pipe_rhs()
10431 && matches!(
10432 self.peek(),
10433 Token::Semicolon
10434 | Token::RBrace
10435 | Token::RParen
10436 | Token::Eof
10437 | Token::PipeForward
10438 ) {
10439 self.pipe_placeholder_list(line)
10440 } else {
10441 self.parse_expression()?
10442 };
10443 Ok(Expr {
10444 kind: ExprKind::SortExpr {
10445 cmp: None,
10446 list: Box::new(list),
10447 },
10448 line,
10449 })
10450 }
10451 }
10452 "reduce" | "fold" | "inject" => {
10453 let (block, list) = self.parse_block_list()?;
10454 Ok(Expr {
10455 kind: ExprKind::ReduceExpr {
10456 block,
10457 list: Box::new(list),
10458 },
10459 line,
10460 })
10461 }
10462 "pmap" => {
10464 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10465 Ok(Expr {
10466 kind: ExprKind::PMapExpr {
10467 block,
10468 list: Box::new(list),
10469 progress: progress.map(Box::new),
10470 flat_outputs: false,
10471 on_cluster: None,
10472 stream: false,
10473 },
10474 line,
10475 })
10476 }
10477 "pmap_on" => {
10478 let (cluster, block, list, progress) =
10479 self.parse_cluster_block_then_list_optional_progress()?;
10480 Ok(Expr {
10481 kind: ExprKind::PMapExpr {
10482 block,
10483 list: Box::new(list),
10484 progress: progress.map(Box::new),
10485 flat_outputs: false,
10486 on_cluster: Some(Box::new(cluster)),
10487 stream: false,
10488 },
10489 line,
10490 })
10491 }
10492 "pflat_map" => {
10493 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10494 Ok(Expr {
10495 kind: ExprKind::PMapExpr {
10496 block,
10497 list: Box::new(list),
10498 progress: progress.map(Box::new),
10499 flat_outputs: true,
10500 on_cluster: None,
10501 stream: false,
10502 },
10503 line,
10504 })
10505 }
10506 "pflat_map_on" => {
10507 let (cluster, block, list, progress) =
10508 self.parse_cluster_block_then_list_optional_progress()?;
10509 Ok(Expr {
10510 kind: ExprKind::PMapExpr {
10511 block,
10512 list: Box::new(list),
10513 progress: progress.map(Box::new),
10514 flat_outputs: true,
10515 on_cluster: Some(Box::new(cluster)),
10516 stream: false,
10517 },
10518 line,
10519 })
10520 }
10521 "pmaps" => {
10522 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10523 Ok(Expr {
10524 kind: ExprKind::PMapExpr {
10525 block,
10526 list: Box::new(list),
10527 progress: progress.map(Box::new),
10528 flat_outputs: false,
10529 on_cluster: None,
10530 stream: true,
10531 },
10532 line,
10533 })
10534 }
10535 "pflat_maps" => {
10536 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10537 Ok(Expr {
10538 kind: ExprKind::PMapExpr {
10539 block,
10540 list: Box::new(list),
10541 progress: progress.map(Box::new),
10542 flat_outputs: true,
10543 on_cluster: None,
10544 stream: true,
10545 },
10546 line,
10547 })
10548 }
10549 "pgreps" => {
10550 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10551 Ok(Expr {
10552 kind: ExprKind::PGrepExpr {
10553 block,
10554 list: Box::new(list),
10555 progress: progress.map(Box::new),
10556 stream: true,
10557 },
10558 line,
10559 })
10560 }
10561 "pmap_chunked" => {
10562 let chunk_size = self.parse_assign_expr()?;
10563 let block = self.parse_block_or_bareword_block()?;
10564 self.eat(&Token::Comma);
10565 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
10566 Ok(Expr {
10567 kind: ExprKind::PMapChunkedExpr {
10568 chunk_size: Box::new(chunk_size),
10569 block,
10570 list: Box::new(list),
10571 progress: progress.map(Box::new),
10572 },
10573 line,
10574 })
10575 }
10576 "pgrep" => {
10577 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10578 Ok(Expr {
10579 kind: ExprKind::PGrepExpr {
10580 block,
10581 list: Box::new(list),
10582 progress: progress.map(Box::new),
10583 stream: false,
10584 },
10585 line,
10586 })
10587 }
10588 "pfor" => {
10589 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10590 Ok(Expr {
10591 kind: ExprKind::PForExpr {
10592 block,
10593 list: Box::new(list),
10594 progress: progress.map(Box::new),
10595 },
10596 line,
10597 })
10598 }
10599 "par_lines" | "par_walk" => {
10600 let args = self.parse_builtin_args()?;
10601 if args.len() < 2 {
10602 return Err(
10603 self.syntax_err(format!("{} requires at least two arguments", name), line)
10604 );
10605 }
10606
10607 if name == "par_lines" {
10608 Ok(Expr {
10609 kind: ExprKind::ParLinesExpr {
10610 path: Box::new(args[0].clone()),
10611 callback: Box::new(args[1].clone()),
10612 progress: None,
10613 },
10614 line,
10615 })
10616 } else {
10617 Ok(Expr {
10618 kind: ExprKind::ParWalkExpr {
10619 path: Box::new(args[0].clone()),
10620 callback: Box::new(args[1].clone()),
10621 progress: None,
10622 },
10623 line,
10624 })
10625 }
10626 }
10627 "pwatch" | "watch" => {
10628 let args = self.parse_builtin_args()?;
10629 if args.len() < 2 {
10630 return Err(
10631 self.syntax_err(format!("{} requires at least two arguments", name), line)
10632 );
10633 }
10634 Ok(Expr {
10635 kind: ExprKind::PwatchExpr {
10636 path: Box::new(args[0].clone()),
10637 callback: Box::new(args[1].clone()),
10638 },
10639 line,
10640 })
10641 }
10642 "fan" => {
10643 let (count, block) = self.parse_fan_count_and_block(line)?;
10649 let progress = self.parse_fan_optional_progress("fan")?;
10650 Ok(Expr {
10651 kind: ExprKind::FanExpr {
10652 count,
10653 block,
10654 progress,
10655 capture: false,
10656 },
10657 line,
10658 })
10659 }
10660 "fan_cap" => {
10661 let (count, block) = self.parse_fan_count_and_block(line)?;
10662 let progress = self.parse_fan_optional_progress("fan_cap")?;
10663 Ok(Expr {
10664 kind: ExprKind::FanExpr {
10665 count,
10666 block,
10667 progress,
10668 capture: true,
10669 },
10670 line,
10671 })
10672 }
10673 "async" => {
10674 if !matches!(self.peek(), Token::LBrace) {
10675 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
10676 }
10677 let block = self.parse_block()?;
10678 Ok(Expr {
10679 kind: ExprKind::AsyncBlock { body: block },
10680 line,
10681 })
10682 }
10683 "spawn" => {
10684 if !matches!(self.peek(), Token::LBrace) {
10685 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
10686 }
10687 let block = self.parse_block()?;
10688 Ok(Expr {
10689 kind: ExprKind::SpawnBlock { body: block },
10690 line,
10691 })
10692 }
10693 "trace" => {
10694 if !matches!(self.peek(), Token::LBrace) {
10695 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
10696 }
10697 let block = self.parse_block()?;
10698 Ok(Expr {
10699 kind: ExprKind::Trace { body: block },
10700 line,
10701 })
10702 }
10703 "timer" => {
10704 let block = self.parse_block_or_bareword_block_no_args()?;
10705 Ok(Expr {
10706 kind: ExprKind::Timer { body: block },
10707 line,
10708 })
10709 }
10710 "bench" => {
10711 let block = self.parse_block_or_bareword_block_no_args()?;
10712 let times = Box::new(self.parse_expression()?);
10713 Ok(Expr {
10714 kind: ExprKind::Bench { body: block, times },
10715 line,
10716 })
10717 }
10718 "spinner" => {
10719 let (message, body) = if matches!(self.peek(), Token::LBrace) {
10721 let body = self.parse_block()?;
10722 (
10723 Box::new(Expr {
10724 kind: ExprKind::String("working".to_string()),
10725 line,
10726 }),
10727 body,
10728 )
10729 } else {
10730 let msg = self.parse_assign_expr()?;
10731 let body = self.parse_block()?;
10732 (Box::new(msg), body)
10733 };
10734 Ok(Expr {
10735 kind: ExprKind::Spinner { message, body },
10736 line,
10737 })
10738 }
10739 "thread" | "t" => {
10740 self.parse_thread_macro(line, false)
10750 }
10751 "retry" => {
10752 let body = if matches!(self.peek(), Token::LBrace) {
10755 self.parse_block()?
10756 } else {
10757 let bw_line = self.peek_line();
10758 let Token::Ident(ref name) = self.peek().clone() else {
10759 return Err(self
10760 .syntax_err("retry: expected block or bareword function name", line));
10761 };
10762 let name = name.clone();
10763 self.advance();
10764 vec![Statement::new(
10765 StmtKind::Expression(Expr {
10766 kind: ExprKind::FuncCall { name, args: vec![] },
10767 line: bw_line,
10768 }),
10769 bw_line,
10770 )]
10771 };
10772 self.eat(&Token::Comma);
10773 match self.peek() {
10774 Token::Ident(ref s) if s == "times" => {
10775 self.advance();
10776 }
10777 _ => {
10778 return Err(self.syntax_err("retry: expected `times =>` after block", line));
10779 }
10780 }
10781 self.expect(&Token::FatArrow)?;
10782 let times = Box::new(self.parse_assign_expr()?);
10783 let mut backoff = RetryBackoff::None;
10784 if self.eat(&Token::Comma) {
10785 match self.peek() {
10786 Token::Ident(ref s) if s == "backoff" => {
10787 self.advance();
10788 }
10789 _ => {
10790 return Err(
10791 self.syntax_err("retry: expected `backoff =>` after comma", line)
10792 );
10793 }
10794 }
10795 self.expect(&Token::FatArrow)?;
10796 let Token::Ident(mode) = self.peek().clone() else {
10797 return Err(self.syntax_err(
10798 "retry: expected backoff mode (none, linear, exponential)",
10799 line,
10800 ));
10801 };
10802 backoff = match mode.as_str() {
10803 "none" => RetryBackoff::None,
10804 "linear" => RetryBackoff::Linear,
10805 "exponential" => RetryBackoff::Exponential,
10806 _ => {
10807 return Err(
10808 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
10809 );
10810 }
10811 };
10812 self.advance();
10813 }
10814 Ok(Expr {
10815 kind: ExprKind::RetryBlock {
10816 body,
10817 times,
10818 backoff,
10819 },
10820 line,
10821 })
10822 }
10823 "rate_limit" => {
10824 self.expect(&Token::LParen)?;
10825 let max = Box::new(self.parse_assign_expr()?);
10826 self.expect(&Token::Comma)?;
10827 let window = Box::new(self.parse_assign_expr()?);
10828 self.expect(&Token::RParen)?;
10829 let body = self.parse_block_or_bareword_block_no_args()?;
10830 let slot = self.alloc_rate_limit_slot();
10831 Ok(Expr {
10832 kind: ExprKind::RateLimitBlock {
10833 slot,
10834 max,
10835 window,
10836 body,
10837 },
10838 line,
10839 })
10840 }
10841 "every" => {
10842 let has_paren = self.eat(&Token::LParen);
10845 let interval = Box::new(self.parse_assign_expr()?);
10846 if has_paren {
10847 self.expect(&Token::RParen)?;
10848 }
10849 let body = if matches!(self.peek(), Token::LBrace) {
10850 self.parse_block()?
10851 } else {
10852 let bline = self.peek_line();
10853 let expr = self.parse_assign_expr()?;
10854 vec![Statement::new(StmtKind::Expression(expr), bline)]
10855 };
10856 Ok(Expr {
10857 kind: ExprKind::EveryBlock { interval, body },
10858 line,
10859 })
10860 }
10861 "gen" => {
10862 if !matches!(self.peek(), Token::LBrace) {
10863 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
10864 }
10865 let body = self.parse_block()?;
10866 Ok(Expr {
10867 kind: ExprKind::GenBlock { body },
10868 line,
10869 })
10870 }
10871 "yield" => {
10872 let e = self.parse_assign_expr()?;
10873 Ok(Expr {
10874 kind: ExprKind::Yield(Box::new(e)),
10875 line,
10876 })
10877 }
10878 "await" => {
10879 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10880 return Ok(e);
10881 }
10882 let a = self.parse_one_arg_or_default()?;
10885 Ok(Expr {
10886 kind: ExprKind::Await(Box::new(a)),
10887 line,
10888 })
10889 }
10890 "slurp" | "cat" | "c" => {
10891 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10892 return Ok(e);
10893 }
10894 let a = self.parse_one_arg_or_default()?;
10895 Ok(Expr {
10896 kind: ExprKind::Slurp(Box::new(a)),
10897 line,
10898 })
10899 }
10900 "capture" => {
10901 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10902 return Ok(e);
10903 }
10904 let a = self.parse_one_arg()?;
10905 Ok(Expr {
10906 kind: ExprKind::Capture(Box::new(a)),
10907 line,
10908 })
10909 }
10910 "fetch_url" => {
10911 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10912 return Ok(e);
10913 }
10914 let a = self.parse_one_arg()?;
10915 Ok(Expr {
10916 kind: ExprKind::FetchUrl(Box::new(a)),
10917 line,
10918 })
10919 }
10920 "pchannel" => {
10921 let capacity = if self.eat(&Token::LParen) {
10922 if matches!(self.peek(), Token::RParen) {
10923 self.advance();
10924 None
10925 } else {
10926 let e = self.parse_expression()?;
10927 self.expect(&Token::RParen)?;
10928 Some(Box::new(e))
10929 }
10930 } else {
10931 None
10932 };
10933 Ok(Expr {
10934 kind: ExprKind::Pchannel { capacity },
10935 line,
10936 })
10937 }
10938 "psort" => {
10939 if matches!(self.peek(), Token::LBrace)
10940 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
10941 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
10942 {
10943 let block = self.parse_block_or_bareword_cmp_block()?;
10944 self.eat(&Token::Comma);
10945 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
10946 Ok(Expr {
10947 kind: ExprKind::PSortExpr {
10948 cmp: Some(block),
10949 list: Box::new(list),
10950 progress: progress.map(Box::new),
10951 },
10952 line,
10953 })
10954 } else {
10955 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
10956 Ok(Expr {
10957 kind: ExprKind::PSortExpr {
10958 cmp: None,
10959 list: Box::new(list),
10960 progress: progress.map(Box::new),
10961 },
10962 line,
10963 })
10964 }
10965 }
10966 "preduce" => {
10967 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10968 Ok(Expr {
10969 kind: ExprKind::PReduceExpr {
10970 block,
10971 list: Box::new(list),
10972 progress: progress.map(Box::new),
10973 },
10974 line,
10975 })
10976 }
10977 "preduce_init" => {
10978 let (init, block, list, progress) =
10979 self.parse_init_block_then_list_optional_progress()?;
10980 Ok(Expr {
10981 kind: ExprKind::PReduceInitExpr {
10982 init: Box::new(init),
10983 block,
10984 list: Box::new(list),
10985 progress: progress.map(Box::new),
10986 },
10987 line,
10988 })
10989 }
10990 "pmap_reduce" => {
10991 let map_block = self.parse_block_or_bareword_block()?;
10992 let reduce_block = if matches!(self.peek(), Token::LBrace) {
10995 self.parse_block()?
10996 } else {
10997 self.expect(&Token::Comma)?;
10999 self.parse_block_or_bareword_cmp_block()?
11000 };
11001 self.eat(&Token::Comma);
11002 let line = self.peek_line();
11003 if let Token::Ident(ref kw) = self.peek().clone() {
11004 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11005 self.advance();
11006 self.expect(&Token::FatArrow)?;
11007 let prog = self.parse_assign_expr()?;
11008 return Ok(Expr {
11009 kind: ExprKind::PMapReduceExpr {
11010 map_block,
11011 reduce_block,
11012 list: Box::new(Expr {
11013 kind: ExprKind::List(vec![]),
11014 line,
11015 }),
11016 progress: Some(Box::new(prog)),
11017 },
11018 line,
11019 });
11020 }
11021 }
11022 if matches!(
11023 self.peek(),
11024 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11025 ) {
11026 return Ok(Expr {
11027 kind: ExprKind::PMapReduceExpr {
11028 map_block,
11029 reduce_block,
11030 list: Box::new(Expr {
11031 kind: ExprKind::List(vec![]),
11032 line,
11033 }),
11034 progress: None,
11035 },
11036 line,
11037 });
11038 }
11039 let mut parts = vec![self.parse_assign_expr()?];
11040 loop {
11041 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11042 break;
11043 }
11044 if matches!(
11045 self.peek(),
11046 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11047 ) {
11048 break;
11049 }
11050 if let Token::Ident(ref kw) = self.peek().clone() {
11051 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11052 self.advance();
11053 self.expect(&Token::FatArrow)?;
11054 let prog = self.parse_assign_expr()?;
11055 return Ok(Expr {
11056 kind: ExprKind::PMapReduceExpr {
11057 map_block,
11058 reduce_block,
11059 list: Box::new(merge_expr_list(parts)),
11060 progress: Some(Box::new(prog)),
11061 },
11062 line,
11063 });
11064 }
11065 }
11066 parts.push(self.parse_assign_expr()?);
11067 }
11068 Ok(Expr {
11069 kind: ExprKind::PMapReduceExpr {
11070 map_block,
11071 reduce_block,
11072 list: Box::new(merge_expr_list(parts)),
11073 progress: None,
11074 },
11075 line,
11076 })
11077 }
11078 "puniq" => {
11079 if self.pipe_supplies_slurped_list_operand() {
11080 return Ok(Expr {
11081 kind: ExprKind::FuncCall {
11082 name: "puniq".to_string(),
11083 args: vec![],
11084 },
11085 line,
11086 });
11087 }
11088 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11089 let mut args = vec![list];
11090 if let Some(p) = progress {
11091 args.push(p);
11092 }
11093 Ok(Expr {
11094 kind: ExprKind::FuncCall {
11095 name: "puniq".to_string(),
11096 args,
11097 },
11098 line,
11099 })
11100 }
11101 "pfirst" => {
11102 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11103 let cr = Expr {
11104 kind: ExprKind::CodeRef {
11105 params: vec![],
11106 body: block,
11107 },
11108 line,
11109 };
11110 let mut args = vec![cr, list];
11111 if let Some(p) = progress {
11112 args.push(p);
11113 }
11114 Ok(Expr {
11115 kind: ExprKind::FuncCall {
11116 name: "pfirst".to_string(),
11117 args,
11118 },
11119 line,
11120 })
11121 }
11122 "pany" => {
11123 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11124 let cr = Expr {
11125 kind: ExprKind::CodeRef {
11126 params: vec![],
11127 body: block,
11128 },
11129 line,
11130 };
11131 let mut args = vec![cr, list];
11132 if let Some(p) = progress {
11133 args.push(p);
11134 }
11135 Ok(Expr {
11136 kind: ExprKind::FuncCall {
11137 name: "pany".to_string(),
11138 args,
11139 },
11140 line,
11141 })
11142 }
11143 "uniq" | "distinct" => {
11144 if self.pipe_supplies_slurped_list_operand() {
11145 return Ok(Expr {
11146 kind: ExprKind::FuncCall {
11147 name: name.clone(),
11148 args: vec![],
11149 },
11150 line,
11151 });
11152 }
11153 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11154 if progress.is_some() {
11155 return Err(self.syntax_err(
11156 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
11157 line,
11158 ));
11159 }
11160 Ok(Expr {
11161 kind: ExprKind::FuncCall {
11162 name: name.clone(),
11163 args: vec![list],
11164 },
11165 line,
11166 })
11167 }
11168 "flatten" => {
11169 if self.pipe_supplies_slurped_list_operand() {
11170 return Ok(Expr {
11171 kind: ExprKind::FuncCall {
11172 name: "flatten".to_string(),
11173 args: vec![],
11174 },
11175 line,
11176 });
11177 }
11178 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11179 if progress.is_some() {
11180 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
11181 }
11182 Ok(Expr {
11183 kind: ExprKind::FuncCall {
11184 name: "flatten".to_string(),
11185 args: vec![list],
11186 },
11187 line,
11188 })
11189 }
11190 "set" => {
11191 if self.pipe_supplies_slurped_list_operand() {
11192 return Ok(Expr {
11193 kind: ExprKind::FuncCall {
11194 name: "set".to_string(),
11195 args: vec![],
11196 },
11197 line,
11198 });
11199 }
11200 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11201 if progress.is_some() {
11202 return Err(self.syntax_err("`progress =>` is not supported for set", line));
11203 }
11204 Ok(Expr {
11205 kind: ExprKind::FuncCall {
11206 name: "set".to_string(),
11207 args: vec![list],
11208 },
11209 line,
11210 })
11211 }
11212 "size" => {
11216 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11217 return Ok(e);
11218 }
11219 if self.pipe_supplies_slurped_list_operand() {
11220 return Ok(Expr {
11221 kind: ExprKind::FuncCall {
11222 name: "size".to_string(),
11223 args: vec![],
11224 },
11225 line,
11226 });
11227 }
11228 let a = self.parse_one_arg_or_default()?;
11229 Ok(Expr {
11230 kind: ExprKind::FuncCall {
11231 name: "size".to_string(),
11232 args: vec![a],
11233 },
11234 line,
11235 })
11236 }
11237 "list_count" | "list_size" | "count" | "len" | "cnt" => {
11238 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11239 return Ok(e);
11240 }
11241 if self.pipe_supplies_slurped_list_operand() {
11242 return Ok(Expr {
11243 kind: ExprKind::FuncCall {
11244 name: name.clone(),
11245 args: vec![],
11246 },
11247 line,
11248 });
11249 }
11250 let args = if matches!(self.peek(), Token::LParen) {
11264 self.advance();
11265 if matches!(self.peek(), Token::RParen) {
11266 self.advance();
11267 Vec::new()
11268 } else {
11269 let inner = self.parse_expression()?;
11270 self.expect(&Token::RParen)?;
11271 vec![inner]
11272 }
11273 } else if self.peek_is_named_unary_terminator() {
11274 Vec::new()
11275 } else {
11276 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11277 if progress.is_some() {
11278 return Err(self.syntax_err(
11279 "`progress =>` is not supported for list_count / list_size / count / cnt",
11280 line,
11281 ));
11282 }
11283 vec![list]
11284 };
11285 Ok(Expr {
11286 kind: ExprKind::FuncCall {
11287 name: name.clone(),
11288 args,
11289 },
11290 line,
11291 })
11292 }
11293 "shuffle" | "shuffled" => {
11294 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11295 return Ok(e);
11296 }
11297 if self.pipe_supplies_slurped_list_operand() {
11298 return Ok(Expr {
11299 kind: ExprKind::FuncCall {
11300 name: "shuffle".to_string(),
11301 args: vec![],
11302 },
11303 line,
11304 });
11305 }
11306 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11307 if progress.is_some() {
11308 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
11309 }
11310 Ok(Expr {
11311 kind: ExprKind::FuncCall {
11312 name: "shuffle".to_string(),
11313 args: vec![list],
11314 },
11315 line,
11316 })
11317 }
11318 "chunked" => {
11319 let mut parts = Vec::new();
11320 if self.eat(&Token::LParen) {
11321 if !matches!(self.peek(), Token::RParen) {
11322 parts.push(self.parse_assign_expr()?);
11323 while self.eat(&Token::Comma) {
11324 if matches!(self.peek(), Token::RParen) {
11325 break;
11326 }
11327 parts.push(self.parse_assign_expr()?);
11328 }
11329 }
11330 self.expect(&Token::RParen)?;
11331 } else {
11332 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11336 loop {
11337 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11338 break;
11339 }
11340 if matches!(
11341 self.peek(),
11342 Token::Semicolon
11343 | Token::RBrace
11344 | Token::RParen
11345 | Token::Eof
11346 | Token::PipeForward
11347 ) {
11348 break;
11349 }
11350 if self.peek_is_postfix_stmt_modifier_keyword() {
11351 break;
11352 }
11353 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11354 }
11355 }
11356 if parts.len() == 1 {
11357 let n = parts.pop().unwrap();
11358 return Ok(Expr {
11359 kind: ExprKind::FuncCall {
11360 name: "chunked".to_string(),
11361 args: vec![n],
11362 },
11363 line,
11364 });
11365 }
11366 if parts.is_empty() {
11367 return Ok(Expr {
11368 kind: ExprKind::FuncCall {
11369 name: "chunked".to_string(),
11370 args: parts,
11371 },
11372 line,
11373 });
11374 }
11375 if parts.len() == 2 {
11376 let n = parts.pop().unwrap();
11377 let list = parts.pop().unwrap();
11378 return Ok(Expr {
11379 kind: ExprKind::FuncCall {
11380 name: "chunked".to_string(),
11381 args: vec![list, n],
11382 },
11383 line,
11384 });
11385 }
11386 Err(self.syntax_err(
11387 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
11388 line,
11389 ))
11390 }
11391 "windowed" => {
11392 let mut parts = Vec::new();
11393 if self.eat(&Token::LParen) {
11394 if !matches!(self.peek(), Token::RParen) {
11395 parts.push(self.parse_assign_expr()?);
11396 while self.eat(&Token::Comma) {
11397 if matches!(self.peek(), Token::RParen) {
11398 break;
11399 }
11400 parts.push(self.parse_assign_expr()?);
11401 }
11402 }
11403 self.expect(&Token::RParen)?;
11404 } else {
11405 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11408 loop {
11409 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11410 break;
11411 }
11412 if matches!(
11413 self.peek(),
11414 Token::Semicolon
11415 | Token::RBrace
11416 | Token::RParen
11417 | Token::Eof
11418 | Token::PipeForward
11419 ) {
11420 break;
11421 }
11422 if self.peek_is_postfix_stmt_modifier_keyword() {
11423 break;
11424 }
11425 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11426 }
11427 }
11428 if parts.len() == 1 {
11429 let n = parts.pop().unwrap();
11430 return Ok(Expr {
11431 kind: ExprKind::FuncCall {
11432 name: "windowed".to_string(),
11433 args: vec![n],
11434 },
11435 line,
11436 });
11437 }
11438 if parts.is_empty() {
11439 return Ok(Expr {
11440 kind: ExprKind::FuncCall {
11441 name: "windowed".to_string(),
11442 args: parts,
11443 },
11444 line,
11445 });
11446 }
11447 if parts.len() == 2 {
11448 let n = parts.pop().unwrap();
11449 let list = parts.pop().unwrap();
11450 return Ok(Expr {
11451 kind: ExprKind::FuncCall {
11452 name: "windowed".to_string(),
11453 args: vec![list, n],
11454 },
11455 line,
11456 });
11457 }
11458 Err(self.syntax_err(
11459 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
11460 line,
11461 ))
11462 }
11463 "any" | "all" | "none" => {
11464 if matches!(self.peek(), Token::LParen) {
11466 self.advance();
11467 let args = self.parse_arg_list()?;
11468 self.expect(&Token::RParen)?;
11469 return Ok(Expr {
11470 kind: ExprKind::FuncCall {
11471 name: name.clone(),
11472 args,
11473 },
11474 line,
11475 });
11476 }
11477 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11481 return Ok(Expr {
11482 kind: ExprKind::FuncCall {
11483 name: name.clone(),
11484 args,
11485 },
11486 line,
11487 });
11488 }
11489 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11491 if progress.is_some() {
11492 return Err(self.syntax_err(
11493 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
11494 line,
11495 ));
11496 }
11497 let cr = Expr {
11498 kind: ExprKind::CodeRef {
11499 params: vec![],
11500 body: block,
11501 },
11502 line,
11503 };
11504 Ok(Expr {
11505 kind: ExprKind::FuncCall {
11506 name: name.clone(),
11507 args: vec![cr, list],
11508 },
11509 line,
11510 })
11511 }
11512 "first" | "detect" | "find" => {
11514 if matches!(self.peek(), Token::LParen) {
11516 self.advance();
11517 let args = self.parse_arg_list()?;
11518 self.expect(&Token::RParen)?;
11519 return Ok(Expr {
11520 kind: ExprKind::FuncCall {
11521 name: "first".to_string(),
11522 args,
11523 },
11524 line,
11525 });
11526 }
11527 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11529 return Ok(Expr {
11530 kind: ExprKind::FuncCall {
11531 name: "first".to_string(),
11532 args,
11533 },
11534 line,
11535 });
11536 }
11537 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11539 if progress.is_some() {
11540 return Err(self.syntax_err(
11541 "`progress =>` is not supported for first/detect/find (use pfirst for parallel + progress)",
11542 line,
11543 ));
11544 }
11545 let cr = Expr {
11546 kind: ExprKind::CodeRef {
11547 params: vec![],
11548 body: block,
11549 },
11550 line,
11551 };
11552 Ok(Expr {
11553 kind: ExprKind::FuncCall {
11554 name: "first".to_string(),
11555 args: vec![cr, list],
11556 },
11557 line,
11558 })
11559 }
11560 "take_while" | "drop_while" | "skip_while" | "reject" | "grepv" | "tap" | "peek"
11561 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
11562 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11564 return Ok(Expr {
11565 kind: ExprKind::FuncCall {
11566 name: name.to_string(),
11567 args,
11568 },
11569 line,
11570 });
11571 }
11572 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11573 if progress.is_some() {
11574 return Err(
11575 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
11576 );
11577 }
11578 let cr = Expr {
11579 kind: ExprKind::CodeRef {
11580 params: vec![],
11581 body: block,
11582 },
11583 line,
11584 };
11585 Ok(Expr {
11586 kind: ExprKind::FuncCall {
11587 name: name.to_string(),
11588 args: vec![cr, list],
11589 },
11590 line,
11591 })
11592 }
11593 "group_by" | "chunk_by" => {
11594 if matches!(self.peek(), Token::LBrace) {
11595 let (block, list) = self.parse_block_list()?;
11596 let cr = Expr {
11597 kind: ExprKind::CodeRef {
11598 params: vec![],
11599 body: block,
11600 },
11601 line,
11602 };
11603 Ok(Expr {
11604 kind: ExprKind::FuncCall {
11605 name: name.to_string(),
11606 args: vec![cr, list],
11607 },
11608 line,
11609 })
11610 } else {
11611 let key_expr = self.parse_assign_expr()?;
11612 self.expect(&Token::Comma)?;
11613 let list_parts = self.parse_list_until_terminator()?;
11614 let list_expr = if list_parts.len() == 1 {
11615 list_parts.into_iter().next().unwrap()
11616 } else {
11617 Expr {
11618 kind: ExprKind::List(list_parts),
11619 line,
11620 }
11621 };
11622 Ok(Expr {
11623 kind: ExprKind::FuncCall {
11624 name: name.to_string(),
11625 args: vec![key_expr, list_expr],
11626 },
11627 line,
11628 })
11629 }
11630 }
11631 "with_index" => {
11632 if self.pipe_supplies_slurped_list_operand() {
11633 return Ok(Expr {
11634 kind: ExprKind::FuncCall {
11635 name: "with_index".to_string(),
11636 args: vec![],
11637 },
11638 line,
11639 });
11640 }
11641 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11642 if progress.is_some() {
11643 return Err(
11644 self.syntax_err("`progress =>` is not supported for with_index", line)
11645 );
11646 }
11647 Ok(Expr {
11648 kind: ExprKind::FuncCall {
11649 name: "with_index".to_string(),
11650 args: vec![list],
11651 },
11652 line,
11653 })
11654 }
11655 "pcache" => {
11656 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11657 Ok(Expr {
11658 kind: ExprKind::PcacheExpr {
11659 block,
11660 list: Box::new(list),
11661 progress: progress.map(Box::new),
11662 },
11663 line,
11664 })
11665 }
11666 "pselect" => {
11667 let paren = self.eat(&Token::LParen);
11668 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
11669 if paren {
11670 self.expect(&Token::RParen)?;
11671 }
11672 if receivers.is_empty() {
11673 return Err(self.syntax_err("pselect needs at least one receiver", line));
11674 }
11675 Ok(Expr {
11676 kind: ExprKind::PselectExpr {
11677 receivers,
11678 timeout: timeout.map(Box::new),
11679 },
11680 line,
11681 })
11682 }
11683 "open" => {
11684 let paren = matches!(self.peek(), Token::LParen);
11685 if paren {
11686 self.advance();
11687 }
11688 if matches!(self.peek(), Token::Ident(ref s) if s == "my") {
11689 self.advance();
11690 let name = self.parse_scalar_var_name()?;
11691 self.expect(&Token::Comma)?;
11692 let mode = self.parse_assign_expr()?;
11693 let file = if self.eat(&Token::Comma) {
11694 Some(self.parse_assign_expr()?)
11695 } else {
11696 None
11697 };
11698 if paren {
11699 self.expect(&Token::RParen)?;
11700 }
11701 Ok(Expr {
11702 kind: ExprKind::Open {
11703 handle: Box::new(Expr {
11704 kind: ExprKind::OpenMyHandle { name },
11705 line,
11706 }),
11707 mode: Box::new(mode),
11708 file: file.map(Box::new),
11709 },
11710 line,
11711 })
11712 } else {
11713 let args = if paren {
11714 self.parse_arg_list()?
11715 } else {
11716 self.parse_list_until_terminator()?
11717 };
11718 if paren {
11719 self.expect(&Token::RParen)?;
11720 }
11721 if args.len() < 2 {
11722 return Err(self.syntax_err("open requires at least 2 arguments", line));
11723 }
11724 Ok(Expr {
11725 kind: ExprKind::Open {
11726 handle: Box::new(args[0].clone()),
11727 mode: Box::new(args[1].clone()),
11728 file: args.get(2).cloned().map(Box::new),
11729 },
11730 line,
11731 })
11732 }
11733 }
11734 "close" => {
11735 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11736 return Ok(e);
11737 }
11738 let a = self.parse_one_arg_or_default()?;
11739 Ok(Expr {
11740 kind: ExprKind::Close(Box::new(a)),
11741 line,
11742 })
11743 }
11744 "opendir" => {
11745 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11746 return Ok(e);
11747 }
11748 let args = self.parse_builtin_args()?;
11749 if args.len() != 2 {
11750 return Err(self.syntax_err("opendir requires two arguments", line));
11751 }
11752 Ok(Expr {
11753 kind: ExprKind::Opendir {
11754 handle: Box::new(args[0].clone()),
11755 path: Box::new(args[1].clone()),
11756 },
11757 line,
11758 })
11759 }
11760 "readdir" => {
11761 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11762 return Ok(e);
11763 }
11764 let a = self.parse_one_arg()?;
11765 Ok(Expr {
11766 kind: ExprKind::Readdir(Box::new(a)),
11767 line,
11768 })
11769 }
11770 "closedir" => {
11771 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11772 return Ok(e);
11773 }
11774 let a = self.parse_one_arg()?;
11775 Ok(Expr {
11776 kind: ExprKind::Closedir(Box::new(a)),
11777 line,
11778 })
11779 }
11780 "rewinddir" => {
11781 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11782 return Ok(e);
11783 }
11784 let a = self.parse_one_arg()?;
11785 Ok(Expr {
11786 kind: ExprKind::Rewinddir(Box::new(a)),
11787 line,
11788 })
11789 }
11790 "telldir" => {
11791 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11792 return Ok(e);
11793 }
11794 let a = self.parse_one_arg()?;
11795 Ok(Expr {
11796 kind: ExprKind::Telldir(Box::new(a)),
11797 line,
11798 })
11799 }
11800 "seekdir" => {
11801 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11802 return Ok(e);
11803 }
11804 let args = self.parse_builtin_args()?;
11805 if args.len() != 2 {
11806 return Err(self.syntax_err("seekdir requires two arguments", line));
11807 }
11808 Ok(Expr {
11809 kind: ExprKind::Seekdir {
11810 handle: Box::new(args[0].clone()),
11811 position: Box::new(args[1].clone()),
11812 },
11813 line,
11814 })
11815 }
11816 "eof" => {
11817 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11818 return Ok(e);
11819 }
11820 if matches!(self.peek(), Token::LParen) {
11821 self.advance();
11822 if matches!(self.peek(), Token::RParen) {
11823 self.advance();
11824 Ok(Expr {
11825 kind: ExprKind::Eof(None),
11826 line,
11827 })
11828 } else {
11829 let a = self.parse_expression()?;
11830 self.expect(&Token::RParen)?;
11831 Ok(Expr {
11832 kind: ExprKind::Eof(Some(Box::new(a))),
11833 line,
11834 })
11835 }
11836 } else {
11837 Ok(Expr {
11838 kind: ExprKind::Eof(None),
11839 line,
11840 })
11841 }
11842 }
11843 "system" => {
11844 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11845 return Ok(e);
11846 }
11847 let args = self.parse_builtin_args()?;
11848 Ok(Expr {
11849 kind: ExprKind::System(args),
11850 line,
11851 })
11852 }
11853 "exec" => {
11854 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11855 return Ok(e);
11856 }
11857 let args = self.parse_builtin_args()?;
11858 Ok(Expr {
11859 kind: ExprKind::Exec(args),
11860 line,
11861 })
11862 }
11863 "eval" => {
11864 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11865 return Ok(e);
11866 }
11867 let a = if matches!(self.peek(), Token::LBrace) {
11868 let block = self.parse_block()?;
11869 Expr {
11870 kind: ExprKind::CodeRef {
11871 params: vec![],
11872 body: block,
11873 },
11874 line,
11875 }
11876 } else {
11877 self.parse_one_arg_or_default()?
11878 };
11879 Ok(Expr {
11880 kind: ExprKind::Eval(Box::new(a)),
11881 line,
11882 })
11883 }
11884 "do" => {
11885 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11886 return Ok(e);
11887 }
11888 let a = self.parse_one_arg()?;
11889 Ok(Expr {
11890 kind: ExprKind::Do(Box::new(a)),
11891 line,
11892 })
11893 }
11894 "require" => {
11895 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11896 return Ok(e);
11897 }
11898 let a = self.parse_one_arg()?;
11899 Ok(Expr {
11900 kind: ExprKind::Require(Box::new(a)),
11901 line,
11902 })
11903 }
11904 "exit" => {
11905 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11906 return Ok(e);
11907 }
11908 if matches!(
11909 self.peek(),
11910 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
11911 ) {
11912 Ok(Expr {
11913 kind: ExprKind::Exit(None),
11914 line,
11915 })
11916 } else {
11917 let a = self.parse_one_arg()?;
11918 Ok(Expr {
11919 kind: ExprKind::Exit(Some(Box::new(a))),
11920 line,
11921 })
11922 }
11923 }
11924 "chdir" => {
11925 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11926 return Ok(e);
11927 }
11928 let a = self.parse_one_arg_or_default()?;
11929 Ok(Expr {
11930 kind: ExprKind::Chdir(Box::new(a)),
11931 line,
11932 })
11933 }
11934 "mkdir" => {
11935 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11936 return Ok(e);
11937 }
11938 let args = self.parse_builtin_args()?;
11939 Ok(Expr {
11940 kind: ExprKind::Mkdir {
11941 path: Box::new(args[0].clone()),
11942 mode: args.get(1).cloned().map(Box::new),
11943 },
11944 line,
11945 })
11946 }
11947 "unlink" | "rm" => {
11948 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11949 return Ok(e);
11950 }
11951 let args = self.parse_builtin_args()?;
11952 Ok(Expr {
11953 kind: ExprKind::Unlink(args),
11954 line,
11955 })
11956 }
11957 "rename" => {
11958 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11959 return Ok(e);
11960 }
11961 let args = self.parse_builtin_args()?;
11962 if args.len() != 2 {
11963 return Err(self.syntax_err("rename requires two arguments", line));
11964 }
11965 Ok(Expr {
11966 kind: ExprKind::Rename {
11967 old: Box::new(args[0].clone()),
11968 new: Box::new(args[1].clone()),
11969 },
11970 line,
11971 })
11972 }
11973 "chmod" => {
11974 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11975 return Ok(e);
11976 }
11977 let args = self.parse_builtin_args()?;
11978 if args.len() < 2 {
11979 return Err(self.syntax_err("chmod requires mode and at least one file", line));
11980 }
11981 Ok(Expr {
11982 kind: ExprKind::Chmod(args),
11983 line,
11984 })
11985 }
11986 "chown" => {
11987 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11988 return Ok(e);
11989 }
11990 let args = self.parse_builtin_args()?;
11991 if args.len() < 3 {
11992 return Err(
11993 self.syntax_err("chown requires uid, gid, and at least one file", line)
11994 );
11995 }
11996 Ok(Expr {
11997 kind: ExprKind::Chown(args),
11998 line,
11999 })
12000 }
12001 "stat" => {
12002 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12003 return Ok(e);
12004 }
12005 let args = self.parse_builtin_args()?;
12006 let arg = if args.len() == 1 {
12007 args[0].clone()
12008 } else if args.is_empty() {
12009 Expr {
12010 kind: ExprKind::ScalarVar("_".into()),
12011 line,
12012 }
12013 } else {
12014 return Err(self.syntax_err("stat requires zero or one argument", line));
12015 };
12016 Ok(Expr {
12017 kind: ExprKind::Stat(Box::new(arg)),
12018 line,
12019 })
12020 }
12021 "lstat" => {
12022 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12023 return Ok(e);
12024 }
12025 let args = self.parse_builtin_args()?;
12026 let arg = if args.len() == 1 {
12027 args[0].clone()
12028 } else if args.is_empty() {
12029 Expr {
12030 kind: ExprKind::ScalarVar("_".into()),
12031 line,
12032 }
12033 } else {
12034 return Err(self.syntax_err("lstat requires zero or one argument", line));
12035 };
12036 Ok(Expr {
12037 kind: ExprKind::Lstat(Box::new(arg)),
12038 line,
12039 })
12040 }
12041 "link" => {
12042 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12043 return Ok(e);
12044 }
12045 let args = self.parse_builtin_args()?;
12046 if args.len() != 2 {
12047 return Err(self.syntax_err("link requires two arguments", line));
12048 }
12049 Ok(Expr {
12050 kind: ExprKind::Link {
12051 old: Box::new(args[0].clone()),
12052 new: Box::new(args[1].clone()),
12053 },
12054 line,
12055 })
12056 }
12057 "symlink" => {
12058 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12059 return Ok(e);
12060 }
12061 let args = self.parse_builtin_args()?;
12062 if args.len() != 2 {
12063 return Err(self.syntax_err("symlink requires two arguments", line));
12064 }
12065 Ok(Expr {
12066 kind: ExprKind::Symlink {
12067 old: Box::new(args[0].clone()),
12068 new: Box::new(args[1].clone()),
12069 },
12070 line,
12071 })
12072 }
12073 "readlink" => {
12074 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12075 return Ok(e);
12076 }
12077 let args = self.parse_builtin_args()?;
12078 let arg = if args.len() == 1 {
12079 args[0].clone()
12080 } else if args.is_empty() {
12081 Expr {
12082 kind: ExprKind::ScalarVar("_".into()),
12083 line,
12084 }
12085 } else {
12086 return Err(self.syntax_err("readlink requires zero or one argument", line));
12087 };
12088 Ok(Expr {
12089 kind: ExprKind::Readlink(Box::new(arg)),
12090 line,
12091 })
12092 }
12093 "files" => {
12094 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12095 return Ok(e);
12096 }
12097 let args = self.parse_builtin_args()?;
12098 Ok(Expr {
12099 kind: ExprKind::Files(args),
12100 line,
12101 })
12102 }
12103 "filesf" | "f" => {
12104 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12105 return Ok(e);
12106 }
12107 let args = self.parse_builtin_args()?;
12108 Ok(Expr {
12109 kind: ExprKind::Filesf(args),
12110 line,
12111 })
12112 }
12113 "fr" => {
12114 let args = self.parse_builtin_args()?;
12115 Ok(Expr {
12116 kind: ExprKind::FilesfRecursive(args),
12117 line,
12118 })
12119 }
12120 "dirs" | "d" => {
12121 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12122 return Ok(e);
12123 }
12124 let args = self.parse_builtin_args()?;
12125 Ok(Expr {
12126 kind: ExprKind::Dirs(args),
12127 line,
12128 })
12129 }
12130 "dr" => {
12131 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12132 return Ok(e);
12133 }
12134 let args = self.parse_builtin_args()?;
12135 Ok(Expr {
12136 kind: ExprKind::DirsRecursive(args),
12137 line,
12138 })
12139 }
12140 "sym_links" => {
12141 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12142 return Ok(e);
12143 }
12144 let args = self.parse_builtin_args()?;
12145 Ok(Expr {
12146 kind: ExprKind::SymLinks(args),
12147 line,
12148 })
12149 }
12150 "sockets" => {
12151 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12152 return Ok(e);
12153 }
12154 let args = self.parse_builtin_args()?;
12155 Ok(Expr {
12156 kind: ExprKind::Sockets(args),
12157 line,
12158 })
12159 }
12160 "pipes" => {
12161 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12162 return Ok(e);
12163 }
12164 let args = self.parse_builtin_args()?;
12165 Ok(Expr {
12166 kind: ExprKind::Pipes(args),
12167 line,
12168 })
12169 }
12170 "block_devices" => {
12171 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12172 return Ok(e);
12173 }
12174 let args = self.parse_builtin_args()?;
12175 Ok(Expr {
12176 kind: ExprKind::BlockDevices(args),
12177 line,
12178 })
12179 }
12180 "char_devices" => {
12181 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12182 return Ok(e);
12183 }
12184 let args = self.parse_builtin_args()?;
12185 Ok(Expr {
12186 kind: ExprKind::CharDevices(args),
12187 line,
12188 })
12189 }
12190 "exe" | "executables" => {
12191 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12192 return Ok(e);
12193 }
12194 let args = self.parse_builtin_args()?;
12195 Ok(Expr {
12196 kind: ExprKind::Executables(args),
12197 line,
12198 })
12199 }
12200 "glob" => {
12201 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12202 return Ok(e);
12203 }
12204 let args = self.parse_builtin_args()?;
12205 Ok(Expr {
12206 kind: ExprKind::Glob(args),
12207 line,
12208 })
12209 }
12210 "glob_par" => {
12211 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12212 return Ok(e);
12213 }
12214 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12215 Ok(Expr {
12216 kind: ExprKind::GlobPar { args, progress },
12217 line,
12218 })
12219 }
12220 "par_sed" => {
12221 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12222 return Ok(e);
12223 }
12224 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12225 Ok(Expr {
12226 kind: ExprKind::ParSed { args, progress },
12227 line,
12228 })
12229 }
12230 "bless" => {
12231 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12232 return Ok(e);
12233 }
12234 let args = self.parse_builtin_args()?;
12235 Ok(Expr {
12236 kind: ExprKind::Bless {
12237 ref_expr: Box::new(args[0].clone()),
12238 class: args.get(1).cloned().map(Box::new),
12239 },
12240 line,
12241 })
12242 }
12243 "caller" => {
12244 if matches!(self.peek(), Token::LParen) {
12245 self.advance();
12246 if matches!(self.peek(), Token::RParen) {
12247 self.advance();
12248 Ok(Expr {
12249 kind: ExprKind::Caller(None),
12250 line,
12251 })
12252 } else {
12253 let a = self.parse_expression()?;
12254 self.expect(&Token::RParen)?;
12255 Ok(Expr {
12256 kind: ExprKind::Caller(Some(Box::new(a))),
12257 line,
12258 })
12259 }
12260 } else {
12261 Ok(Expr {
12262 kind: ExprKind::Caller(None),
12263 line,
12264 })
12265 }
12266 }
12267 "wantarray" => {
12268 if matches!(self.peek(), Token::LParen) {
12269 self.advance();
12270 self.expect(&Token::RParen)?;
12271 }
12272 Ok(Expr {
12273 kind: ExprKind::Wantarray,
12274 line,
12275 })
12276 }
12277 "sub" => {
12278 if crate::no_interop_mode() {
12280 return Err(self.syntax_err(
12281 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
12282 line,
12283 ));
12284 }
12285 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
12287 let body = self.parse_block()?;
12288 Ok(Expr {
12289 kind: ExprKind::CodeRef { params, body },
12290 line,
12291 })
12292 }
12293 "fn" => {
12294 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
12296 self.parse_sub_attributes()?;
12297 let body = self.parse_fn_eq_body_or_block(false)?;
12298 Ok(Expr {
12299 kind: ExprKind::CodeRef { params, body },
12300 line,
12301 })
12302 }
12303 _ => {
12304 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name)
12309 {
12310 return Ok(Expr {
12311 kind: ExprKind::String(name),
12312 line,
12313 });
12314 }
12315 if Self::is_underscore_topic_slot(&name) {
12331 if matches!(self.peek(), Token::LBracket) && self.peek_line() == line {
12332 self.advance(); let index = self.parse_expression()?;
12334 self.expect(&Token::RBracket)?;
12335 return Ok(Expr {
12336 kind: ExprKind::ArrayElement {
12337 array: format!("__topicstr__{}", name),
12338 index: Box::new(index),
12339 },
12340 line,
12341 });
12342 }
12343 return Ok(Expr {
12344 kind: ExprKind::ScalarVar(name.clone()),
12345 line,
12346 });
12347 }
12348 if matches!(self.peek(), Token::LParen) {
12350 self.advance();
12351 let args = self.parse_arg_list()?;
12352 self.expect(&Token::RParen)?;
12353 Ok(Expr {
12354 kind: ExprKind::FuncCall { name, args },
12355 line,
12356 })
12357 } else if self.peek().is_term_start()
12358 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
12359 && matches!(self.peek_at(1), Token::Ident(_)))
12360 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
12361 && !(matches!(self.peek(), Token::LBrace)
12362 && self.peek_line() > self.prev_line())
12363 && !(matches!(self.peek(), Token::BitNot)
12364 && self.suppress_tilde_range == 0
12365 && matches!(
12366 self.peek_at(1),
12367 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
12368 ))
12369 {
12370 let args = self.parse_list_until_terminator()?;
12384 Ok(Expr {
12385 kind: ExprKind::FuncCall { name, args },
12386 line,
12387 })
12388 } else {
12389 Ok(Expr {
12395 kind: ExprKind::Bareword(name),
12396 line,
12397 })
12398 }
12399 }
12400 }
12401 }
12402
12403 fn parse_print_like(
12404 &mut self,
12405 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
12406 ) -> PerlResult<Expr> {
12407 let line = self.peek_line();
12408 let handle = if let Token::Ident(ref h) = self.peek().clone() {
12410 if h.chars().all(|c| c.is_uppercase() || c == '_')
12411 && !matches!(self.peek(), Token::LParen)
12412 {
12413 let h = h.clone();
12414 let saved = self.pos;
12415 self.advance();
12416 let is_tilde_range_after = matches!(self.peek(), Token::BitNot)
12423 && self.suppress_tilde_range == 0
12424 && matches!(
12425 self.peek_at(1),
12426 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
12427 );
12428 if !is_tilde_range_after
12429 && (self.peek().is_term_start()
12430 || matches!(
12431 self.peek(),
12432 Token::DoubleString(_)
12433 | Token::BacktickString(_)
12434 | Token::SingleString(_)
12435 ))
12436 {
12437 Some(h)
12438 } else {
12439 self.pos = saved;
12440 None
12441 }
12442 } else {
12443 None
12444 }
12445 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
12446 let v = v.clone();
12456 if v == "_" {
12457 None
12458 } else {
12459 let saved = self.pos;
12460 self.advance();
12461 let next = self.peek().clone();
12462 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
12463 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
12464 if !is_stmt_modifier
12465 && !matches!(next, Token::LBracket | Token::LBrace)
12466 && (next.is_term_start()
12467 || matches!(
12468 next,
12469 Token::DoubleString(_)
12470 | Token::BacktickString(_)
12471 | Token::SingleString(_)
12472 ))
12473 {
12474 Some(format!("${v}"))
12476 } else {
12477 self.pos = saved;
12478 None
12479 }
12480 }
12481 } else {
12482 None
12483 };
12484 let args =
12489 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
12490 let line_topic = self.peek_line();
12491 self.advance(); self.advance(); vec![Expr {
12494 kind: ExprKind::ScalarVar("_".into()),
12495 line: line_topic,
12496 }]
12497 } else {
12498 self.parse_list_until_terminator()?
12499 };
12500 Ok(Expr {
12501 kind: make(handle, args),
12502 line,
12503 })
12504 }
12505
12506 fn parse_block_list(&mut self) -> PerlResult<(Block, Expr)> {
12507 let block = self.parse_block()?;
12508 let block_end_line = self.prev_line();
12509 self.eat(&Token::Comma);
12510 if self.in_pipe_rhs()
12514 && (matches!(
12515 self.peek(),
12516 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12517 ) || self.peek_line() > block_end_line)
12518 {
12519 let line = self.peek_line();
12520 return Ok((block, self.pipe_placeholder_list(line)));
12521 }
12522 let list = self.parse_expression()?;
12523 Ok((block, list))
12524 }
12525
12526 fn parse_comma_expr_list_with_timeout_tail(
12529 &mut self,
12530 paren: bool,
12531 ) -> PerlResult<(Vec<Expr>, Option<Expr>)> {
12532 let mut parts = vec![self.parse_assign_expr()?];
12533 loop {
12534 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12535 break;
12536 }
12537 if paren && matches!(self.peek(), Token::RParen) {
12538 break;
12539 }
12540 if matches!(
12541 self.peek(),
12542 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12543 ) {
12544 break;
12545 }
12546 if self.peek_is_postfix_stmt_modifier_keyword() {
12547 break;
12548 }
12549 if let Token::Ident(ref kw) = self.peek().clone() {
12550 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
12551 self.advance();
12552 self.expect(&Token::FatArrow)?;
12553 let t = self.parse_assign_expr()?;
12554 return Ok((parts, Some(t)));
12555 }
12556 }
12557 parts.push(self.parse_assign_expr()?);
12558 }
12559 Ok((parts, None))
12560 }
12561
12562 fn parse_init_block_then_list_optional_progress(
12564 &mut self,
12565 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
12566 let init = self.parse_assign_expr()?;
12567 self.expect(&Token::Comma)?;
12568 let block = self.parse_block_or_bareword_block()?;
12569 self.eat(&Token::Comma);
12570 let line = self.peek_line();
12571 if let Token::Ident(ref kw) = self.peek().clone() {
12572 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12573 self.advance();
12574 self.expect(&Token::FatArrow)?;
12575 let prog = self.parse_assign_expr()?;
12576 return Ok((
12577 init,
12578 block,
12579 Expr {
12580 kind: ExprKind::List(vec![]),
12581 line,
12582 },
12583 Some(prog),
12584 ));
12585 }
12586 }
12587 if matches!(
12588 self.peek(),
12589 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12590 ) {
12591 return Ok((
12592 init,
12593 block,
12594 Expr {
12595 kind: ExprKind::List(vec![]),
12596 line,
12597 },
12598 None,
12599 ));
12600 }
12601 let mut parts = vec![self.parse_assign_expr()?];
12602 loop {
12603 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12604 break;
12605 }
12606 if matches!(
12607 self.peek(),
12608 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12609 ) {
12610 break;
12611 }
12612 if self.peek_is_postfix_stmt_modifier_keyword() {
12613 break;
12614 }
12615 if let Token::Ident(ref kw) = self.peek().clone() {
12616 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12617 self.advance();
12618 self.expect(&Token::FatArrow)?;
12619 let prog = self.parse_assign_expr()?;
12620 return Ok((init, block, merge_expr_list(parts), Some(prog)));
12621 }
12622 }
12623 parts.push(self.parse_assign_expr()?);
12624 }
12625 Ok((init, block, merge_expr_list(parts), None))
12626 }
12627
12628 fn parse_cluster_block_then_list_optional_progress(
12630 &mut self,
12631 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
12632 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
12635 let cluster = self.parse_assign_expr();
12636 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
12637 let cluster = cluster?;
12638 self.eat(&Token::Comma);
12640 let block = self.parse_block_or_bareword_block()?;
12641 let block_end_line = self.prev_line();
12642 self.eat(&Token::Comma);
12643 let line = self.peek_line();
12644 if let Token::Ident(ref kw) = self.peek().clone() {
12645 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12646 self.advance();
12647 self.expect(&Token::FatArrow)?;
12648 let prog = self.parse_assign_expr_stop_at_pipe()?;
12649 return Ok((
12650 cluster,
12651 block,
12652 Expr {
12653 kind: ExprKind::List(vec![]),
12654 line,
12655 },
12656 Some(prog),
12657 ));
12658 }
12659 }
12660 let empty_list_ok = matches!(
12661 self.peek(),
12662 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12663 ) || (self.in_pipe_rhs()
12664 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
12665 if empty_list_ok {
12666 return Ok((
12667 cluster,
12668 block,
12669 Expr {
12670 kind: ExprKind::List(vec![]),
12671 line,
12672 },
12673 None,
12674 ));
12675 }
12676 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
12677 loop {
12678 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12679 break;
12680 }
12681 if matches!(
12682 self.peek(),
12683 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12684 ) {
12685 break;
12686 }
12687 if self.peek_is_postfix_stmt_modifier_keyword() {
12688 break;
12689 }
12690 if let Token::Ident(ref kw) = self.peek().clone() {
12691 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12692 self.advance();
12693 self.expect(&Token::FatArrow)?;
12694 let prog = self.parse_assign_expr_stop_at_pipe()?;
12695 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
12696 }
12697 }
12698 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12699 }
12700 Ok((cluster, block, merge_expr_list(parts), None))
12701 }
12702
12703 fn parse_block_then_list_optional_progress(
12712 &mut self,
12713 ) -> PerlResult<(Block, Expr, Option<Expr>)> {
12714 let block = self.parse_block_or_bareword_block()?;
12715 let block_end_line = self.prev_line();
12716 self.eat(&Token::Comma);
12717 let line = self.peek_line();
12718 if let Token::Ident(ref kw) = self.peek().clone() {
12719 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12720 self.advance();
12721 self.expect(&Token::FatArrow)?;
12722 let prog = self.parse_assign_expr_stop_at_pipe()?;
12723 return Ok((
12724 block,
12725 Expr {
12726 kind: ExprKind::List(vec![]),
12727 line,
12728 },
12729 Some(prog),
12730 ));
12731 }
12732 }
12733 let empty_list_ok = matches!(
12741 self.peek(),
12742 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12743 ) || (self.in_pipe_rhs()
12744 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
12745 if empty_list_ok {
12746 return Ok((
12747 block,
12748 Expr {
12749 kind: ExprKind::List(vec![]),
12750 line,
12751 },
12752 None,
12753 ));
12754 }
12755 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
12756 loop {
12757 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12758 break;
12759 }
12760 if matches!(
12761 self.peek(),
12762 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12763 ) {
12764 break;
12765 }
12766 if self.peek_is_postfix_stmt_modifier_keyword() {
12767 break;
12768 }
12769 if let Token::Ident(ref kw) = self.peek().clone() {
12770 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12771 self.advance();
12772 self.expect(&Token::FatArrow)?;
12773 let prog = self.parse_assign_expr_stop_at_pipe()?;
12774 return Ok((block, merge_expr_list(parts), Some(prog)));
12775 }
12776 }
12777 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12778 }
12779 Ok((block, merge_expr_list(parts), None))
12780 }
12781
12782 fn parse_fan_count_and_block(&mut self, line: usize) -> PerlResult<(Option<Box<Expr>>, Block)> {
12784 if matches!(self.peek(), Token::LBrace) {
12786 let block = self.parse_block()?;
12787 return Ok((None, block));
12788 }
12789 let saved = self.pos;
12790 let first = self.parse_postfix()?;
12792 if matches!(self.peek(), Token::LBrace) {
12793 let block = self.parse_block()?;
12795 Ok((Some(Box::new(first)), block))
12796 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
12797 || (matches!(self.peek(), Token::Comma)
12798 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
12799 {
12800 let block = self.bareword_to_no_arg_block(first);
12802 Ok((None, block))
12803 } else if matches!(first.kind, ExprKind::Integer(_)) {
12804 self.eat(&Token::Comma);
12806 let body = self.parse_fan_blockless_body(line)?;
12807 Ok((Some(Box::new(first)), body))
12808 } else {
12809 self.pos = saved;
12812 let body = self.parse_fan_blockless_body(line)?;
12813 Ok((None, body))
12814 }
12815 }
12816
12817 fn parse_fan_blockless_body(&mut self, line: usize) -> PerlResult<Block> {
12819 if matches!(self.peek(), Token::LBrace) {
12820 return self.parse_block();
12821 }
12822 if let Token::Ident(ref name) = self.peek().clone() {
12824 if matches!(
12825 self.peek_at(1),
12826 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
12827 ) {
12828 let name = name.clone();
12829 self.advance();
12830 let body = Expr {
12831 kind: ExprKind::FuncCall { name, args: vec![] },
12832 line,
12833 };
12834 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
12835 }
12836 }
12837 let expr = self.parse_assign_expr_stop_at_pipe()?;
12839 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
12840 }
12841
12842 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
12845 let line = expr.line;
12846 let body = match &expr.kind {
12847 ExprKind::Bareword(name) => Expr {
12848 kind: ExprKind::FuncCall {
12849 name: name.clone(),
12850 args: vec![],
12851 },
12852 line,
12853 },
12854 _ => expr,
12855 };
12856 vec![Statement::new(StmtKind::Expression(body), line)]
12857 }
12858
12859 fn parse_block_or_bareword_block(&mut self) -> PerlResult<Block> {
12868 if matches!(self.peek(), Token::LBrace) {
12869 return self.parse_block();
12870 }
12871 let line = self.peek_line();
12872 if let Token::Ident(ref name) = self.peek().clone() {
12875 if matches!(
12876 self.peek_at(1),
12877 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
12878 ) {
12879 let name = name.clone();
12880 self.advance();
12881 let body = Expr {
12882 kind: ExprKind::FuncCall {
12883 name,
12884 args: vec![Expr {
12885 kind: ExprKind::ScalarVar("_".to_string()),
12886 line,
12887 }],
12888 },
12889 line,
12890 };
12891 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
12892 }
12893 }
12894 let expr = self.parse_assign_expr_stop_at_pipe()?;
12896 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
12897 }
12898
12899 fn parse_block_or_bareword_block_no_args(&mut self) -> PerlResult<Block> {
12904 if matches!(self.peek(), Token::LBrace) {
12905 return self.parse_block();
12906 }
12907 let line = self.peek_line();
12908 if let Token::Ident(ref name) = self.peek().clone() {
12909 if matches!(
12910 self.peek_at(1),
12911 Token::Comma
12912 | Token::Semicolon
12913 | Token::RBrace
12914 | Token::Eof
12915 | Token::PipeForward
12916 | Token::Integer(_)
12917 ) {
12918 let name = name.clone();
12919 self.advance();
12920 let body = Expr {
12921 kind: ExprKind::FuncCall { name, args: vec![] },
12922 line,
12923 };
12924 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
12925 }
12926 }
12927 let expr = self.parse_postfix()?;
12928 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
12929 }
12930
12931 fn is_known_bareword(name: &str) -> bool {
12939 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
12940 }
12941
12942 fn is_try_builtin_name(name: &str) -> bool {
12948 crate::builtins::BUILTIN_ARMS
12949 .iter()
12950 .any(|arm| arm.contains(&name))
12951 }
12952
12953 fn is_perl5_core(name: &str) -> bool {
12958 matches!(
12959 name,
12960 "map" | "grep" | "sort" | "reverse" | "join" | "split"
12962 | "push" | "pop" | "shift" | "unshift" | "splice"
12963 | "splice_last" | "splice1" | "spl_last"
12964 | "pack" | "unpack"
12965 | "unpack_first" | "unpack1" | "up1"
12966 | "keys" | "values" | "each"
12968 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
12970 | "lc" | "uc" | "lcfirst" | "ucfirst"
12971 | "length" | "substr" | "index" | "rindex"
12972 | "sprintf" | "printf" | "print" | "say"
12973 | "pos" | "quotemeta" | "study"
12974 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
12976 | "exp" | "log" | "rand" | "srand"
12977 | "time" | "localtime" | "gmtime"
12979 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
12981 | "caller" | "delete" | "exists" | "bless" | "prototype"
12982 | "tie" | "untie" | "tied"
12983 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
12985 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
12986 | "format" | "formline" | "select" | "vec"
12987 | "sysopen" | "sysread" | "sysseek" | "syswrite"
12988 | "stat" | "lstat" | "rename" | "unlink" | "utime"
12990 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
12991 | "glob" | "opendir" | "readdir" | "closedir"
12992 | "link" | "readlink" | "symlink"
12993 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
12995 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
12997 | "semctl" | "semget" | "semop"
12998 | "shmctl" | "shmget" | "shmread" | "shmwrite"
12999 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
13001 | "fork" | "wait" | "waitpid" | "kill" | "alarm" | "sleep"
13002 | "chroot" | "times" | "umask" | "reset"
13003 | "getpgrp" | "setpgrp" | "getppid"
13004 | "getpriority" | "setpriority"
13005 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
13007 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
13008 | "getpeername" | "getsockname"
13009 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
13011 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
13012 | "getlogin"
13013 | "gethostbyname" | "gethostbyaddr" | "gethostent"
13014 | "getnetbyname" | "getnetent"
13015 | "getprotobyname" | "getprotoent"
13016 | "getservbyname" | "getservent"
13017 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
13018 | "endpwent" | "endgrent"
13019 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
13020 | "return" | "do" | "eval" | "require"
13022 | "my" | "our" | "local" | "use" | "no"
13023 | "sub" | "if" | "unless" | "while" | "until"
13024 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
13025 | "not" | "and" | "or"
13026 | "qw" | "qq" | "q"
13028 | "BEGIN" | "END"
13030 )
13031 }
13032
13033 fn stryke_extension_name(name: &str) -> Option<&str> {
13036 match name {
13037 | "proceed" | "intercept_list" | "intercept_remove" | "intercept_clear"
13039 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
13041 | "pgrep" | "pfor" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
13042 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
13043 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
13044 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
13045 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
13046 | "pmaps" | "pflat_maps" | "pgreps"
13047 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "fi" | "find_all" | "reduce" | "fold"
13049 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
13050 | "first" | "detect" | "find" | "compact" | "concat" | "chain" | "reject" | "grepv" | "flatten" | "set"
13051 | "min_by" | "max_by" | "sort_by" | "tally" | "find_index"
13052 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
13053 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
13054 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
13055 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
13056 | "zip_with" | "count_by" | "skip" | "first_or"
13057 | "input" | "lines" | "words" | "chars" | "digits" | "letters" | "letters_uc" | "letters_lc"
13059 | "punctuation" | "punct"
13060 | "sentences" | "sents"
13061 | "paragraphs" | "paras" | "sections" | "sects"
13062 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
13063 | "trim" | "avg" | "stddev"
13064 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
13065 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
13066 | "frequencies" | "freq" | "interleave" | "ddump" | "stringify" | "str" | "top"
13067 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
13068 | "to_html" | "to_markdown" | "to_table" | "xopen"
13069 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
13070 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste" | "preview"
13071 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
13072 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
13073 | "to_hash" | "to_set"
13074 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
13075 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
13076 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
13077 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
13078 | "inc" | "dec" | "elapsed"
13079 | "files" | "filesf" | "f" | "fr" | "dirs" | "d" | "dr" | "sym_links"
13081 | "sockets" | "pipes" | "block_devices" | "char_devices" | "exe" | "executables"
13082 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
13083 | "copy" | "move" | "spurt" | "spit" | "read_bytes" | "which"
13084 | "getcwd" | "touch" | "gethostname" | "uname"
13085 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
13087 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
13088 | "par_fetch" | "par_csv_read" | "par_pipeline"
13089 | "json_encode" | "json_decode" | "json_jq"
13090 | "http_request" | "serve" | "ssh"
13091 | "html_parse" | "css_select" | "xml_parse" | "xpath"
13092 | "smtp_send"
13093 | "net_interfaces" | "net_ipv4" | "net_ipv6" | "net_mac"
13094 | "net_public_ip" | "net_dns" | "net_reverse_dns"
13095 | "net_ping" | "net_port_open" | "net_ports_scan"
13096 | "net_latency" | "net_download" | "net_headers"
13097 | "net_dns_servers" | "net_gateway" | "net_whois" | "net_hostname"
13098 | "git_log" | "git_status" | "git_diff" | "git_branches"
13100 | "git_tags" | "git_blame" | "git_authors" | "git_files"
13101 | "git_show" | "git_root"
13102 | "audio_convert" | "audio_info" | "id3_read" | "id3_write"
13104 | "to_pdf" | "pdf_text" | "pdf_pages"
13106 | "toml_encode" | "toml_decode"
13108 | "yaml_encode" | "yaml_decode"
13109 | "xml_encode" | "xml_decode"
13110 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
13112 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
13113 | "shake128" | "shake256"
13114 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
13115 | "uuid" | "crc32"
13116 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
13117 | "ripemd160" | "rmd160" | "md4"
13118 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
13119 | "murmur3" | "murmur3_32" | "murmur3_128"
13120 | "siphash" | "siphash_keyed"
13121 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
13122 | "poly1305" | "poly1305_mac"
13123 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
13124 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
13125 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
13126 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
13127 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
13128 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
13129 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
13130 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
13131 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
13132 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
13133 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
13134 | "secretbox" | "secretbox_seal" | "secretbox_open"
13135 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
13136 | "nacl_box_open" | "box_open"
13137 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
13138 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
13139 | "barcode_ean13" | "ean13" | "barcode_svg"
13140 | "argon2_hash" | "argon2" | "argon2_verify"
13141 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
13142 | "scrypt_hash" | "scrypt" | "scrypt_verify"
13143 | "pbkdf2" | "pbkdf2_derive"
13144 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
13145 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
13146 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
13147 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
13148 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
13149 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
13150 | "ecdsa_p256_verify" | "p256_verify"
13151 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
13152 | "ecdsa_p384_verify" | "p384_verify"
13153 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
13154 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
13155 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
13156 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
13157 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
13158 | "ed25519_verify" | "ed_verify"
13159 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
13160 | "base64_encode" | "base64_decode"
13161 | "hex_encode" | "hex_decode"
13162 | "url_encode" | "url_decode"
13163 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
13164 | "brotli" | "br" | "brotli_decode" | "ubr"
13165 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
13166 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
13167 | "lz4" | "lz4_decode" | "unlz4"
13168 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
13169 | "lzw" | "lzw_decode" | "unlzw"
13170 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
13171 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
13172 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
13173 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
13175 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
13176 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
13177 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
13178 | "gammaincc_reg" | "gamma_ur"
13179 | "datetime_utc" | "datetime_now_tz"
13181 | "datetime_format_tz" | "datetime_add_seconds"
13182 | "datetime_from_epoch"
13183 | "datetime_parse_rfc3339" | "datetime_parse_local"
13184 | "datetime_strftime"
13185 | "dateseq" | "dategrep" | "dateround" | "datesort"
13186 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
13188 | "log_info" | "log_warn" | "log_error"
13190 | "log_debug" | "log_trace" | "log_json" | "log_level"
13191 | "async" | "spawn" | "trace" | "timer" | "bench"
13193 | "eval_timeout" | "retry" | "rate_limit" | "every"
13194 | "gen" | "watch"
13195 | "cache_clear" | "cache_exists" | "cache_stats" | "cacheview"
13197 | "assert_eq" | "assert_ne" | "assert_ok" | "assert_err"
13199 | "assert_true" | "assert_false"
13200 | "assert_gt" | "assert_lt" | "assert_ge" | "assert_le"
13201 | "assert_match" | "assert_contains" | "assert_near" | "assert_dies"
13202 | "test_run" | "run_tests" | "test_skip" | "skip_test" | "skip_assert"
13203 | "mounts" | "du" | "du_tree" | "process_list"
13205 | "thread_count" | "pool_info" | "par_bench"
13206 | "stress_cpu" | "scpu" | "stress_mem" | "smem"
13208 | "stress_io" | "sio" | "stress_test" | "st"
13209 | "heat" | "fire" | "fire_and_forget" | "pin"
13210 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
13212 | "stdin"
13213 | "__stryke_rust_compile"
13215 | "vec_set_value"
13216 | "p" | "rev"
13218 | "even" | "odd" | "zero" | "nonzero"
13220 | "positive" | "pos_n" | "negative" | "neg_n"
13221 | "sign" | "negate" | "double" | "triple" | "half"
13222 | "identity" | "id"
13223 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
13224 | "gcd" | "lcm" | "min2" | "max2"
13225 | "log2" | "log10" | "hypot"
13226 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
13227 | "pow2" | "abs_diff"
13228 | "factorial" | "fact" | "fibonacci" | "fib"
13229 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
13230 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
13231 | "median" | "mode_val" | "variance"
13232 | "is_empty" | "is_blank" | "is_numeric"
13234 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
13235 | "is_space" | "is_whitespace"
13236 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
13237 | "capitalize" | "cap" | "swap_case" | "repeat"
13238 | "title_case" | "title" | "squish"
13239 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
13240 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
13241 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
13242 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
13244 | "is_code" | "is_coderef" | "is_ref"
13245 | "is_undef" | "is_defined" | "is_def"
13246 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
13247 | "invert" | "merge_hash"
13249 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
13250 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
13252 | "riffle" | "intersperse" | "every_nth"
13254 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
13255 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
13257 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
13258 | "bits_count" | "popcount" | "leading_zeros" | "lz"
13259 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
13260 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
13262 | "shift_left" | "shl" | "shift_right" | "shr"
13263 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
13264 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
13266 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
13268 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
13269 | "yards_to_m" | "m_to_yards"
13270 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
13272 | "stone_to_kg" | "kg_to_stone"
13273 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
13275 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
13276 | "kb_to_mb" | "mb_to_gb"
13277 | "bits_to_bytes" | "bytes_to_bits"
13278 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
13280 | "seconds_to_hours" | "hours_to_seconds"
13281 | "seconds_to_days" | "days_to_seconds"
13282 | "minutes_to_hours" | "hours_to_minutes"
13283 | "hours_to_days" | "days_to_hours"
13284 | "is_leap_year" | "is_leap" | "days_in_month"
13286 | "month_name" | "month_short"
13287 | "weekday_name" | "weekday_short" | "quarter_of"
13288 | "now_ms" | "now_us" | "now_ns"
13290 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
13291 | "rgb_to_hex" | "hex_to_rgb"
13293 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
13294 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
13295 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
13296 | "strip_ansi"
13297 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
13298 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
13299 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
13300 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
13301 | "bright_magenta" | "bright_cyan" | "bright_white"
13302 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
13303 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
13304 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
13305 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
13306 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
13307 | "white_bold" | "bold_white"
13308 | "blink" | "rapid_blink" | "hidden" | "overline"
13309 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
13310 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
13311 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
13312 | "ipv4_to_int" | "int_to_ipv4"
13314 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
13315 | "path_ext" | "path_stem" | "path_parent" | "path_join" | "path_split"
13317 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
13318 | "const_fn" | "always_true" | "always_false"
13320 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
13321 | "count_eq" | "count_ne" | "all_eq"
13323 | "all_distinct" | "all_unique" | "has_duplicates"
13324 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
13325 | "quote" | "single_quote" | "unquote"
13327 | "extract_between" | "ellipsis"
13328 | "coin_flip" | "dice_roll"
13330 | "random_int" | "random_float" | "random_bool"
13331 | "random_choice" | "random_between"
13332 | "random_string" | "random_alpha" | "random_digit"
13333 | "refresh_stashes"
13335 | "os_name" | "os_arch" | "num_cpus"
13337 | "pid" | "ppid" | "uid" | "gid"
13338 | "username" | "home_dir" | "temp_dir"
13339 | "mem_total" | "mem_free" | "mem_used"
13340 | "swap_total" | "swap_free" | "swap_used"
13341 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
13342 | "load_avg" | "sys_uptime" | "page_size"
13343 | "os_version" | "os_family" | "endianness" | "pointer_width"
13344 | "proc_mem" | "rss"
13345 | "transpose" | "unzip"
13347 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
13348 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
13349 | "tan" | "asin" | "acos" | "atan"
13351 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
13352 | "sqr" | "cube_fn"
13353 | "mod_op" | "ceil_div" | "floor_div"
13354 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
13355 | "degrees" | "radians"
13356 | "min_abs" | "max_abs"
13357 | "saturate" | "sat01" | "wrap_around"
13358 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
13360 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
13361 | "first_word" | "last_word"
13362 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
13363 | "lowercase" | "uppercase"
13364 | "pascal_case" | "pc_case"
13365 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
13366 | "is_palindrome" | "hamming_distance"
13367 | "longest_common_prefix" | "lcp"
13368 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
13369 | "replace_first" | "replace_all_str"
13370 | "contains_any" | "contains_all"
13371 | "starts_with_any" | "ends_with_any"
13372 | "is_pair" | "is_triple"
13374 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
13375 | "is_empty_arr" | "is_empty_hash"
13376 | "is_subset" | "is_superset" | "is_permutation"
13377 | "first_eq" | "last_eq"
13379 | "index_of" | "last_index_of" | "positions_of"
13380 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
13381 | "distinct_count" | "longest" | "shortest"
13382 | "array_union" | "list_union"
13383 | "array_intersection" | "list_intersection"
13384 | "array_difference" | "list_difference"
13385 | "symmetric_diff" | "group_of_n" | "chunk_n"
13386 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
13387 | "pick_keys" | "pick" | "omit_keys" | "omit"
13389 | "map_keys_fn" | "map_values_fn"
13390 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
13391 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
13392 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
13394 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
13396 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
13398 | "argc" | "script_name"
13399 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
13400 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
13402 | "email_domain" | "email_local"
13404 | "url_host" | "url_path" | "url_query" | "url_scheme"
13405 | "file_size" | "fsize" | "file_mtime" | "mtime"
13407 | "file_atime" | "atime" | "file_ctime" | "ctime"
13408 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
13409 | "path_is_abs" | "path_is_rel"
13410 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
13412 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
13413 | "reverse_list" | "list_reverse"
13414 | "without" | "without_nth" | "take_last" | "drop_last"
13415 | "pairwise" | "zipmap"
13416 | "format_bytes" | "human_bytes"
13417 | "format_duration" | "human_duration"
13418 | "format_number" | "group_number"
13419 | "format_percent" | "pad_number"
13420 | "spaceship" | "cmp_num" | "cmp_str"
13421 | "compare_versions" | "version_cmp"
13422 | "hash_insert" | "hash_update" | "hash_delete"
13423 | "matches_regex" | "re_match"
13424 | "count_regex_matches" | "regex_extract"
13425 | "regex_split_str" | "regex_replace_str"
13426 | "shuffle_chars" | "random_char" | "nth_word"
13427 | "head_lines" | "tail_lines" | "count_substring"
13428 | "is_valid_hex" | "hex_upper" | "hex_lower"
13429 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
13430 | "us_to_ns" | "ns_to_us"
13431 | "liters_to_gallons" | "gallons_to_liters"
13432 | "liters_to_ml" | "ml_to_liters"
13433 | "cups_to_ml" | "ml_to_cups"
13434 | "newtons_to_lbf" | "lbf_to_newtons"
13435 | "joules_to_cal" | "cal_to_joules"
13436 | "watts_to_hp" | "hp_to_watts"
13437 | "pascals_to_psi" | "psi_to_pascals"
13438 | "bar_to_pascals" | "pascals_to_bar"
13439 | "match"
13441 | "fst" | "rest" | "rst" | "second" | "snd"
13443 | "last_clj" | "lastc" | "butlast" | "bl"
13444 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
13445 | "cons" | "conj"
13446 | "peek_clj" | "pkc" | "pop_clj" | "popc"
13447 | "some" | "not_any" | "not_every"
13448 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
13449 | "fnil" | "juxt"
13450 | "memoize" | "memo" | "curry" | "once"
13451 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
13452 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
13453 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
13454 | "reductions" | "rdcs"
13455 | "partition_by" | "pby" | "partition_all" | "pall"
13456 | "split_at" | "spat" | "split_with" | "spw"
13457 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
13458 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
13459 | "apply" | "appl"
13460 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
13462 | "zip_longest" | "zipl" | "zip_fill" | "zipf" | "combinations" | "comb" | "permutations" | "perm"
13463 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
13464 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
13465 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
13466 | "each_slice" | "eslice" | "each_cons" | "econs"
13467 | "one" | "none_match" | "nonem"
13468 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
13469 | "minmax" | "mmx" | "minmax_by" | "mmxb"
13470 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
13471 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
13472 | "sum_by" | "sumb" | "uniq_by" | "uqb"
13473 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
13474 | "step" | "upto" | "downto"
13475 | "find_last" | "fndl" | "find_last_index" | "fndli"
13477 | "at_index" | "ati" | "replace_at" | "repa"
13478 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
13479 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
13480 | "object_keys" | "okeys" | "object_values" | "ovals"
13481 | "object_entries" | "oents" | "object_from_entries" | "ofents"
13482 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
13484 | "nub" | "sort_on" | "srton"
13485 | "intersperse_val" | "isp" | "intercalate" | "ical"
13486 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
13487 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
13488 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
13490 | "partition_either" | "peith" | "try_fold" | "tfld"
13491 | "map_while" | "mapw" | "inspect" | "insp"
13492 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
13494 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
13496 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
13497 | "lines_from" | "lfrm" | "unlines" | "unlns"
13498 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
13499 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
13500 | "interpose" | "ipos" | "partition_n" | "partn"
13501 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
13502 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
13503 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
13505 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
13506 | "each_with_object" | "ewo" | "reduce_right" | "redr"
13507 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
13508 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
13509 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
13510 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
13511 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
13512 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
13513 | "union_list" | "unionl" | "intersect_list" | "intl"
13514 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
13515 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
13517 | "split_regex" | "splre" | "replace_regex" | "replre"
13518 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
13519 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
13520 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
13521 | "pluralize" | "plur" | "ordinalize" | "ordn"
13522 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
13523 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
13524 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
13525 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
13526 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
13528 | "dot_product" | "dotp" | "cross_product" | "crossp"
13529 | "matrix_mul" | "matmul" | "mm"
13530 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
13531 | "distance" | "dist" | "manhattan_distance" | "mdist"
13532 | "covariance" | "cov" | "correlation" | "corr"
13533 | "iqr" | "quantile" | "qntl" | "clamp_int" | "clpi"
13534 | "in_range" | "inrng" | "wrap_range" | "wrprng"
13535 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
13536 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
13538 | "diff_days" | "diffd" | "diff_hours" | "diffh"
13539 | "start_of_day" | "sod" | "end_of_day" | "eod"
13540 | "start_of_hour" | "soh" | "start_of_minute" | "som"
13541 | "urle" | "urld"
13543 | "html_encode" | "htmle" | "html_decode" | "htmld"
13544 | "adler32" | "adl32" | "fnv1a" | "djb2"
13545 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
13547 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
13548 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
13549 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
13550 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
13552 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
13553 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
13554 | "partition_point" | "ppt" | "lower_bound" | "lbound"
13555 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
13556 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
13558 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
13559 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
13560 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
13561 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
13562 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
13563 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
13564 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
13566 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
13567 | "connected_components_graph" | "ccgraph"
13568 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
13569 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
13571 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
13572 | "is_hostname_valid" | "ishost"
13573 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
13574 | "is_iso_datetime" | "isisodtm"
13575 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
13576 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
13578 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
13579 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
13580 | "find_all_indices" | "fndalli"
13581 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
13582 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
13583 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
13585 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
13586 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
13587 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
13588 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
13589 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
13590 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
13591 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
13592 | "longest_run" | "lrun" | "longest_increasing" | "linc"
13594 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
13595 | "majority_element" | "majority" | "kth_largest" | "kthl"
13596 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
13597 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
13598 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
13600 | "overlap_coefficient" | "overlapcoef"
13601 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
13602 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
13604 | "hamdist" | "jaro_similarity" | "jarosim"
13605 | "longest_common_substring" | "lcsub"
13606 | "longest_common_subsequence" | "lcseq"
13607 | "count_words" | "wcount" | "count_lines" | "lcount"
13608 | "count_chars" | "ccount" | "count_bytes" | "bcount"
13609 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
13611 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
13612 | "mobius" | "mob" | "is_squarefree" | "issqfr"
13613 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
13614 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
13615 | "day_of_year" | "doy" | "week_of_year" | "woy"
13617 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
13618 | "age_in_years" | "ageyrs"
13619 | "when_true" | "when_false" | "if_else" | "clamp_fn"
13622 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
13623 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
13624 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
13625 | "coalesce" | "default_to" | "fallback"
13626 | "apply_list" | "zip_apply" | "scan"
13627 | "keep_if" | "reject_if" | "group_consecutive"
13628 | "after_n" | "before_n" | "clamp_list" | "normalize_list" | "softmax"
13629
13630 | "matrix_multiply" | "mat_mul"
13634 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
13635
13636
13637
13638 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
13639 | "linspace" | "arange"
13640 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
13642 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
13643 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
13645 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
13646 | "stack_new" | "queue_new" | "lru_new"
13648 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
13649 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
13650 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
13652 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
13653 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
13654 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
13655 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
13656 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
13658 | "planck" | "speed_of_light" | "sqrt2"
13659 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
13661 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
13662 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
13663 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
13664 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
13665 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
13666 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
13668 | "cosine_similarity" | "cube_root" | "entropy" | "float_bits" | "fma"
13669 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
13670 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
13671 | "sigmoid" | "signum" | "square_root"
13672 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
13674 | "squares_seq" | "triangular_seq"
13675 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
13677 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
13678 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
13679 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
13680 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
13681 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
13682 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
13683 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
13684 | "xor_strings"
13685 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
13687 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
13688 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
13689 | "group_by_size" | "hash_filter_keys" | "hash_from_list" | "hash_map_values"
13690 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
13691 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
13692 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
13693 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
13694 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
13695 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
13696 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
13697 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
13698 | "wrap_index" | "digits_of"
13699 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
13701 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
13702 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
13703 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
13704 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
13705 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
13706 | "count_digits" | "count_letters" | "count_lower" | "count_match"
13708 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
13709 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
13710 | "truthy_count" | "undef_count"
13711 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
13713 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
13714 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
13715 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
13716 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
13717 | "range_exclusive" | "range_inclusive"
13718 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
13720 | "collatz_length" | "collatz_sequence" | "convolution" | "cross_entropy"
13721 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
13722 | "epsilon" | "euclidean_distance" | "euler_number" | "exponential_moving_average"
13723 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
13724 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
13725 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
13726 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
13727 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
13728 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
13729 | "tribonacci" | "weighted_mean" | "winsorize"
13730 | "chi_square_stat" | "describe" | "five_number_summary"
13732 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
13733 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
13734 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
13735 | "z_score" | "z_scores"
13736 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
13738 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
13739 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
13740 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
13741 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
13743 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
13744 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
13745 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
13746 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
13748 | "circle_from_three_points" | "convex_hull" | "ellipse_perimeter"
13749 | "frustum_volume" | "haversine_distance" | "line_intersection"
13750 | "point_in_polygon" | "polygon_perimeter" | "pyramid_volume"
13751 | "reflect_point" | "scale_point" | "sector_area"
13752 | "torus_surface" | "torus_volume" | "translate_point"
13753 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
13754 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
13756 | "gravitational_constant" | "phi" | "pi" | "planck_constant" | "proton_mass"
13757 | "sol" | "tau"
13758 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
13760 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
13762 | "bond_price" | "bond_yield" | "capm" | "continuous_compound"
13763 | "discounted_payback" | "duration" | "irr"
13764 | "max_drawdown" | "modified_duration" | "nper" | "num_periods" | "payback_period"
13765 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
13766 | "wacc" | "xirr"
13767 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
13769 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
13770 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
13771 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
13772 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
13773 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
13774 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
13776 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
13777 | "to_emoji_num"
13778 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
13780 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
13782 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
13784 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
13785 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
13786 | "rgb_to_hsl" | "rgb_to_hsv"
13787 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
13789 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
13790 | "matrix_transpose"
13791 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
13793 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
13794 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
13795 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
13796 | "zero_crossings"
13797 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
13799 | "downsample" | "energy" | "envelope" | "highpass_filter" | "idft"
13800 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
13801 | "power_spectrum" | "resample" | "spectral_centroid" | "spectrogram" | "upsample"
13802 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
13803 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
13805 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
13806 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
13807 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
13809 | "sierpinski" | "tower_of_hanoi" | "truth_table"
13810 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
13812 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
13814 | "geometric_series" | "stirling_approx"
13815 | "double_factorial" | "rising_factorial" | "falling_factorial"
13816 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
13817 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
13818 | "map_range"
13819 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
13821 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
13822 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
13823 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
13824 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
13825 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
13826 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
13827 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
13828 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
13829 | "projectile_range" | "projectile_max_height" | "projectile_time"
13830 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
13831 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
13832 | "lens_power" | "thin_lens" | "magnification_lens"
13833 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
13835 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
13836 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
13837 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
13839 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
13840 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
13841 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
13842 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
13843 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
13844 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
13845 | "matrix_solve" | "msolve" | "solve"
13847 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
13848 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
13849 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
13850 | "matrix_pinv" | "mpinv" | "pinv"
13851 | "matrix_cholesky" | "mchol" | "cholesky"
13852 | "matrix_det_general" | "mdetg" | "det"
13853 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
13855 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
13856 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
13857 | "confidence_interval" | "ci"
13858 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
13860 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
13861 | "t_pdf" | "tpdf" | "student_pdf"
13862 | "f_pdf" | "fpdf" | "fisher_pdf"
13863 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
13864 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
13865 | "pareto_pdf" | "paretopdf"
13866 | "lagrange_interp" | "lagrange" | "linterp"
13868 | "cubic_spline" | "cspline" | "spline"
13869 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
13870 | "trapz" | "trapezoid" | "simpson" | "simps"
13872 | "numerical_diff" | "numdiff" | "diff_array"
13873 | "cumtrapz" | "cumulative_trapz"
13874 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
13876 | "golden_section" | "golden" | "gss"
13877 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
13879 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
13881 | "floyd_warshall" | "floydwarshall" | "apsp"
13882 | "prim_mst" | "mst" | "prim"
13883 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
13885 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
13887 | "silu" | "swish" | "mish" | "softplus"
13888 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
13889 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
13891 | "lambert_w" | "lambertw" | "productlog"
13892 | "bessel_j" | "bessel_y" | "bessel_i" | "bessel_k"
13894 | "hankel_h1" | "hankel_h2" | "bessel_j_zero"
13895 | "airy_ai" | "airy_bi" | "airy_ai_prime" | "airy_bi_prime"
13896 | "spherical_bessel_j" | "spherical_bessel_y"
13897 | "struve_h" | "struve_l" | "kelvin_ber" | "kelvin_bei"
13898 | "legendre_p" | "legendre_q" | "assoc_legendre_p"
13900 | "hermite_h" | "hermite_he" | "laguerre_l" | "assoc_laguerre_l"
13901 | "jacobi_p" | "gegenbauer_c" | "chebyshev_t" | "chebyshev_u"
13902 | "spherical_harmonic_y" | "zernike_r"
13903 | "elliptic_k" | "elliptic_e" | "elliptic_pi" | "elliptic_f"
13905 | "elliptic_e_inc" | "elliptic_pi_inc"
13906 | "carlson_rf" | "carlson_rd" | "carlson_rj"
13907 | "jacobi_sn" | "jacobi_cn" | "jacobi_dn" | "jacobi_am"
13908 | "elliptic_theta"
13909 | "weierstrass_p" | "weierstrass_zeta" | "weierstrass_sigma"
13910 | "zeta" | "riemann_zeta" | "hurwitz_zeta"
13912 | "polylog" | "dilog" | "lerch_phi"
13913 | "riemann_siegel_z" | "riemann_siegel_theta"
13914 | "dirichlet_eta" | "dirichlet_beta"
13915 | "hypergeometric_2f1" | "hyper_2f1"
13917 | "hypergeometric_1f1" | "hyper_1f1" | "kummer_m"
13918 | "hypergeometric_0f1" | "hyper_0f1"
13919 | "hypergeometric_pfq" | "hyper_pfq"
13920 | "hypergeometric_u" | "tricomi_u"
13921 | "dedekind_eta" | "klein_j" | "klein_invariant_j"
13923 | "modular_lambda" | "ramanujan_tau"
13924 | "sin_integral" | "si_int" | "cos_integral" | "ci_int"
13926 | "sinh_integral" | "shi_int" | "cosh_integral" | "chi_int"
13927 | "exp_integral_e" | "ei_n" | "exp_integral_ei" | "ei_int"
13928 | "log_integral" | "li_int" | "fresnel_s" | "fresnel_c"
13929 | "jacobi_symbol" | "kronecker_symbol"
13931 | "primitive_root" | "multiplicative_order"
13932 | "mangoldt_lambda" | "von_mangoldt" | "carmichael_lambda"
13933 | "squares_r" | "thue_morse" | "rudin_shapiro"
13934 | "farey_sequence" | "farey"
13935 | "frobenius_number" | "frobenius_solve" | "stern_brocot"
13936 | "stirling_s1" | "stirling_first" | "bell_polynomial_b" | "bell_y"
13938 | "clebsch_gordan" | "three_j_symbol" | "wigner_3j"
13939 | "six_j_symbol" | "wigner_6j" | "nine_j_symbol" | "wigner_9j"
13940 | "debruijn_sequence" | "debruijn" | "wigner_d"
13941 | "q_pochhammer" | "q_factorial" | "q_binomial"
13943 | "q_hypergeometric_pfq"
13944 | "mittag_leffler_e" | "mittag_leffler"
13945 | "coulomb_wave_f" | "coulomb_wave_g"
13946 | "inverse_erf" | "erfinv" | "inverse_erfc" | "erfcinv"
13948 | "inverse_gamma_regularized" | "gamma_lr_inv"
13949 | "inverse_beta_regularized" | "beta_reg_inv"
13950 | "inverse_jacobi_sn"
13951 | "dirac_delta" | "heaviside_theta" | "heaviside"
13953 | "unit_box" | "unit_triangle"
13954 | "square_wave" | "triangle_wave" | "sawtooth_wave" | "dirac_comb"
13955 | "liouville_lambda" | "jordan_totient" | "ramanujan_sum"
13957 | "cyclotomic_polynomial" | "cyclotomic" | "legendre_symbol"
13958 | "pythagorean_triple_q" | "gen_pythagorean_triple"
13959 | "sophie_germain_q" | "mersenne_q"
13960 | "lucas_lehmer_test" | "lucas_lehmer"
13961 | "continued_fraction" | "from_continued_fraction" | "convergents"
13962 | "best_rational_approximation" | "best_rational"
13963 | "motzkin_number" | "motzkin"
13965 | "narayana_number" | "narayana"
13966 | "delannoy_number" | "delannoy"
13967 | "schroder_number" | "schroder" | "large_schroder"
13968 | "small_schroder_number" | "small_schroder"
13969 | "eulerian_number"
13970 | "bernoulli_polynomial" | "euler_polynomial"
13971 | "pell_number" | "pell" | "pell_lucas_number" | "pell_lucas"
13972 | "perrin_number" | "perrin" | "padovan_number" | "padovan"
13973 | "kronecker_product" | "tensor_product" | "tensor_contract"
13975 | "matrix_rank" | "mrank"
13976 | "companion_matrix" | "companion"
13977 | "characteristic_polynomial" | "charpoly"
13978 | "singular_values" | "svals"
13979 | "nullspace" | "null_space" | "kernel"
13980 | "polynomial_gcd" | "polygcd"
13982 | "polynomial_quotient" | "polyquot"
13983 | "polynomial_remainder" | "polyrem"
13984 | "polynomial_resultant" | "resultant"
13985 | "polynomial_discriminant" | "discriminant"
13986 | "polynomial_roots" | "polyroots"
13987 | "gumbel_pdf" | "gumbel_cdf" | "gumbel_quantile"
13989 | "frechet_pdf" | "frechet_cdf" | "frechet_quantile"
13990 | "logistic_pdf" | "logistic_cdf" | "logistic_quantile"
13991 | "rayleigh_pdf" | "rayleigh_cdf" | "rayleigh_quantile"
13992 | "inverse_gamma_pdf" | "inverse_gamma_cdf" | "inverse_gamma_quantile"
13993 | "kumaraswamy_pdf" | "kumaraswamy_cdf" | "kumaraswamy_quantile"
13994 | "mathieu_a" | "mathieu_characteristic_a"
13996 | "mathieu_ce" | "mathieu_se"
13997 | "heun_g"
13999 | "haar_transform" | "haar" | "haar_inverse" | "ihaar"
14001 | "daubechies_db4" | "db4" | "daubechies_db4_inverse" | "idb4"
14002 | "topo_sort_adj"
14004 | "scc_tarjan" | "tarjan_scc" | "strongly_connected"
14005 | "bipartite_q" | "is_bipartite"
14006 | "max_flow_edmonds_karp" | "max_flow" | "edmonds_karp"
14007 | "min_cut" | "eccentricity"
14008 | "graph_diameter" | "graph_radius"
14009 | "stieltjes_constant" | "stieltjes"
14011 | "gauss_sum" | "kloosterman_sum"
14012 | "eta_quotient" | "root_approximant"
14013 | "numerical_gradient" | "ngrad"
14015 | "numerical_jacobian" | "njac"
14016 | "numerical_hessian" | "nhess"
14017 | "numerical_divergence" | "ndiv"
14018 | "numerical_curl" | "ncurl"
14019 | "numerical_laplacian" | "nlap"
14020 | "nelder_mead" | "simplex_min"
14022 | "gradient_descent" | "gd_min"
14023 | "bfgs_minimize" | "bfgs"
14024 | "levenberg_marquardt" | "lev_marq" | "lm_min"
14025 | "conjugate_gradient" | "cg_solve"
14026 | "least_squares" | "lstsq"
14027 | "romberg" | "romberg_int"
14029 | "gauss_legendre_quad" | "glquad" | "gl_quad"
14030 | "monte_carlo_integrate" | "mc_int"
14031 | "adaptive_simpson" | "asimp"
14032 | "lu_decompose" | "ludec"
14034 | "qr_decompose" | "qrdec"
14035 | "householder_reflector" | "householder"
14036 | "givens_rotation" | "givens"
14037 | "forward_substitute" | "fwdsub"
14038 | "back_substitute" | "backsub"
14039 | "hessenberg_reduce" | "hessen"
14040 | "poly_derivative" | "polyder"
14042 | "poly_integrate" | "polyint"
14043 | "poly_compose" | "poly_eval_horner" | "horner"
14044 | "pade_approximant" | "pade"
14045 | "quat_mul" | "quat_conj" | "quat_norm" | "quat_inv"
14047 | "quat_from_axis_angle" | "axis_angle_to_quat"
14048 | "quat_to_axis_angle"
14049 | "quat_to_matrix" | "quat_from_matrix" | "matrix_to_quat"
14050 | "quat_slerp" | "slerp"
14051 | "euler_zyx_to_matrix" | "matrix_to_euler_zyx"
14052 | "rotate_3d_vec"
14053 | "kl_divergence" | "kl_div"
14055 | "js_divergence" | "js_div"
14056 | "mutual_information" | "mi"
14057 | "cross_entropy_arr" | "cross_entropy_dist"
14058 | "renyi_entropy" | "tsallis_entropy"
14059 | "pauli_x" | "pauli_y" | "pauli_z"
14061 | "pauli_id" | "pauli_i" | "pauli_identity"
14062 | "ket_bra" | "density_matrix" | "expectation_value" | "expval"
14063 | "commutator" | "anticommutator"
14064 | "partial_trace" | "ptrace"
14065 | "von_neumann_entropy" | "vn_entropy"
14066 | "bose_einstein" | "fermi_dirac"
14068 | "maxwell_boltzmann_speed" | "mb_speed"
14069 | "partition_function" | "z_partition"
14070 | "helmholtz_free_energy" | "free_energy_f"
14071 | "boltzmann_factor"
14072 | "einstein_specific_heat" | "einstein_cv"
14073 | "fresnel_reflection_te" | "fresnel_reflection_tm"
14075 | "fresnel_transmission_te" | "fresnel_transmission_tm"
14076 | "abcd_thin_lens" | "abcd_free_space"
14077 | "gaussian_beam_q"
14078 | "kepler_solve"
14080 | "true_to_eccentric" | "eccentric_to_mean"
14081 | "julian_date" | "j_date"
14082 | "jd_to_gregorian" | "jd_to_date"
14083 | "sidereal_time_gmst" | "gmst"
14084 | "vis_viva" | "orbital_period_kepler"
14085 | "orbital_elements_to_state" | "elem_to_state"
14086 | "kalman_step" | "kalman_filter"
14088 | "exponential_smoothing" | "exp_smooth"
14089 | "holt_winters" | "arma_yw_fit" | "ar_yw"
14090 | "pagerank" | "betweenness_centrality" | "closeness_centrality"
14092 | "eigenvector_centrality" | "degree_centrality" | "triangle_count"
14093 | "rgumbel" | "rfrechet" | "rrayleigh"
14095 | "rlogistic" | "rkumaraswamy" | "rinverse_gamma" | "rinvgamma"
14096 | "graham_scan" | "convex_hull_2d"
14098 | "line_line_intersect_2d" | "ll_intersect_2d"
14099 | "point_segment_distance" | "p_seg_dist"
14100 | "forward_diff" | "fdiff"
14102 | "forward_diff_grad" | "fdiff_grad"
14103 | "bartlett_test" | "levene_test"
14105 | "fishers_exact_test_2x2" | "fishers_exact"
14106 | "mcnemar_test"
14107 | "runs_test" | "wald_wolfowitz"
14108 | "friedman_test" | "kruskal_wallis_test" | "kruskal"
14109 | "sign_test"
14110 | "anderson_darling_normality" | "ad_normality"
14111 | "jarque_bera_test" | "jb_test"
14112 | "ljung_box_test" | "ljung_box"
14113 | "durbin_watson_stat" | "durbin_watson"
14114 | "mahalanobis_distance" | "mahalanobis_dist"
14116 | "cosine_distance" | "canberra_distance"
14117 | "bray_curtis_distance" | "bray_curtis"
14118 | "l1_distance"
14119 | "chi_squared_distance"
14120 | "multivariate_normal_pdf" | "mvn_pdf"
14122 | "multivariate_normal_sample" | "rmvn"
14123 | "dirichlet_pdf" | "dirichlet_sample" | "rdirichlet"
14124 | "skellam_pmf"
14125 | "inverse_gaussian_pdf" | "wald_pdf"
14126 | "inverse_gaussian_cdf" | "wald_cdf"
14127 | "inverse_gaussian_sample" | "rwald"
14128 | "non_central_chi2_pdf" | "ncchi2_pdf"
14129 | "matrix_exp" | "expm" | "matrix_log" | "logm"
14131 | "matrix_sqrt" | "sqrtm" | "matrix_sin" | "sinm"
14132 | "matrix_cos" | "cosm"
14133 | "rk45_dormand_prince" | "rk45" | "dopri5"
14135 | "midpoint_step" | "ode_midpoint"
14136 | "heun_step" | "ode_heun"
14137 | "verlet_step" | "ode_verlet"
14138 | "logistic_regression" | "logit_fit"
14140 | "poisson_regression"
14141 | "ridge_regression" | "ridge"
14142 | "lasso_coord" | "lasso"
14143 | "bootstrap_mean_ci" | "boot_mean_ci"
14145 | "jackknife_estimate" | "jackknife"
14146 | "permutation_test_diff" | "perm_test_diff"
14147 | "acf_at_lag" | "diff_op" | "lag_op"
14149 | "decompose_classical" | "decompose_ts"
14150 | "combinations_list" | "permutations_list"
14152 | "cyclic_permutations" | "subsets_of_size"
14153 | "longest_increasing_subseq" | "lis"
14155 | "knapsack_01" | "knapsack"
14156 | "subset_sum_target" | "subset_sum"
14157 | "coin_change_min" | "coin_change_minimum"
14158 | "edit_distance_levenshtein" | "edit_distance"
14159 | "one_hot_encode" | "onehot" | "label_encode"
14161 | "categorical_cross_entropy" | "cce"
14162 | "classification_metrics" | "binary_metrics"
14163 | "roc_auc" | "auroc"
14164 | "gaussian_blur_kernel" | "sobel_x" | "sobel_y"
14166 | "prewitt_x" | "prewitt_y"
14167 | "laplacian_of_gaussian" | "log_kernel"
14168 | "brownian_path" | "wiener_path"
14170 | "geometric_brownian_path" | "gbm_path"
14171 | "poisson_process" | "random_walk_1d"
14172 | "lempel_ziv_complexity" | "lz_complexity"
14174 | "huffman_code_lengths" | "huffman"
14175 | "shannon_entropy_rate" | "block_entropy_rate"
14176 | "planck_blackbody" | "blackbody"
14178 | "rayleigh_jeans" | "compton_shift"
14179 | "rydberg_energy"
14180 | "hydrogen_radial_wavefunction" | "h_rad_psi"
14181 | "integer_log" | "ilog"
14183 | "aks_primality" | "aks"
14184 | "elliptic_curve_add" | "ec_add"
14185 | "berlekamp_massey" | "bm_lfsr"
14186 | "bezout_coefficients" | "bezout" | "extended_euclid"
14187 | "factor_quadratic" | "complete_square"
14189 | "partial_fraction_simple" | "partial_fraction"
14190 | "gauss_chebyshev_quad" | "gc_quad"
14192 | "gauss_hermite_quad" | "gh_quad"
14193 | "gauss_laguerre_quad" | "glag_quad"
14194 | "clenshaw_curtis_quad" | "cc_quad"
14195 | "tanh_sinh_quad" | "ts_quad"
14196 | "gauss_legendre_2d" | "gl_2d"
14197 | "monte_carlo_2d" | "mc_2d"
14198 | "simulated_annealing" | "sa_min"
14200 | "simplex_lp" | "lp_simplex"
14201 | "particle_swarm" | "pso_min"
14202 | "gev_pdf" | "gev_cdf" | "gev_sample" | "rgev"
14204 | "gen_pareto_pdf" | "gen_pareto_cdf"
14205 | "gen_pareto_sample" | "rgenpareto"
14206 | "skew_normal_pdf" | "skew_normal_cdf"
14207 | "mixture_normal_pdf"
14208 | "categorical_sample" | "rcat"
14209 | "multinomial_pmf" | "multinomial_sample" | "rmultinom"
14210 | "truncated_normal_pdf"
14211 | "truncated_normal_sample" | "rtnorm"
14212 | "dbscan" | "gmm_em_1d" | "gmm_1d"
14214 | "silhouette_score"
14215 | "davies_bouldin_index" | "db_index"
14216 | "calinski_harabasz_index" | "ch_index"
14217 | "mds_2d" | "pcoa_2d" | "mean_shift"
14218 | "batch_norm" | "layer_norm"
14220 | "dropout_mask"
14221 | "max_pool_1d" | "avg_pool_1d"
14222 | "attention_softmax" | "positional_encoding"
14223 | "glorot_init" | "xavier_init"
14224 | "he_init" | "kaiming_init"
14225 | "adam_step" | "rmsprop_step"
14226 | "ewma" | "ccf" | "periodogram"
14228 | "welch_psd" | "welch"
14229 | "lag_features"
14230 | "median_filter_2d"
14232 | "threshold_otsu" | "otsu"
14233 | "histogram_equalize" | "hist_eq"
14234 | "erode_2d" | "dilate_2d"
14235 | "mse_loss" | "mae_loss" | "huber_loss"
14237 | "vincenty_distance" | "vincenty"
14239 | "mercator_project"
14240 | "destination_from_bearing" | "dest_bearing"
14241 | "recaman" | "recaman_seq"
14243 | "sylvester" | "sylvester_seq"
14244 | "happy_q" | "is_happy"
14245 | "amicable_pair_q"
14246 | "aliquot_sequence"
14247 | "magic_constant"
14248 | "clustering_coefficient_local" | "cc_local"
14250 | "clustering_coefficient_global" | "cc_global"
14251 | "assortativity" | "common_neighbors" | "jaccard_neighbors"
14252 | "adamic_adar"
14253 | "preferential_attachment_score" | "pa_score"
14254 | "triangle_3d_normal" | "triangle_3d_area"
14256 | "tetrahedron_volume"
14257 | "plane_from_3_points" | "plane_from_pts"
14258 | "point_to_plane_distance" | "pt_plane_dist"
14259 | "ray_triangle_intersect" | "moller_trumbore"
14260 | "ray_sphere_intersect" | "aabb_overlap"
14261 | "gauss_seidel"
14263 | "jacobi_iteration" | "jacobi_solve"
14264 | "sor_solve" | "sor"
14265 | "thomas_tridiag_solve" | "thomas"
14266 | "richardson_extrapolation" | "richardson"
14267 | "finite_difference_5pt" | "fd5pt"
14268 | "tonelli_shanks_sqrt" | "tonelli_shanks"
14270 | "baby_step_giant_step" | "bsgs"
14271 | "pollard_rho_factor" | "pollard_rho"
14272 | "modular_lcm" | "mlcm"
14273 | "crt_general" | "crt_arbitrary"
14274 | "van_der_waals_p" | "vdw_pressure"
14276 | "nernst_equation" | "nernst"
14277 | "arrhenius_rate" | "arrhenius"
14278 | "reduced_mass"
14279 | "ph_to_concentration" | "ph_to_h"
14280 | "metropolis_hastings" | "mh_sampler"
14282 | "gibbs_sampler_step" | "gibbs_step"
14283 | "euler_maruyama" | "em_sde"
14284 | "milstein" | "milstein_sde"
14285 | "ornstein_uhlenbeck_path" | "ou_path"
14286 | "hmm_forward" | "hmm_viterbi" | "hmm_backward"
14287 | "kaplan_meier" | "km_estimator" | "log_rank_test"
14289 | "needleman_wunsch" | "nw_align"
14290 | "smith_waterman" | "sw_align"
14291 | "gibbs_free_energy" | "delta_g"
14293 | "henderson_hasselbalch" | "hh_eq"
14294 | "radioactive_decay"
14295 | "half_life_to_constant" | "hl_to_lambda"
14296 | "pid_step"
14298 | "transfer_function_eval" | "tf_eval"
14299 | "bode_magnitude_db" | "bode_mag_db"
14300 | "bode_phase_deg"
14301 | "lqr_2x2"
14302 | "nash_eq_2x2" | "nash_2x2"
14304 | "shapley_value" | "expected_utility"
14305 | "hungarian_assignment" | "hungarian"
14307 | "tsp_nearest_neighbor" | "tsp_nn"
14308 | "vertex_cover_2approx" | "vc_2approx"
14309 | "heat_eq_1d" | "wave_eq_1d"
14311 | "laplace_2d_jacobi" | "laplace_jacobi"
14312 | "beta_binomial_update"
14314 | "normal_normal_update"
14315 | "gamma_poisson_update"
14316 | "dirichlet_multinomial_update"
14317 | "hadamard_gate" | "h_gate"
14319 | "cnot_gate" | "cx_gate"
14320 | "swap_gate" | "cz_gate"
14321 | "qft_matrix" | "phase_gate"
14322 | "s_gate" | "t_gate"
14323 | "bezier_eval"
14325 | "catmull_rom_eval" | "cmr_eval"
14326 | "cubic_hermite_eval" | "ch_eval"
14327 | "bspline_basis" | "nik_basis"
14328 | "freq_to_midi" | "midi_to_freq"
14330 | "equal_temperament_freq"
14331 | "cents_difference" | "cents_diff"
14332 | "redshift_z" | "hubble_distance" | "luminosity_distance"
14334 | "reynolds_number" | "mach_number"
14336 | "prandtl_number" | "bernoulli_velocity"
14337 | "negative_binomial_pmf" | "nb_pmf"
14339 | "hypergeometric_pmf"
14340 | "beta_binomial_pmf" | "bb_pmf"
14341 | "von_mises_pdf" | "vmf_pdf"
14342 | "erdos_renyi_random" | "erdos_renyi"
14344 | "barabasi_albert_random" | "barabasi_albert"
14345 | "watts_strogatz_random" | "watts_strogatz"
14346 | "rgb_to_lab" | "lab_to_rgb"
14348 | "kelvin_to_rgb" | "color_temp_rgb"
14349 | "bell_triangle" | "surjection_count"
14351 | "distinct_partition_count" | "q_partition"
14352 | "fibonacci_q" | "is_fib_number"
14353 | "bonferroni_correction" | "bonferroni"
14355 | "benjamini_hochberg" | "bh_fdr"
14356 | "tukey_hsd"
14357 | "hellinger_distance"
14358 | "wasserstein_1d" | "earth_movers_1d"
14359 | "chi_squared_divergence"
14360 | "beta_geometric_pmf"
14361 | "generalized_gamma_pdf" | "gengamma_pdf"
14362 | "zip_pmf" | "zero_inflated_poisson_pmf"
14363 | "stefan_boltzmann_luminosity" | "stellar_luminosity"
14364 | "photon_momentum" | "photon_energy_ev"
14365 | "dipole_radiation_power" | "larmor_power"
14366 | "parallax_to_distance" | "hawking_temperature"
14367 | "roche_limit" | "apparent_magnitude" | "distance_modulus"
14368 | "beer_lambert" | "absorbance"
14369 | "rate_law_n"
14370 | "freezing_point_depression" | "fpd"
14371 | "mixed_nash_2x2" | "minimax_2x2"
14372 | "barycentric_coords_2d" | "barycentric_2d"
14374 | "bresenham_line" | "bilinear_interp_2d"
14375 | "point_in_polygon_2d"
14376 | "hilbert_transform" | "cepstrum"
14377 | "butterworth_lowpass_coeffs" | "butter_lp"
14378 | "savitzky_golay_coeffs" | "sg_coeffs"
14379 | "savitzky_golay_filter" | "sg_filter"
14380 | "canny_edge_intensity" | "canny_intensity"
14381 | "bilateral_filter_basic" | "bilateral_filter"
14382 | "kmeans_pp_init" | "kpp_init"
14383 | "elbow_score" | "wcss"
14384 | "young_tableaux_count" | "syt_count"
14385 | "euler_alt_permutation" | "euler_zigzag"
14386 | "genocchi_number" | "lattice_paths_count"
14387 | "tetration"
14388 | "ackermann_limited" | "ackermann"
14389 | "perfect_power_q" | "b_smooth_q"
14390 | "k_core"
14392 | "rich_club_coefficient" | "rich_club"
14393 | "rsa_basic_encrypt" | "rsa_enc_int"
14394 | "rsa_basic_decrypt" | "rsa_dec_int"
14395 | "dh_shared_secret"
14396 | "bell_state_phi_plus" | "bell_phi_plus"
14397 | "bell_state_psi_minus" | "bell_psi_minus"
14398 | "density_matrix_purity" | "rho_purity"
14399 | "concurrence_2qubit"
14400 | "point_in_circle"
14401 | "circle_circle_intersect_2d"
14402 | "polygon_centroid"
14403 | "sutherland_hodgman_clip" | "sh_clip"
14404 | "kalman_rts_smoother" | "rts_smoother"
14405 | "gc_content" | "codon_to_aa"
14407 | "reverse_complement_dna" | "rev_comp_dna"
14408 | "hamming_dna"
14409 | "blosum62_pair_score" | "blosum62"
14410 | "kmer_count"
14411 | "great_circle_bearing" | "gc_bearing"
14413 | "midpoint_lat_lon" | "mid_geo"
14414 | "utm_zone_for"
14415 | "area_polygon_lat_lon" | "geo_polygon_area"
14416 | "crr_binomial_option" | "crr_option"
14418 | "bond_price_clean"
14419 | "bond_yield_to_maturity" | "bond_ytm"
14420 | "modified_duration_bond"
14421 | "convexity_bond" | "bond_convexity"
14422 | "ssim" | "psnr" | "mssim"
14424 | "db_spl_from_pa" | "db_spl"
14426 | "a_weighting_factor" | "a_weight"
14427 | "octave_band_center" | "octave_center"
14428 | "semitone_ratio"
14429 | "hardy_weinberg"
14431 | "expected_heterozygosity" | "het_e"
14432 | "fst_simple"
14433 | "allele_frequencies"
14434 | "sir_step" | "sir_r0" | "doubling_time"
14436 | "theil_index"
14438 | "herfindahl_hirschman" | "hhi"
14439 | "atkinson_index"
14440 | "lorenz_curve_points"
14441 | "iota_range" | "iota"
14443 | "reshape_array" | "reshape"
14444 | "grade_up" | "grade_asc"
14445 | "grade_down" | "grade_desc"
14446 | "plasma_frequency" | "omega_p"
14448 | "debye_length" | "lambda_d"
14449 | "cyclotron_frequency" | "omega_c"
14450 | "larmor_radius" | "gyroradius"
14451 | "jaro_winkler_similarity" | "jaro_winkler"
14453 | "metaphone_simple"
14454 | "elo_rating_update" | "elo"
14456 | "glicko_rating_update" | "glicko"
14457 | "dice_sum_pmf"
14458 | "cohens_d" | "effect_size_d"
14460 | "cliff_delta"
14461 | "vargha_delaney_a12" | "a12"
14462 | "step_response_2nd_order" | "step_2nd"
14464 | "overshoot_2nd_order" | "overshoot_pct"
14465 | "frobenius_norm"
14467 | "spectral_norm" | "operator_norm_2"
14468 | "trace_matrix" | "tr_mat"
14469 | "homophily_index" | "homophily"
14471 | "dyad_census" | "triad_census"
14472 | "sigmoid_inverse" | "logit"
14474 | "partition_at" | "drop_at" | "insert_at_idx"
14476 | "replace_at_index" | "set_at"
14477 | "swap_indices" | "nth_largest" | "nth_smallest"
14478 | "position_of_all_matching" | "positions_of_all"
14479 | "string_take_first" | "string_take_last"
14480 | "string_drop_first" | "string_drop_last"
14481 | "pluralize_simple"
14482 | "singularize_simple" | "singularize"
14483 | "capitalize_words" | "title_words"
14484 | "format_table_simple" | "ascii_table"
14485 | "days_between" | "weeks_between"
14486 | "months_between" | "years_between"
14487 | "first_of_month" | "last_of_month"
14488 | "day_of_week_iso" | "iso_dow"
14489 | "easter_sunday" | "chinese_zodiac"
14490 | "iso_week_number" | "iso_week"
14491 | "relative_luminance" | "wcag_luminance"
14492 | "contrast_ratio_wcag" | "wcag_contrast"
14493 | "delta_e_76" | "delta_e"
14494 | "color_blend_t" | "lerp_color"
14495 | "chord_to_freqs" | "scale_to_intervals"
14496 | "interval_semitones"
14497 | "transpose_freq_semitones" | "transpose_semi"
14498 | "bpm_to_period" | "midi_to_pitch_class"
14499 | "key_signature_for" | "circle_of_fifths_step"
14500 | "moon_phase" | "equation_of_time"
14501 | "solar_declination" | "sidereal_day_period" | "ecliptic_obliquity"
14502 | "permutation_order"
14503 | "permutation_parity" | "perm_sign"
14504 | "identity_permutation"
14505 | "permutation_compose" | "perm_mul"
14506 | "flesch_reading_ease" | "flesch_kincaid_grade"
14507 | "gunning_fog"
14508 | "automated_readability_index" | "ari"
14509 | "lix"
14510 | "adjusted_r_squared" | "adj_r2"
14511 | "aic" | "bic"
14512 | "residuals_compute" | "compute_residuals"
14513 | "composition_count" | "weak_composition_count"
14514 | "necklace_count" | "bracelet_count"
14515 | "multiset_permutations_count" | "multinomial_count"
14516 | "pearson_hash_byte" | "pearson_hash"
14517 | "xorshift32_step" | "lcg_next_u32"
14518 | "fisher_yates_shuffle"
14519 | "tetrahedral_number" | "square_pyramidal_number"
14521 | "octahedral_number" | "pentagonal_pyramidal_number"
14522 | "cake_number" | "cuban_number" | "centered_hexagonal_number"
14523 | "carmichael_q" | "is_carmichael"
14524 | "sphenic_q" | "is_sphenic"
14525 | "seven_smooth_q" | "is_7_smooth"
14526 | "cartesian_product_n" | "cart_n"
14527 | "multiset_union" | "multiset_intersection" | "multiset_difference"
14528 | "polynomial_roots_dk" | "durand_kerner"
14529 | "lin_bairstow_step" | "bairstow"
14530 | "heap_sift_down"
14531 | "fenwick_build" | "bit_build"
14532 | "fenwick_query" | "bit_query"
14533 | "segment_tree_sum" | "seg_sum"
14534 | "kmp_failure" | "kmp"
14535 | "z_array" | "z_func"
14536 | "suffix_array_naive"
14537 | "manacher_radii" | "manacher"
14538 | "rabin_karp_hash" | "lcp_array"
14539 | "regex_escape_simple"
14540 | "horspool_search" | "bm_horspool"
14541 | "lpt_schedule" | "lpt"
14542 | "johnsons_rule" | "johnson_2m"
14543 | "bit_reverse_32" | "bit_reverse"
14544 | "bin_to_gray" | "gray_to_bin"
14545 | "swap_bits_pos" | "swap_bits"
14546 | "hamming_weight" | "popcnt"
14547 | "hamming_distance_int" | "hamdist_int"
14548 | "internal_rate_of_return"
14549 | "modified_irr" | "mirr"
14550 | "payback_period_simple" | "payback_simple"
14551 | "rfc3339_format" | "rfc3339"
14552 | "rfc3339_parse"
14553 | "iso_ordinal_date" | "ordinal_date"
14554 | "lazy_caterer" | "central_polygonal"
14556 | "centered_square" | "centered_triangular" | "centered_pentagonal"
14557 | "star_number" | "dodecahedral_number" | "icosahedral_number"
14558 | "pronic_number" | "squared_triangular"
14559 | "woodall_number" | "cullen_number"
14560 | "repunit" | "repdigit" | "kaprekar_routine_step"
14561 | "smith_q"
14562 | "keith_q" | "is_keith"
14563 | "armstrong_q" | "is_armstrong"
14564 | "fnv1a_hash" | "djb2_hash"
14565 | "jenkins_one_at_a_time" | "jenkins_oat"
14566 | "murmurhash3_x32"
14567 | "adler32_hash" | "crc16_ccitt"
14568 | "vec_dot"
14569 | "l1_norm" | "l2_norm" | "vec_l2"
14570 | "linf_norm" | "max_norm" | "lp_norm"
14571 | "unit_vector"
14572 | "vector_project" | "proj" | "vector_reject"
14573 | "orthogonalize_vectors" | "gram_schmidt"
14574 | "outer_product" | "vec_outer"
14575 | "matrix_diagonal" | "mdiagvec"
14576 | "matrix_anti_diagonal"
14577 | "matrix_symmetric_q" | "matrix_orthogonal_q"
14578 | "geometric_mean_arr" | "harmonic_mean_arr"
14579 | "quadratic_mean_arr" | "lehmer_mean"
14580 | "running_mean" | "running_variance"
14581 | "outlier_iqr_q" | "z_score_robust"
14582 | "geometric_sequence" | "arithmetic_sequence"
14583 | "log_sum_exp" | "lse"
14584 | "log_sigmoid" | "log1p_exp"
14585 | "string_chars"
14586 | "string_words_count" | "word_count_simple"
14587 | "string_lines_count" | "line_count_simple"
14588 | "string_intersperse" | "string_replicate"
14589 | "string_uniq_chars" | "string_letter_frequency"
14590 | "anagram_q" | "is_anagram_q"
14591 | "string_take_while" | "string_drop_while"
14592 | "string_split_at_first" | "string_partition_at_word"
14593 | "relativistic_kinetic"
14595 | "lorentz_factor_v" | "doppler_relativistic"
14596 | "drag_force_quadratic" | "terminal_velocity"
14597 | "carnot_efficiency" | "otto_efficiency"
14598 | "brayton_efficiency" | "diesel_efficiency"
14599 | "specific_heat_const_v" | "speed_of_sound_ideal"
14600 | "kepler_period_au" | "synodic_period"
14601 | "hill_radius" | "jeans_length"
14602 | "chandrasekhar_mass" | "eddington_luminosity"
14603 | "schwarzschild_radius_m" | "gravity_at_radius"
14604 | "gravitational_pe"
14605 | "freefall_time" | "pendulum_freq" | "spring_period"
14606 | "centripetal_accel" | "lens_focal_length"
14607 | "avogadros_number" | "boltzmann_const"
14608 | "planck_const_h" | "gas_constant_r"
14609 | "concentration_dilute" | "partial_pressure"
14610 | "mole_fraction" | "molarity" | "molality"
14611 | "normality_chem" | "ionic_strength"
14612 | "titration_volume"
14613 | "atomic_radius_pm" | "de_broglie_wavelength_kg"
14614 | "lotka_volterra_step"
14615 | "michaelis_menten" | "hill_equation"
14616 | "lineweaver_burk" | "eadie_hofstee_y"
14617 | "arrhenius_temp_q10"
14618 | "body_surface_area_dubois" | "bsa_dubois"
14619 | "bmr_harris_benedict_male" | "bmr_harris_benedict_female"
14620 | "max_heart_rate" | "target_heart_rate"
14621 | "vo2_max_estimate" | "pulse_pressure"
14622 | "mean_arterial_pressure" | "map_bp"
14623 | "dew_point_magnus" | "heat_index_celsius"
14624 | "wind_chill_celsius" | "pressure_altitude_m"
14625 | "density_altitude_m" | "saturation_vapor_pressure"
14626 | "humidex" | "utci_simple"
14627 | "resistance_parallel" | "r_parallel"
14628 | "resistance_series" | "r_series"
14629 | "capacitance_parallel" | "c_parallel"
14630 | "capacitance_series" | "c_series"
14631 | "inductance_parallel" | "l_parallel"
14632 | "inductance_series" | "l_series"
14633 | "voltage_divider" | "current_divider"
14634 | "lc_resonant" | "q_factor_rlc"
14635 | "skin_depth" | "wire_resistance"
14636 | "motor_torque" | "efficiency_ratio"
14637 | "dB_voltage" | "db_voltage"
14638 | "dB_power" | "db_power"
14639 | "bfs_distances" | "dfs_preorder" | "connected_components"
14641 | "graph_is_tree" | "graph_density"
14642 | "graph_average_degree" | "graph_max_degree" | "graph_min_degree"
14643 | "graph_complement"
14644 | "in_degree_directed" | "out_degree_directed"
14645 | "graph_eccentricity_all" | "is_connected"
14646 | "articulation_points" | "bridges_edges"
14647 | "eulerian_path_q" | "hamiltonian_brute"
14648 | "string_to_charcodes" | "charcodes_to_string"
14649 | "string_xor"
14650 | "string_camel_to_snake" | "string_snake_to_camel"
14651 | "string_kebab_to_snake" | "string_snake_to_kebab"
14652 | "palindromic_q" | "substring_count"
14653 | "string_truncate_ellipsis" | "string_expand_tabs"
14654 | "string_normalize_spaces"
14655 | "days_in_year" | "quarter_of_year"
14656 | "zeller_day_of_week" | "age_from_birthdate"
14657 | "business_days_between" | "unix_epoch_to_iso"
14658 | "loan_payment_pmt" | "loan_balance"
14659 | "amortization_total_interest"
14660 | "apr_to_apy" | "apy_to_apr"
14661 | "compound_interest_periods" | "simple_interest_compute"
14662
14663 | "perpetuity_value" | "growing_perpetuity"
14664 | "annuity_present_value" | "annuity_future_value"
14665 | "capm_expected_return"
14666 | "treynor_ratio"
14667 | "jensens_alpha" | "information_ratio"
14668 | "friction_factor_laminar" | "swamee_jain_factor"
14669 | "pipe_pressure_drop" | "orifice_velocity"
14670 | "chezy_velocity" | "manning_velocity"
14671 | "froude_number" | "weber_number" | "grashof_number"
14672 | "nusselt_dittus_boelter"
14673 | "mollweide_project" | "robinson_project" | "sinusoidal_project"
14675 | "equirectangular_project" | "lambert_azimuthal_project" | "albers_conic_project"
14676 | "geohash_encode" | "geohash_decode" | "geohash_neighbor" | "geohash_bbox"
14677 | "gabor_kernel" | "unsharp_mask_kernel" | "emboss_kernel"
14678 | "box_blur_kernel" | "motion_blur_kernel" | "sharpen_kernel"
14679 | "edge_detect_kernel" | "sobel_diagonal_kernel" | "haar_2d_step"
14680 | "db4_coeffs" | "db6_coeffs" | "sym4_coeffs" | "coif1_coeffs"
14681 | "aes_sbox_byte" | "aes_inv_sbox_byte"
14682 | "chacha20_qround" | "xtea_round" | "speck_round" | "simon_round"
14683 | "kepler_hyperbolic" | "hohmann_dv1" | "hohmann_dv2" | "hohmann_total"
14684 | "bielliptic_total" | "lambert_simple"
14685 | "horizon_distance" | "solar_zenith_angle" | "air_mass_kasten"
14686 | "solar_constant" | "julian_centuries_j2000"
14687 | "mean_solar_longitude" | "mean_solar_anomaly" | "lst_to_solar"
14688 | "ra_dec_to_az_alt" | "ecliptic_to_equatorial" | "equatorial_to_galactic"
14689 | "orbital_eccentricity" | "semi_major_axis"
14690 | "specific_orbital_energy" | "specific_angular_momentum"
14691 | "toffoli_gate" | "ccx_gate" | "fredkin_gate" | "cswap_gate"
14692 | "iswap_gate" | "sqrt_swap_gate"
14693 | "rx_gate" | "ry_gate" | "rz_gate"
14694 | "ghz_state_n" | "w_state_n"
14695 | "depolarizing_channel" | "dephasing_channel" | "amplitude_damping_channel"
14696 | "quantum_fidelity_pure" | "trace_distance"
14697 | "bell_inequality_chsh" | "pauli_decomposition_2x2"
14698 | "quantum_relative_entropy" | "qft_4_real"
14699 | "bwt_encode" | "bwt_decode" | "mtf_encode" | "mtf_decode"
14700
14701 | "lyndon_factorize" | "christoffel_word" | "sturmian_word"
14702 | "z_function_alt" | "period_of_string" | "borders_of_string"
14703 | "thue_morse_string" | "fibonacci_word"
14704 | "mann_kendall_tau" | "theil_sen_slope" | "hodges_lehmann"
14705 | "huber_m_estimator" | "winsorized_variance_arr"
14706 | "bowley_skewness" | "pearson_skewness_2"
14707 | "concordance_correlation" | "quantile_p"
14708 | "label_propagation_step" | "modularity_q"
14709 | "clique_count_3" | "local_efficiency" | "global_efficiency"
14710 | "diameter_unweighted"
14711 | "aitken_delta_squared" | "wynn_epsilon"
14712 | "shanks_transform" | "levin_t_transform"
14713 | "harmonic_seq_sum" | "alternating_seq_sum"
14714 | "sparse_csr_build" | "sparse_csr_mul_vec" | "sparse_density"
14716 | "lower_triangular_q" | "upper_triangular_q"
14717 | "diagonal_dominance_q" | "matrix_zero_q" | "matrix_identity_q"
14718 | "matrix_random_uniform" | "matrix_random_normal"
14719 | "andrew_monotone_chain" | "polygon_area_signed"
14720 | "polygon_convex_q" | "iou_2d_axis_aligned" | "hausdorff_distance_2d"
14721 | "minkowski_sum_simple" | "circle_3_points"
14722 | "polygon_winding_number" | "segment_length"
14723 | "segments_parallel_q" | "segments_perpendicular_q"
14724 | "burr_xii_pdf" | "burr_xii_cdf" | "dagum_pdf" | "lomax_pdf"
14725 | "birnbaum_saunders_pdf" | "tukey_lambda_quantile"
14726 | "half_cauchy_pdf" | "half_logistic_pdf" | "reciprocal_pdf"
14727 | "levy_pdf" | "voigt_profile_simple"
14728 | "gompertz_pdf" | "inverse_weibull_pdf"
14729 | "log_gamma_simple" | "inverse_chi2_pdf"
14730 | "poly1305_block_step" | "x25519_field_mul" | "curve25519_mul_simple"
14731 | "secp256k1_y_recover" | "hmac_step_xor"
14732 | "pkcs7_pad" | "pkcs7_unpad" | "xor_byte_string"
14733 | "atbash_cipher"
14734 | "vigenere_encrypt" | "vigenere_decrypt" | "xor_brute_keylen"
14735 | "arima_diff" | "seasonal_diff"
14736 | "garch_step" | "egarch_step"
14737 | "realized_volatility" | "max_drawdown_arr"
14738 | "calmar_ratio" | "omega_ratio" | "kelly_criterion"
14739 | "var_historical" | "cvar_historical"
14740 | "graph_degree_distribution" | "graph_count_edges"
14741 | "graph_bipartite_match_simple" | "graph_count_triangles"
14742 | "graph_avg_clustering" | "graph_transitivity"
14743 | "graph_max_clique_brute" | "graph_independent_set_brute"
14744 | "graph_count_paths_length_k" | "graph_pagerank_simple"
14745 | "boole_rule" | "boole_int"
14747 | "gauss_legendre_5" | "gl5"
14748 | "gauss_kronrod_15" | "gk15"
14749
14750 | "midpoint_rule"
14751 | "adams_bashforth_4" | "ab4"
14752 | "heun_method" | "rk45_cash_karp" | "rkck"
14753 | "milne_pc" | "milne"
14754 | "modified_midpoint_ode" | "modmidpoint"
14755 | "backward_euler" | "implicit_euler"
14756 | "crank_nicolson_ode" | "cn_ode"
14757 | "brent_root" | "brent" | "ridders_root" | "ridders"
14758 | "steffensen_root" | "steffensen" | "halley_root" | "halley"
14759 | "householder_root" | "muller_root" | "muller"
14760 | "regula_falsi" | "false_position"
14761 | "secant_root" | "secant"
14762 | "anderson_step" | "aberth_step" | "inverse_quad_interp"
14763 | "lm_step" | "gradient_descent_step"
14764 | "nesterov_step" | "adagrad_step"
14765 | "cg_beta_pr" | "cg_beta_fr" | "bfgs_h_update_1d"
14766 | "wolfe_strong_q" | "dogleg_step"
14767 | "nelder_mead_reflect" | "nelder_mead_expand" | "nelder_mead_contract"
14768 | "sa_accept_prob" | "sa_boltzmann_temp" | "sa_cauchy_temp"
14769 | "sa_geometric_temp" | "acceptance_target"
14770 | "bs_call" | "blackscholes_call" | "bs_put" | "blackscholes_put"
14772 | "bs_theta_call" | "bs_rho_call"
14773 | "bachelier_call" | "black76_call"
14774 | "crr_american_call" | "crr_american_put" | "jr_european_call"
14775 | "trinomial_call" | "heston_price_simple" | "sabr_implied_vol"
14776 | "merton_jump_call" | "asian_call_mc" | "barrier_up_out_call"
14777 | "digital_call" | "lookback_call"
14778 | "macaulay_duration" | "forward_rate"
14779 | "discount_continuous" | "ytm_newton"
14780 | "vasicek_bond" | "cir_bond" | "hull_white_drift"
14781 | "cds_upfront" | "black_karasinski_drift" | "quanto_adjustment"
14782 | "fx_forward" | "garman_kohlhagen_call" | "margrabe" | "stulz_min_call"
14783 | "sharpe_annualized"
14784 | "jensen_alpha" | "modified_sharpe"
14785 | "ph_from_h" | "poh_from_oh" | "pka_from_ka"
14787 | "henderson_base"
14788 | "arrhenius_k" | "eyring_k"
14789 | "first_order_concentration" | "first_order_half_life"
14790 | "second_order_concentration" | "second_order_half_life"
14791 | "zero_order_concentration"
14792
14793 | "ideal_gas_n" | "redlich_kwong_p"
14794 | "compressibility_z"
14795 | "kc_from_rates" | "kp_from_kc" | "reaction_quotient" | "rxn_q"
14796 | "le_chatelier_dir"
14797 | "dg_from_k" | "k_from_dg" | "vant_hoff" | "clausius_clapeyron" | "antoine_p"
14798 | "emf_from_half_cells" | "faraday_mass_deposited"
14799 | "transmittance" | "ksp_from_concs"
14800 | "debye_huckel"
14801 | "cp_monatomic_ideal" | "cv_monatomic_ideal"
14802 | "heat_capacity_q" | "calorimeter_dt" | "enthalpy_reaction"
14803 | "avogadro_count" | "moles_from_mass"
14804 | "dilution_v2" | "raoult_law" | "bp_elevation" | "fp_depression"
14805 | "osmotic_pressure" | "rydberg_lambda" | "bohr_radius_n"
14806 | "bohr_energy_ev" | "photon_energy_freq" | "photon_energy_lambda"
14807 | "de_broglie"
14808 | "logistic_growth_step" | "logistic_growth_analytic"
14810 | "gompertz_growth_step" | "allee_growth_step"
14811 | "growth_rate_from_ratio"
14812 | "seir_step" | "seird_step" | "sis_step"
14813 | "r0_basic" | "rt_effective" | "herd_immunity_threshold" | "generation_time"
14814 | "inverse_simpson"
14815 | "pielou_evenness" | "margalef_richness" | "menhinick_richness"
14816 | "berger_parker" | "sorensen_dice"
14817 | "rao_quadratic_entropy"
14818 | "selection_step" | "nei_genetic_distance"
14819 | "effective_pop_size" | "carrying_capacity_from_data"
14820 | "petersen_estimator" | "chapman_estimator"
14821 | "lv_competition_step"
14822 | "holling_type1" | "holling_type2" | "holling_type3"
14823 | "leslie_step" | "net_reproductive_rate" | "generation_time_demo"
14824 | "finite_rate_lambda" | "kleibers_law" | "bergmann_adjust"
14825 | "q10" | "species_area" | "intrinsic_growth_rate"
14826 | "macarthur_wilson_immigration" | "macarthur_wilson_extinction"
14827 | "island_equilibrium"
14828 | "efield_point" | "epotential_point"
14830 | "capacitor_charge"
14831 | "ohm_voltage" | "power_vi" | "power_i2r"
14832
14833 | "capacitance_parallel_sum"
14834 | "bfield_wire" | "bfield_solenoid" | "lorentz_force_mag"
14835 | "faraday_emf"
14836 | "lc_frequency" | "lc_omega"
14837 | "rc_tau" | "rl_tau"
14838 | "poynting_magnitude" | "em_intensity" | "radiation_pressure"
14839 | "em_wavelength" | "em_frequency"
14840 | "snell_theta2"
14841 | "index_from_speed" | "fresnel_reflection_normal"
14842 | "fresnel_rs" | "fresnel_rp"
14843 | "lensmaker" | "thin_lens_v" | "mirror_equation_v"
14844 | "lens_magnification" | "diffraction_grating_angle"
14845 | "single_slit_min" | "rayleigh_resolution"
14846 | "lorentz_gamma"
14847 | "rel_momentum" | "rel_ke" | "rel_total_energy" | "rel_energy_pm"
14848 | "relativistic_doppler" | "rel_velocity_add"
14849
14850 | "wave_string_speed" | "sound_solid" | "sound_gas"
14851 | "doppler_classical" | "standing_wave_fundamental"
14852 | "open_pipe_harmonic" | "closed_pipe_harmonic"
14853 | "sound_db"
14854 | "alfven_speed"
14855 | "grav_time_dilation" | "grav_redshift"
14856 | "kosaraju_scc" | "bridges"
14858 | "max_flow_ek" | "min_cut_value" | "hopcroft_karp"
14859
14860 | "katz_centrality" | "hits_simple"
14861 | "pagerank_damped" | "cc_count" | "cc_labels"
14862 | "topological_sort_kahn" | "has_cycle_directed" | "has_cycle_undirected"
14863 | "diameter_bfs" | "radius_bfs"
14864 | "num_edges" | "k_coreness"
14865 | "greedy_coloring" | "chromatic_number_greedy"
14866 | "sum_degrees" | "avg_degree" | "max_degree"
14867 | "is_tree" | "girth"
14868 | "hamming_window" | "hann_window" | "blackman_window"
14870 | "blackman_harris_window" | "bartlett_window" | "welch_window"
14871 | "kaiser_window" | "tukey_window" | "gaussian_window"
14872 | "hilbert_envelope"
14873 | "biquad_step" | "biquad_lowpass_coeffs" | "biquad_highpass_coeffs"
14874 | "biquad_bandpass_coeffs" | "biquad_notch_coeffs" | "biquad_allpass_coeffs"
14875 | "biquad_peak_coeffs" | "biquad_lowshelf_coeffs" | "biquad_highshelf_coeffs"
14876 | "butterworth_prewarp" | "butterworth_order"
14877 | "fir_moving_average" | "fir_lowpass_design"
14878 | "spectrogram_simple"
14879 | "zero_pad" | "resample_nearest" | "resample_linear" | "quantize"
14880 | "mu_law_encode" | "mu_law_decode" | "a_law_encode" | "a_law_decode"
14881 | "chirp_linear"
14882 | "fnv1a_32" | "fnv1a_64" | "sdbm_hash"
14884 | "siphash24"
14885 | "pbkdf2_hmac_step" | "scrypt_round" | "bcrypt_cost_iters"
14886 | "argon2_block_mix" | "hkdf_expand_step"
14887 | "lfsr_galois_step" | "mt19937_temper" | "xorshift64" | "xorshift32"
14888 | "pcg32_step" | "lcg_numrec_step" | "splitmix64_step" | "wyhash_mix"
14889
14890 | "xor_cipher_byte"
14891 | "railfence_encrypt" | "beaufort" | "affine_encrypt" | "substitution_encrypt"
14892 | "letter_frequency" | "english_chi2" | "index_of_coincidence" | "kasiski_repeats"
14893 | "deterministic_prime" | "dh_shared" | "rsa_encrypt_simple"
14894 | "monobit_test" | "approximate_entropy"
14895 | "gini_impurity" | "entropy_bits" | "information_gain" | "gain_ratio"
14897 | "nb_gaussian_likelihood" | "nb_bernoulli_likelihood" | "nb_multinomial_log_likelihood"
14898 | "adaboost_alpha" | "hinge_loss" | "squared_hinge"
14899 | "logistic_loss"
14900 | "sigmoid_grad" | "tanh_grad"
14901 | "relu_grad"
14902 | "softsign" | "prelu" | "threshold_act"
14903 | "confusion_counts" | "mcc" | "f_beta" | "specificity"
14904 | "balanced_accuracy" | "cohen_kappa" | "brier_score" | "log_loss"
14905 | "tversky" | "mahalanobis_1d"
14906 | "log_softmax" | "one_hot" | "topk_indices"
14907 | "minmax_scale" | "zscore_norm" | "robust_scale"
14908 | "triangle_area_heron" | "triangle_area_pts"
14910 | "triangle_inradius" | "triangle_circumradius"
14911 | "regular_ngon_area" | "regular_ngon_inradius" | "regular_ngon_circumradius"
14912 | "n_ball_volume"
14913 | "cylinder_surface" | "cone_surface"
14914
14915 | "ellipsoid_volume" | "ellipsoid_surface_approx"
14916 | "dist_point_line_2d" | "dist_point_plane_3d" | "closest_pt_segment_2d"
14917 | "bbox_from_points"
14918 | "euclidean_distance_nd"
14919 | "hamming_distance_str"
14920 | "great_circle_law_of_cos"
14921 | "initial_bearing" | "midpoint_great_circle"
14922 | "shoelace_area" | "polygon_is_convex" | "convex_hull_jarvis"
14923 | "euler_characteristic" | "genus_from_euler"
14924 | "spherical_triangle_area" | "polygon_with_holes_area" | "picks_theorem"
14925 | "centroid_nd" | "covariance_matrix_pts" | "simplex_volume_3d"
14926 | "hyper2f1" | "hyper1f1" | "hyper0f1" | "pochhammer"
14928 | "mathieu_ce0" | "mathieu_se1" | "parabolic_d0" | "parabolic_d1"
14929 | "whittaker_m" | "struve_h0" | "struve_h1"
14930 | "lambert_w0" | "wright_omega"
14931 | "sinhc" | "cosh_minus1_over_x2"
14932 | "sine_integral_si" | "cosine_integral_ci" | "exp_integral_e1"
14933 | "dawson_function" | "owen_t"
14934 | "spherical_bessel_j0" | "spherical_bessel_j1"
14935 | "spherical_bessel_y0" | "spherical_bessel_y1"
14936 | "mod_sph_bessel_i0" | "mod_sph_bessel_i1" | "mod_sph_bessel_k0"
14937 | "coulomb_f0"
14938 | "polylog_li2" | "polylog_n"
14939
14940 | "ti2" | "clausen_cl2"
14941 | "bose_einstein_g" | "fermi_dirac_int"
14942 | "theta3" | "theta2"
14943 | "jacobi_sn_small_q" | "jacobi_cn_small_q" | "jacobi_dn_small_q"
14944 | "riemann_xi" | "bessel_jn_general" | "bessel_in_general"
14945 | "absolute_magnitude"
14947 | "pc_to_ly" | "ly_to_pc" | "pc_to_au" | "au_to_m"
14948 | "solar_mass_to_kg" | "solar_luminosity_to_w"
14949 | "hubble_distance_mpc" | "comoving_distance_approx" | "critical_density"
14950 | "et_freq_ratio" | "midi_to_hz" | "hz_to_midi" | "cents_between"
14951 | "just_intonation_ratio" | "pythagorean_ratio"
14952 | "beat_frequency" | "bpm_to_spb" | "note_name_to_midi"
14953 | "rgb_to_yiq" | "rgb_to_yuv601"
14954 | "srgb_to_xyz" | "xyz_to_lab" | "delta_e_94"
14955
14956
14957 | "feet_to_meters" | "meters_to_feet"
14958 | "lb_to_kg" | "kg_to_lb"
14959 | "mph_to_kmh" | "kmh_to_mph" | "mps_to_kmh" | "kmh_to_mps" | "knots_to_kmh"
14960 | "atm_to_pa" | "pa_to_atm" | "mmhg_to_pa"
14961 | "ev_to_joules" | "joules_to_ev" | "btu_to_joules" | "kwh_to_joules"
14962 | "bpm_to_midi_tick_us" | "iso226_phon_adjustment"
14963 | "db_to_amp" | "amp_to_db"
14964 | "roman_encode" | "roman_decode" | "number_to_english"
14965 | "hubble_lcdm" | "hubble_time" | "hubble_distance_si" | "critical_density_si"
14967 | "comoving_distance" | "angular_diameter_distance"
14968 | "lookback_time" | "age_at_z" | "scale_factor" | "redshift_from_a"
14969 | "omega_m_at_z" | "lcdm_eos" | "cpl_w" | "deceleration_q"
14970 | "schwarzschild_radius_kg" | "kerr_ergosphere_eq" | "kerr_horizon"
14971 | "bh_entropy" | "bh_evaporation_time"
14972 | "schwarzschild_isco" | "photon_sphere_radius"
14973 | "tidal_force" | "grav_dilation_factor" | "lense_thirring_omega"
14974 | "gw_strain_amplitude" | "chirp_mass" | "grav_binding_energy"
14975 | "roche_limit_rigid" | "roche_limit_fluid"
14976 | "lagrange_l1" | "sphere_of_influence"
14977 | "freefall_velocity_schwarzschild" | "einstein_ring_radius"
14978 | "microlensing_magnification" | "cosmic_distance_modulus_si"
14979 | "cmb_temperature" | "cmb_temperature_at_z"
14980 | "stefan_boltzmann_si" | "planck_spectral_radiance"
14981 | "schwarzschild_g_tt" | "schwarzschild_g_rr" | "kretschmann_schwarzschild"
14982 | "hill_velocity" | "vacuum_energy_density"
14983 | "sound_horizon_recomb" | "bao_scale_today" | "sigma8_default"
14984 | "lensing_convergence" | "sigma_crit"
14985 | "perihelion_precession" | "shapiro_delay" | "light_deflection_angle"
14986 | "tov_mass_limit"
14987 | "main_sequence_lifetime" | "schwarzschild_freefall_time"
14988 | "friedmann_density_total" | "cosmological_constant"
14989
14990 | "planck_energy"
14991 | "pure_state_density" | "purity"
14993 | "linear_entropy" | "quantum_mutual_info"
14994 | "eof_from_concurrence"
14995 | "bell_state_index" | "chsh_expectation" | "tsirelson_bound"
14996 | "pauli_real_part" | "pauli_y_imag"
14997 | "bloch_to_density_real" | "bloch_purity_check"
14998 | "fidelity_pure_real" | "l1_coherence" | "relative_entropy_coherence"
14999 | "kraus_apply" | "bit_flip_prob" | "phase_flip_prob"
15000 | "depolarizing_density_2x2" | "amplitude_damping_excited"
15001 | "quantum_fisher_info" | "cramer_rao_bound" | "squeezing_db" | "heisenberg_min"
15002 | "coherent_mean_photons" | "thermal_mean_photons" | "poisson_photon_pmf"
15003 | "bose_einstein_pmf" | "mandel_q" | "g2_zero"
15004 | "free_particle_energy" | "infinite_well_energy" | "harmonic_oscillator_energy"
15005 | "hydrogen_energy_n" | "stark_shift_linear"
15006 | "zeeman_energy" | "larmor_frequency" | "rabi_frequency"
15007 | "schrodinger_step_real" | "probability_density" | "state_norm" | "state_normalize"
15008 | "quantum_variance" | "spin_casimir"
15009 | "cg_simple" | "wigner_3j_bound" | "qho_ground_state"
15010 | "tunneling_prob" | "gamow_factor" | "compton_wavelength" | "uncertainty_position"
15011 | "berry_phase_spin_half" | "zeno_survival" | "decoherence_time"
15012 | "ramsey_visibility" | "fermi_golden_rule"
15013 | "needleman_wunsch_score" | "smith_waterman_score" | "pam250_score"
15015 | "tanimoto_bits" | "translate_dna" | "transcribe_dna_rna" | "reverse_transcribe"
15016 | "at_content" | "tm_wallace" | "tm_marmur" | "codon_adaptation_index"
15017 | "kmer_jaccard" | "sequence_shannon_info" | "pwm_score"
15018 | "msa_column_entropy" | "seq_logo_information"
15019 | "damerau_levenshtein" | "lcs_length"
15020 | "hirschberg_lcs_length" | "common_kmers"
15021 | "jukes_cantor_distance" | "kimura_2p_distance" | "felsenstein_step"
15022 | "branch_length_substitutions" | "num_unrooted_trees" | "bayes_posterior"
15023 | "hw_expected_counts" | "allele_frequency" | "ld_d" | "ld_r_squared"
15024 | "heterozygosity" | "ne_from_variance"
15025 | "expected_coverage" | "lander_waterman_gaps"
15026 | "bh_adjusted_p" | "zscore_count"
15027 | "go_enrichment_p" | "blosum45_score"
15028 | "henikoff_weight" | "hamming_protein" | "codon_usage_variance"
15029 | "dnds_ratio" | "mutation_rate" | "tajimas_d" | "wattersons_theta"
15030 | "coalescent_expected_time" | "coalescent_tree_length" | "nm_from_fst"
15031 | "bdf1_step" | "bdf2_step" | "bdf3_step" | "bdf4_step" | "bdf5_step" | "bdf6_step"
15033 | "ab1_step" | "ab2_step" | "ab3_step"
15034 | "am2_step" | "am3_step" | "am4_step"
15035 | "ros2_step" | "imex_euler_step" | "symplectic_euler_step"
15036 | "leapfrog_step" | "stormer_verlet_step"
15037 | "rk4_single" | "dopri5_combine" | "rkf45_error"
15038 | "lobatto_iiia_2" | "lobatto_iiic_3" | "gauss_irk_2_stage" | "magnus_1st"
15039 | "euler_lte" | "trapezoidal_lte" | "pi_step_size"
15040 | "stiffness_ratio" | "spectral_radius"
15041 | "heun_euler_step" | "bogacki_shampine_step" | "verner_8_combine"
15042 | "rk_combine" | "ab_coeff_sum"
15043 | "newmark_beta_step" | "wilson_theta_step"
15044 | "strang_split" | "lie_split"
15045 | "exp_euler_step" | "etd_rk2" | "dde_euler_step"
15046 | "em_step" | "milstein_step" | "heun_sde_step" | "stratonovich_correction"
15047 | "predictor_corrector" | "numerical_jacobian_col"
15048 | "cn_coefficient" | "imex_theta_split" | "bulirsch_stoer_step"
15049 | "cfl_number" | "diffusion_stability"
15050 | "lax_friedrichs_flux" | "lax_wendroff_flux"
15051 | "van_leer_limiter" | "minmod_limiter" | "superbee_limiter" | "mc_limiter"
15052 | "pollard_p_minus_1" | "fermat_factor"
15054 | "trial_smallest_factor" | "bsgs_discrete_log"
15055 | "mertens" | "liouville"
15056 | "is_b_smooth" | "primorial_n"
15057 | "pseudoprime_base2" | "strong_pseudoprime"
15058 | "aks_witness_count" | "qs_relation"
15059 | "index_calculus_naive" | "lll_2x2_step" | "coppersmith_bound"
15060 | "shor_period_prob" | "rsa_d_from_e" | "dh_secret"
15061 | "elgamal_encrypt" | "ecc_point_double" | "continued_fraction_sqrt"
15062 | "pell_fundamental" | "sum_two_squares" | "class_number_bound"
15063 | "smith_normal_2x2_step" | "regulator_naive"
15064 | "power_residue_check" | "wieferich_check" | "wilson_test"
15065 | "goldbach_pair" | "english_likeness" | "xor_break_singlebyte"
15066 | "bit_reverse_64"
15067 | "gf256_multiply" | "hash_combine"
15068 | "arch_lm_test" | "breusch_pagan_test" | "white_robust_se"
15070 | "newey_west_se" | "hansen_j_test" | "gmm_moment_condition"
15071 | "hausman_test" | "breusch_godfrey_test" | "box_pierce_test"
15072 | "adf_test_stat" | "pp_test_stat" | "kpss_test_stat"
15073 | "dickey_fuller_critical" | "engle_granger_step"
15074 | "johansen_trace_step" | "vecm_alpha_beta"
15075 | "panel_within_estimator" | "panel_between_estimator"
15076 | "panel_random_effects" | "arellano_bond_step"
15077 | "ols_estimator" | "ols_residual_variance" | "ols_r_squared"
15078 | "ols_adjusted_r2" | "akaike_info_crit" | "bayesian_info_crit"
15079 | "hannan_quinn_ic" | "f_statistic_pooled" | "breusch_pagan_lm"
15080 | "ramsey_reset_test" | "chow_test_stat" | "white_test_stat"
15081 | "goldfeld_quandt" | "wald_test_stat" | "score_test_stat"
15082 | "likelihood_ratio_test" | "two_sls_iv" | "iv_estimator"
15083 | "mle_normal_log_lik" | "mle_exponential_log_lik"
15084 | "mle_poisson_log_lik" | "gmm_moment_function"
15085 | "pooling_test_stat" | "heteroskedasticity_test"
15086 | "robust_se_huber_white" | "bootstrap_se_estimate"
15087 | "heckman_correction" | "tobit_log_likelihood"
15088 | "probit_log_likelihood" | "logit_log_likelihood"
15089 | "multinomial_logit_prob" | "ordered_probit_threshold"
15090 | "panel_var_step" | "impulse_response_step"
15091 | "variance_decomposition" | "granger_causality_chi2"
15092 | "cointegration_residual" | "error_correction_step"
15093 | "random_walk_innovation" | "random_walk_drift_step"
15094 | "ar_model_likelihood" | "ma_model_likelihood"
15095 | "arma_model_innovation"
15096 | "euler_char_complex" | "betti_zero" | "betti_one" | "betti_two"
15098 | "genus_surface" | "chern_first_2d" | "genus_curve_arith"
15099 | "genus_curve_geo" | "hodge_diamond_value" | "poincare_duality"
15100 | "fundamental_group_zn" | "homology_rank" | "cohomology_rank"
15101 | "homotopy_group_sphere_pi" | "mapping_class_torus"
15102 | "linking_number_two" | "writhe_polygon" | "torsion_coefficient"
15103 | "simplex_volume_n" | "simplicial_volume" | "nerve_complex_count"
15104 | "cech_zero_cohomology" | "de_rham_zero"
15105 | "poincare_polynomial_eval" | "chromatic_homology_rank"
15106 | "khovanov_q_grading" | "hochschild_zero" | "cyclic_homology_step"
15107 | "group_cohomology_dim" | "group_homology_dim"
15108 | "abelianization_quotient" | "free_group_rank_lower"
15109 | "nilpotency_class_lower" | "solvable_length_upper"
15110 | "schreier_index" | "todd_genus_eval" | "hirzebruch_signature"
15111 | "chern_simons_action" | "gauss_bonnet_total"
15112 | "seifert_genus_lower" | "alexander_polynomial_at_one"
15113 | "jones_polynomial_at_minus_one" | "jones_polynomial_at_i"
15114 | "homfly_evaluation" | "kauffman_bracket_eval"
15115 | "cabling_pair_signature" | "seifert_form_2x2"
15116 | "turaev_alexander_step" | "v_polynomial_eval"
15117 | "polynomial_jones_skein" | "delta_complex_count"
15118 | "poset_zeta_two" | "mobius_poset_two" | "mobius_function_pair"
15119 | "mobius_inversion_step" | "incidence_algebra_dim"
15120 | "quiver_path_count" | "representation_dim_step"
15121 | "weyl_group_order" | "root_system_count"
15122 | "cartan_determinant_a2" | "cartan_matrix_b2"
15123 | "killing_form_su2" | "casimir_eigenvalue_su2"
15124 | "universal_enveloping_dim" | "verma_character_step"
15125 | "plethystic_substitution_value" | "schur_polynomial_eval"
15126 | "hall_inner_product_two" | "plactic_class_size"
15127 | "robinson_schensted_pair" | "yamanouchi_word_count"
15128 | "rsk_size" | "character_su2" | "character_sun"
15129 | "quantum_dimension_su2" | "quantum_dimension_q"
15130 | "fusion_rule_su2_step" | "modular_data_s_value"
15131 | "modular_data_t_value" | "verlinde_count_step"
15132 | "quantum_invariant_eval" | "operad_count_two"
15133 | "moduli_dimension_curves" | "hodge_polynomial_eval"
15134 | "mirror_symmetry_check" | "gromov_witten_invariant"
15135 | "donaldson_invariant" | "seiberg_witten_value"
15136 | "floer_homology_rank" | "khovanov_rasmussen_s"
15137 | "ozsvath_szabo_tau" | "heegaard_genus_lower"
15138 | "fintushel_stern_step" | "bauer_furuta_step"
15139 | "geometric_intersection_number"
15140 | "algebraic_intersection_number"
15141 | "nernst_potential_full" | "electrode_potential_step"
15143 | "exchange_current_density" | "butler_volmer_current"
15144 | "tafel_anodic_current" | "tafel_cathodic_current"
15145 | "mass_transport_overpotential" | "limiting_current_density"
15146 | "diffusion_layer_thickness" | "faradaic_efficiency"
15147 | "coulombic_efficiency_cell" | "energy_efficiency_cell"
15148 | "voltaic_efficiency" | "charge_capacity_battery"
15149 | "energy_density_battery" | "power_density_battery"
15150 | "specific_capacity_active" | "columbic_capacity_lihalfcell"
15151 | "ragone_point" | "peukert_capacity" | "peukert_exponent_fit"
15152 | "shepherd_voltage_step" | "nernst_planck_flux"
15153 | "debye_length_electrolyte" | "debye_huckel_activity"
15154 | "gouy_chapman_potential" | "stern_layer_capacitance"
15155 | "double_layer_capacitance" | "helmholtz_capacitance"
15156 | "zeta_potential_estimate" | "electroosmotic_velocity"
15157 | "hagen_poiseuille_eo" | "diffuse_layer_thickness"
15158 | "poisson_boltzmann_step" | "linearized_pb_step"
15159 | "electrochem_impedance_z" | "randles_circuit_z"
15160 | "warburg_impedance" | "cole_cole_eis" | "nyquist_phase"
15161 | "charge_transfer_resistance" | "solution_resistance_estimate"
15162 | "ionic_conductivity_arrhenius" | "nernst_einstein_diffusivity"
15163 | "walden_product" | "kohlrausch_law"
15164 | "onsager_relation_two_species" | "trasatti_voltammetry_charge"
15165 | "randles_sevcik_peak" | "levich_current_rde"
15166 | "koutecky_levich_intercept" | "mott_schottky_capacitance"
15167 | "flat_band_potential" | "schottky_barrier_height"
15168 | "photocurrent_density" | "quantum_efficiency_photo"
15169 | "overall_efficiency_pec" | "fuel_cell_polarization"
15170 | "electrolyzer_voltage" | "faraday_efficiency_h2"
15171 | "overpotential_oer" | "overpotential_her"
15172 | "electrocrystallization_step" | "nucleation_rate_constant"
15173 | "metal_corrosion_rate" | "pourbaix_line_value"
15174 | "mixed_potential_step" | "electrochemiluminescence_yield"
15175 | "solid_electrolyte_capacity" | "ionic_liquid_viscosity_step"
15176 | "lithium_ion_diffusivity" | "soc_estimate_coulomb"
15177 | "soh_capacity_fade" | "ocv_lithium_ion_step"
15178 | "state_of_charge_kalman" | "thermal_runaway_threshold"
15179 | "joule_heating_battery" | "calorimetric_heat_battery"
15180 | "abuse_test_voltage" | "swelling_strain_step"
15181 | "sei_resistance_growth" | "binder_content_optimal"
15182 | "porosity_active_layer" | "tortuosity_estimate_bruggeman"
15183 | "electrolyte_decomposition_temp" | "gibbs_thomson_undercooling"
15184 | "nernst_diffusion_layer" | "diff_coeff_aqueous_estimate"
15185 | "salt_activity_coefficient" | "mean_activity_coeff_pitzer"
15186 | "osmotic_coefficient_pitzer" | "debye_huckel_screening_factor"
15187 | "ph_at_isoelectric" | "buffer_capacity_acid_base"
15188 | "henderson_hasselbalch_solve" | "titration_endpoint_index"
15189 | "tensor_contract_two" | "tensor_outer_two" | "tensor_trace_index"
15191 | "tensor_symmetrize_two" | "tensor_antisymmetrize_two"
15192 | "levi_civita_three" | "levi_civita_four"
15193 | "kronecker_three" | "kronecker_four"
15194 | "metric_minkowski_eta_step" | "metric_schwarzschild_step"
15195 | "metric_kerr_step_simple" | "metric_frw_lapse"
15196 | "christoffel_first_kind_step" | "christoffel_second_kind_step"
15197 | "riemann_tensor_step_zero" | "riemann_curvature_normal_form"
15198 | "ricci_tensor_step_zero" | "scalar_curvature_step"
15199 | "einstein_tensor_step" | "weyl_tensor_step_zero"
15200 | "schouten_tensor_step" | "geodesic_equation_step_zero"
15201 | "parallel_transport_step" | "covariant_derivative_step"
15202 | "christoffel_symbol_normalize" | "ricci_identity_step"
15203 | "bianchi_first_identity_check" | "bianchi_second_identity_check"
15204 | "killing_vector_lie_step" | "lie_derivative_scalar_step"
15205 | "lie_derivative_vector_step" | "exterior_derivative_one_form"
15206 | "hodge_star_one_form" | "codifferential_step"
15207 | "laplace_de_rham_step" | "volume_form_riemannian"
15208 | "hodge_inner_product_one" | "sectional_curvature_two_plane"
15209 | "gauss_codazzi_step" | "mainardi_codazzi_step"
15210 | "weingarten_map_step" | "shape_operator_eig"
15211 | "mean_curvature_step" | "gaussian_curvature_step"
15212 | "extrinsic_principal_curv" | "intrinsic_principal_curv"
15213 | "geodesic_curvature_step" | "darboux_frame_step"
15214 | "fermi_normal_step" | "synge_world_function"
15215 | "raychaudhuri_step" | "expansion_scalar_step"
15216 | "shear_tensor_step" | "twist_tensor_step"
15217 | "optical_scalars_step" | "peeling_step_psi4"
15218 | "ads_metric_step" | "de_sitter_metric_step"
15219 | "warped_product_step_zero" | "kaluza_klein_step"
15220 | "brans_dicke_step" | "horndeski_step"
15221 | "einstein_dilaton_step" | "gauss_bonnet_term_2d"
15222 | "chern_pontryagin_4d_step" | "adm_mass_step"
15223 | "komar_mass_step" | "bondi_mass_step"
15224 | "brown_york_quasilocal" | "isolated_horizon_charge"
15225 | "trapped_surface_check" | "apparent_horizon_step"
15226 | "event_horizon_check" | "cosmological_constant_term"
15227 | "de_sitter_radius_step" | "anti_de_sitter_radius_step"
15228 | "penrose_diagram_factor" | "conformal_compactification_step"
15229 | "schwarzschild_kruskal_step" | "gullstrand_painleve_step"
15230 | "kerr_newman_charge_term" | "boyer_lindquist_step"
15231 | "hartle_thorne_metric" | "oppenheimer_volkoff_step"
15232 | "post_newtonian_step" | "shapiro_delay_step"
15233 | "mercury_perihelion_advance"
15234 | "gravitational_wave_quadrupole"
15235 | "plus_polarization_amp" | "cross_polarization_amp"
15236 | "chirp_mass_inspiral_step" | "isco_radius_kerr_step"
15237 | "spin_orbit_coupling_term" | "spin_spin_coupling_term"
15238 | "hawking_area_increase" | "unruh_temperature_full"
15239 | "bekenstein_entropy_step" | "holographic_entanglement_step"
15240 | "ryu_takayanagi_step" | "swampland_distance_check"
15241 | "conditional_entropy_step" | "joint_entropy_step"
15243 | "relative_entropy_kl" | "mutual_information_step"
15244 | "chain_rule_entropy" | "fano_inequality_bound"
15245 | "data_processing_inequality" | "arithmetic_coding_interval"
15246 | "range_coding_step" | "golomb_rice_code"
15247 | "elias_gamma_code" | "elias_delta_code" | "exp_golomb_code"
15248 | "fibonacci_code" | "shannon_fano_elias_code"
15249 | "huffman_balanced_step" | "arithmetic_decode_interval"
15250 | "range_decode_step" | "universal_code_length"
15251 | "ziv_lempel_estimate" | "lz77_match_length"
15252 | "lz78_dictionary_growth" | "lzw_step_dict"
15253 | "ppm_predict_prob" | "deflate_huffman_lit"
15254 | "brotli_distance_code_count" | "zstd_window_size_log"
15255 | "mpeg_quant_value" | "jpeg_zig_zag_index"
15256 | "jpeg_dct_8x8_quant" | "hadamard_walsh_transform_step"
15257 | "karhunen_loeve_step" | "discrete_haar_step"
15258 | "db4_wavelet_step" | "biorthogonal_step"
15259 | "beylkin_wavelet_step" | "coiflet_wavelet_step"
15260 | "mallat_pyramid_step" | "threshold_soft_value"
15261 | "threshold_hard_value" | "median_filter_window"
15262 | "mean_filter_window" | "gaussian_filter_window"
15263 | "unsharp_mask_step" | "sobel_kernel_value"
15264 | "prewitt_kernel_value" | "roberts_kernel_value"
15265 | "laplacian_kernel_value" | "canny_threshold_step"
15266 | "hough_accumulator_step" | "ransac_iteration_count"
15267 | "optical_flow_lk_step" | "horn_schunck_step"
15268 | "kalman_predict_state" | "kalman_update_state"
15269 | "particle_filter_resample" | "unscented_sigma_point"
15270 | "ekf_jacobian_step" | "markov_decision_value"
15271 | "bellman_equation_step" | "q_learning_update"
15272 | "policy_iteration_step" | "value_iteration_step"
15273 | "sarsa_update" | "double_q_learning_step"
15274 | "ucb1_action_value" | "thompson_sample_beta"
15275 | "boltzmann_softmax_action" | "explore_exploit_epsilon"
15276 | "montecarlo_returns_step" | "td_zero_update"
15277 | "td_lambda_update" | "gradient_temporal_diff"
15278 | "deep_q_target" | "ddpg_critic_loss_step"
15279 | "ppo_clip_term" | "trpo_kl_constraint"
15280 | "a3c_advantage_step" | "ppo_advantage_step"
15281 | "gae_advantage_step" | "generalized_advantage"
15282 | "information_bottleneck_step" | "free_energy_principle"
15283 | "fisher_info_metric" | "kullback_jensen_div"
15284 | "hellinger_distance_step" | "total_variation_distance"
15285 | "bhattacharyya_coefficient" | "wasserstein_dist_emp"
15286 | "chisquare_metric" | "hellinger_kernel"
15287 | "jensen_shannon_div" | "renyi_divergence_step"
15288 | "amari_alpha_div" | "csiszar_phi_div"
15289 | "sinkhorn_iteration_step" | "sliced_wasserstein"
15290 | "gromov_wasserstein_step" | "spectral_signature_match"
15291 | "mfcc_coeff_step" | "chroma_feature_step"
15292 | "tsp_lower_bound_mst" | "tsp_held_karp_step"
15294 | "christofides_ratio_bound" | "two_opt_swap_delta"
15295 | "or_opt_delta" | "three_opt_delta" | "lin_kernighan_step"
15296 | "nearest_neighbor_tour_step" | "greedy_edge_tour"
15297 | "nearest_insertion_step" | "farthest_insertion_step"
15298 | "cheapest_insertion_step" | "max_flow_ford_fulkerson_step"
15299 | "edmonds_karp_step" | "dinic_blocking_flow"
15300 | "push_relabel_step" | "boykov_kolmogorov_step"
15301 | "mincut_stoer_wagner" | "gomory_hu_step"
15302 | "karger_contract_edge" | "karger_min_cut_count"
15303 | "maximum_bipartite_matching" | "hopcroft_karp_phase"
15304 | "blossom_match_step" | "weighted_match_kuhn_step"
15305 | "hungarian_method_step" | "ap_jonker_volgenant_step"
15306 | "assignment_lower_bound" | "job_shop_makespan_lower"
15307 | "flow_shop_johnson_step" | "parallel_machine_lpt"
15308 | "parallel_machine_spt" | "list_scheduling_step"
15309 | "graham_2approx_bound" | "chc_bound_makespan"
15310 | "bin_packing_first_fit" | "bin_packing_best_fit"
15311 | "bin_packing_next_fit" | "bin_packing_lower_bound_l1"
15312 | "multidim_packing_step" | "knapsack_01_dp_value"
15313 | "knapsack_unbounded_dp" | "knapsack_fractional_step"
15314 | "knapsack_branch_bound" | "knapsack_lp_relaxation"
15315 | "multi_knapsack_step" | "quadratic_assignment_step"
15316 | "qap_lower_bound" | "graph_coloring_dsatur_step"
15317 | "graph_coloring_welsh_powell"
15318 | "graph_coloring_brooks_bound" | "graph_coloring_lp_bound"
15319 | "fractional_chromatic_lower" | "list_coloring_step"
15320 | "edge_coloring_vizing_step" | "clique_number_lower"
15321 | "independence_number_upper" | "vertex_cover_lp_round"
15322 | "dominating_set_greedy_step" | "dominating_set_lp_bound"
15323 | "set_cover_greedy_step" | "set_cover_lp_round"
15324 | "hitting_set_greedy" | "weighted_set_cover_step"
15325 | "matroid_greedy_step" | "matroid_intersection_step"
15326 | "submodular_greedy_step" | "submodular_curvature_bound"
15327 | "nemhauser_wolsey_bound" | "lp_relax_round"
15328 | "branch_and_bound_step" | "cutting_plane_step"
15329 | "gomory_cut_step" | "chvatal_gomory_cut"
15330 | "mixed_integer_round_up" | "mixed_integer_round_down"
15331 | "sos_constraint_check" | "column_generation_step"
15332 | "benders_decomposition_step" | "dantzig_wolfe_step"
15333 | "lagrangian_relax_step" | "lagrangian_dual_step"
15334 | "subgradient_step_size" | "nonlinear_dual_step"
15335 | "augmented_lagrangian_step" | "admm_primal_step"
15336 | "admm_dual_step" | "proximal_gradient_step"
15337 | "nesterov_accelerate_step" | "fista_step" | "ista_step"
15338 | "mirror_descent_step" | "frank_wolfe_step"
15339 | "conditional_gradient_step" | "greedy_set_cover_round"
15340 | "local_search_swap_step" | "tabu_search_move_score"
15341 | "simulated_annealing_step" | "genetic_crossover_one_point"
15342 | "mutation_bit_flip_prob" | "roulette_wheel_select_index"
15343 | "stefan_boltzmann_radiation" | "emissivity_grey_body"
15345 | "albedo_blackbody_balance" | "solar_constant_at_distance"
15346 | "total_solar_irradiance_step" | "absorbed_short_wave"
15347 | "emitted_long_wave" | "clausius_clapeyron_full"
15348 | "relative_humidity_step" | "dewpoint_temperature_full"
15349 | "wet_bulb_potential" | "virtual_temperature_full"
15350 | "density_altitude_full" | "geopotential_height_full"
15351 | "geometric_height_full" | "adiabatic_lapse_rate_dry"
15352 | "adiabatic_lapse_rate_moist" | "brunt_vaisala_full"
15353 | "richardson_number_step" | "gradient_richardson_full"
15354 | "flux_richardson_full" | "turbulent_kinetic_energy_step"
15355 | "mixing_length_prandtl" | "monin_obukhov_length"
15356 | "similarity_function_phi" | "log_law_wind_profile"
15357 | "power_law_wind_profile" | "ekman_layer_depth"
15358 | "ekman_pumping_step" | "geostrophic_wind_step"
15359 | "gradient_wind_step" | "thermal_wind_step"
15360 | "quasi_geostrophic_omega" | "omega_equation_step"
15361 | "potential_temperature_step" | "equivalent_potential_temp"
15362 | "saturation_equivalent_pt" | "ipv_potential_vorticity"
15363 | "ertel_pv_step" | "absolute_vorticity_step"
15364 | "relative_vorticity_step" | "divergence_omega_step"
15365 | "streamfunction_step" | "velocity_potential_step"
15366 | "helmholtz_decomp_step" | "courant_friedrichs_lewy"
15367 | "peclet_number_step" | "prandtl_number_step"
15368 | "reynolds_full_number" | "schmidt_number_step"
15369 | "sherwood_number_step" | "nusselt_full_number"
15370 | "grashof_number_step" | "rayleigh_number_step"
15371 | "weber_number_step" | "froude_number_step"
15372 | "strouhal_full" | "mach_full_step"
15373 | "biot_number_step" | "fourier_number_step"
15374 | "turbulence_intensity_step" | "hurst_exponent_estimate"
15375 | "detrended_fluct_alpha" | "power_spectrum_slope"
15376 | "spectral_kappa_minus53" | "batchelor_scale_step"
15377 | "kolmogorov_microscale" | "taylor_microscale_step"
15378 | "integral_length_scale" | "turbulent_dissipation_eps"
15379 | "isotropic_relation_check" | "sst_anomaly_step"
15380 | "enso_index_step" | "amo_index_step" | "nao_index_step"
15381 | "soi_oscillation_index" | "pdo_index_step" | "mjo_phase_step"
15382 | "walker_circulation_step" | "hadley_cell_max_lat"
15383 | "ferrel_cell_step" | "itcz_position_lat" | "trade_wind_speed"
15384 | "westerlies_jet_speed" | "polar_vortex_radius"
15385 | "arctic_oscillation_step" | "indian_monsoon_index"
15386 | "african_monsoon_index" | "qbo_oscillation_step"
15387 | "solar_cycle_phase" | "sunspot_relative_number"
15388 | "geomagnetic_kp_index" | "ozone_dobson_total"
15389 | "chlorine_radical_decay" | "montreal_protocol_track"
15390 | "co2_growth_rate_step" | "methane_growth_rate"
15391 | "aerosol_optical_depth" | "ice_age_milankovitch"
15392 | "greenhouse_forcing_step"
15393 | "game_two_player_value" | "nash_equilibrium_pair"
15395 | "mixed_strategy_value" | "zero_sum_minmax"
15396 | "saddle_point_check" | "correlated_equilibrium_value"
15397 | "shapley_value_two_step" | "banzhaf_index_two"
15398 | "nucleolus_lp_step" | "core_membership_check"
15399 | "imputation_efficient_check" | "imputation_individual_rational"
15400 | "prisoners_dilemma_payoff" | "matching_pennies_payoff"
15401 | "chicken_game_payoff" | "stag_hunt_payoff"
15402 | "battle_sexes_payoff" | "public_goods_game_payoff"
15403 | "tragedy_commons_metric" | "ultimatum_acceptance_prob"
15404 | "dictator_game_share" | "trust_game_repayment"
15405 | "cooperative_game_value" | "characteristic_function"
15406 | "bargaining_set_check" | "kalai_smorodinsky_step"
15407 | "nash_bargaining_solution" | "egalitarian_solution"
15408 | "utilitarian_solution" | "social_welfare_sum"
15409 | "arrow_impossibility_check" | "gibbard_satterthwaite_check"
15410 | "borda_count_step" | "condorcet_winner_check"
15411 | "plurality_winner_step" | "kemeny_score_step"
15412 | "dodgson_swap_count" | "coombs_runoff_step"
15413 | "single_transferable_vote" | "range_voting_score"
15414 | "approval_voting_max" | "schulze_method_step"
15415 | "copeland_score_step" | "black_method_winner"
15416 | "median_voter_step" | "hotelling_location_step"
15417 | "arrow_pareto_check" | "fair_division_envy_free"
15418 | "proportional_share" | "maximin_share"
15419 | "egalitarian_split" | "nash_social_welfare"
15420 | "divisible_goods_proportional" | "indivisible_envy_free_check"
15421 | "adjusted_winner_pct" | "sealed_bid_first_price"
15422 | "sealed_bid_second_price" | "english_auction_step"
15423 | "dutch_auction_step" | "all_pay_auction_step"
15424 | "vcg_payment_step" | "revenue_equivalence_check"
15425 | "truthful_mechanism_check" | "incentive_compatibility_check"
15426 | "mechanism_design_obj" | "double_auction_step"
15427 | "combinatorial_auction_step" | "posted_price_offer_accept"
15428 | "matching_market_step" | "deferred_acceptance_step"
15429 | "boston_mechanism_step" | "top_trading_cycles_step"
15430 | "school_choice_match" | "roommate_match_step"
15431 | "network_formation_step" | "coordination_game_payoff"
15432 | "evolutionary_stable_strategy" | "replicator_dynamics_step"
15433 | "hawk_dove_payoff" | "fictitious_play_step"
15434 | "best_response_dynamic" | "quantal_response_logit"
15435 | "level_k_step" | "cognitive_hierarchy_step"
15436 | "sequential_eq_check" | "subgame_perfect_eq"
15437 | "stackelberg_step" | "cournot_quantity_step"
15438 | "bertrand_price_step" | "hotelling_price_step"
15439 | "collusion_payoff_step" | "folk_theorem_value"
15440 | "repeated_game_avg_payoff" | "discount_factor_step"
15441 | "trigger_strategy_payoff" | "grim_trigger_step"
15442 | "tit_for_tat_step" | "prisoners_repeated_eq"
15443 | "mertens_zamir_step" | "ex_post_value_check"
15444 | "ex_ante_value_check" | "common_knowledge_iterations"
15445 | "cas_simplify_term" | "cas_expand_two_terms"
15447 | "cas_factor_quadratic" | "cas_partial_fraction_simple"
15448 | "cas_polynomial_gcd_step" | "cas_polynomial_div_step"
15449 | "cas_lagrange_interpolate" | "cas_chebyshev_eval"
15450 | "cas_legendre_eval" | "cas_hermite_eval"
15451 | "cas_laguerre_eval" | "cas_jacobi_eval"
15452 | "cas_gegenbauer_eval" | "cas_taylor_coefficient"
15453 | "cas_padé_diagonal" | "cas_continued_fraction_step"
15454 | "cas_resultant_two" | "cas_subresultant_two"
15455 | "cas_groebner_lt_step" | "cas_buchberger_step"
15456 | "cas_macaulay_matrix_step" | "cas_modular_inverse"
15457 | "cas_extended_euclid_step" | "cas_smith_normal_step"
15458 | "cas_hermite_normal_step" | "cas_radical_simplify"
15459 | "cas_minimal_polynomial" | "cas_gcd_polynomial_step"
15460 | "cas_resultant_x_y" | "cas_solve_linear"
15461 | "cas_solve_quadratic" | "cas_solve_cubic"
15462 | "cas_solve_quartic" | "cas_solve_polynomial_n"
15463 | "cas_root_isolate_step" | "cas_sturm_sequence_step"
15464 | "cas_descartes_rule_count" | "cas_companion_matrix_root"
15465 | "cas_polynomial_roots_kahan"
15466 | "cas_eigenvalue_inverse_iteration" | "cas_qr_iteration_step"
15467 | "cas_jacobi_eigen_step" | "cas_lanczos_iteration_step"
15468 | "cas_arnoldi_iteration_step" | "cas_givens_rotation_apply"
15469 | "cas_householder_reflection" | "cas_modified_gram_schmidt"
15470 | "cas_classical_gram_schmidt" | "cas_rank_revealing_qr"
15471 | "cas_pivoted_lu_step" | "cas_block_lu_step"
15472 | "cas_cholesky_step" | "cas_modified_cholesky"
15473 | "cas_ldlt_step" | "cas_bunch_kaufman_step"
15474 | "cas_woodbury_identity" | "cas_matrix_pencil_step"
15475 | "cas_generalized_eigen" | "cas_singular_value_step"
15476 | "cas_truncated_svd_value" | "cas_pseudoinverse_step"
15477 | "cas_polar_decomposition" | "cas_schur_decomposition_step"
15478 | "cas_quasi_triangular" | "cas_riccati_continuous_step"
15479 | "cas_riccati_discrete_step" | "cas_lyapunov_continuous_step"
15480 | "cas_lyapunov_discrete_step" | "cas_sylvester_equation_step"
15481 | "cas_kronecker_product_step" | "cas_vec_operator_step"
15482 | "cas_matrix_function_step" | "cas_matrix_log_step"
15483 | "cas_matrix_exp_pade" | "cas_matrix_sqrt_step"
15484 | "cas_drazin_inverse_step" | "cas_moore_penrose_step"
15485 | "cas_least_squares_solve" | "cas_total_least_squares"
15486 | "cas_constrained_ls_step" | "cas_truncated_lsq"
15487 | "cas_regularized_lsq_tikhonov" | "cas_basis_pursuit_step"
15488 | "cas_lasso_soft_threshold" | "cas_elastic_net_step"
15489 | "cas_omp_step" | "cas_iht_iteration"
15490 | "cas_cosamp_step" | "cas_admm_lasso_step"
15491 | "cas_proximal_l1_step" | "cas_proximal_l2_step"
15492 | "cas_proximal_l_inf_step" | "cas_indicator_simplex_proj"
15493 | "cas_proj_l1_ball" | "cas_proj_l2_ball"
15494 | "cas_proj_box" | "cas_proj_psd_cone"
15495 | "cas_proj_soc_step" | "cas_proj_exp_cone"
15496 | "cas_dykstra_step" | "cas_alternating_projection"
15497 | "cas_polya_enumeration_step" | "cas_burnside_count_step"
15498 | "ml_relu_step" | "ml_leaky_relu_step" | "ml_elu_step"
15500 | "ml_selu_step" | "ml_gelu_step" | "ml_swish_step"
15501 | "ml_mish_step" | "ml_softplus_step" | "ml_softsign_step"
15502 | "ml_hard_sigmoid" | "ml_hard_tanh" | "ml_prelu_step"
15503 | "ml_celu_step" | "ml_silu_step" | "ml_logsumexp_step"
15504 | "ml_log_softmax_step" | "ml_log_sigmoid"
15505 | "ml_glu_step" | "ml_geglu_step" | "ml_swiglu_step"
15506 | "ml_attention_score_step" | "ml_scaled_dot_product"
15507 | "ml_multihead_avg" | "ml_softmax_temperature"
15508 | "ml_dropout_mask_prob" | "ml_layer_norm_step"
15509 | "ml_batch_norm_step" | "ml_group_norm_step"
15510 | "ml_rms_norm_step" | "ml_instance_norm_step"
15511 | "ml_weight_norm_step" | "ml_spectral_norm_step"
15512 | "ml_l2_normalize_step" | "ml_huber_loss_step"
15513 | "ml_smooth_l1_loss" | "ml_focal_loss_step"
15514 | "ml_dice_loss_step" | "ml_iou_loss_step"
15515 | "ml_giou_loss_step" | "ml_diou_loss_step"
15516 | "ml_ciou_loss_step" | "ml_contrastive_loss"
15517 | "ml_triplet_loss_step" | "ml_arcface_loss_step"
15518 | "ml_center_loss_step" | "ml_kl_divergence_loss"
15519 | "ml_cross_entropy_loss" | "ml_binary_cross_entropy"
15520 | "ml_label_smoothing" | "ml_mixup_lambda"
15521 | "ml_cutmix_box_iou" | "ml_random_erasing_step"
15522 | "ml_cosine_lr_schedule" | "ml_warmup_lr_step"
15523 | "ml_step_lr_schedule" | "ml_exponential_lr"
15524 | "ml_polynomial_lr" | "ml_one_cycle_lr"
15525 | "ml_inverse_sqrt_lr" | "ml_cyclic_lr_step"
15526 | "ml_sgd_step" | "ml_momentum_step"
15527 | "ml_nesterov_momentum" | "ml_adagrad_step"
15528 | "ml_rmsprop_step" | "ml_adam_step"
15529 | "ml_adamw_step" | "ml_adamax_step"
15530 | "ml_nadam_step" | "ml_radam_step"
15531 | "ml_lookahead_step" | "ml_lamb_step"
15532 | "ml_lars_step" | "ml_yogi_step"
15533 | "ml_amsgrad_step" | "ml_adabelief_step"
15534 | "ml_shampoo_step" | "ml_lion_step"
15535 | "ml_sophia_step" | "ml_gradient_clip_norm"
15536 | "ml_gradient_clip_value" | "ml_gradient_accumulate"
15537 | "ml_gradient_centralize" | "ml_weight_decay_step"
15538 | "ml_he_init_value" | "ml_xavier_init_value"
15539 | "ml_glorot_init_value" | "ml_orthogonal_init"
15540 | "ml_truncnormal_init" | "ml_kaiming_init"
15541 | "ml_lecun_init_value" | "ml_zero_init"
15542 | "ml_constant_init" | "ml_uniform_init"
15543 | "ml_one_hot_index" | "ml_label_to_id"
15544 | "ml_id_to_label_step" | "ml_token_logit_top_k"
15545 | "ml_topk_argmax" | "ml_nucleus_sample_p"
15546 | "ml_temperature_decay" | "ml_repetition_penalty"
15547 | "ml_eos_logit_boost"
15548 | "nlp_bm25_score" | "nlp_tf_idf_step" | "nlp_okapi_score"
15550 | "nlp_word_freq_value" | "nlp_doc_freq_step"
15551 | "nlp_inverse_doc_freq" | "nlp_cosine_similarity_two"
15552 | "nlp_jaccard_similarity_two" | "nlp_overlap_coefficient"
15553 | "nlp_dice_coefficient_two" | "nlp_simpson_coefficient"
15554 | "nlp_levenshtein_dist" | "nlp_damerau_levenshtein"
15555 | "nlp_jaro_distance" | "nlp_jaro_winkler"
15556 | "nlp_hamming_distance" | "nlp_lcs_length" | "nlp_lcs_ratio"
15557 | "nlp_meteor_score" | "nlp_bleu_score_n"
15558 | "nlp_rouge_score_n" | "nlp_chrf_score" | "nlp_ter_score"
15559 | "nlp_wer_score" | "nlp_cer_score" | "nlp_perplexity_value"
15560 | "nlp_bits_per_character" | "nlp_char_ngram_count"
15561 | "nlp_word_ngram_count" | "nlp_skip_gram_count"
15562 | "nlp_byte_pair_merge_step" | "nlp_wordpiece_score"
15563 | "nlp_unigram_lm_score" | "nlp_kneser_ney_step"
15564 | "nlp_witten_bell_step" | "nlp_good_turing_count"
15565 | "nlp_laplace_smoothing" | "nlp_lidstone_smoothing"
15566 | "nlp_jelinek_mercer" | "nlp_dirichlet_smoothing"
15567 | "nlp_query_likelihood_step" | "nlp_kl_lm_div"
15568 | "nlp_pmi_score" | "nlp_npmi_score"
15569 | "nlp_chi2_collocation" | "nlp_loglikelihood_collocation"
15570 | "nlp_t_score_collocation" | "nlp_dunning_log_likelihood"
15571 | "nlp_lda_alpha_step" | "nlp_lda_beta_step"
15572 | "nlp_lda_topic_dist" | "nlp_plsa_step"
15573 | "nlp_word2vec_skipgram_loss" | "nlp_word2vec_cbow_loss"
15574 | "nlp_glove_loss_step" | "nlp_fasttext_subword_count"
15575 | "nlp_byte_level_bpe_step" | "nlp_sentencepiece_score"
15576 | "nlp_unigram_subword_loss" | "nlp_subword_regularization"
15577 | "nlp_pointwise_attn_score" | "nlp_relative_position_bias"
15578 | "nlp_alibi_position_bias" | "nlp_rope_rotary_angle"
15579 | "nlp_rope_apply_step" | "nlp_position_encoding_sin"
15580 | "nlp_position_encoding_cos" | "nlp_pe_freq_band"
15581 | "nlp_max_seq_len_check" | "nlp_token_drop_rate"
15582 | "nlp_byte_frequency" | "nlp_char_frequency"
15583 | "nlp_punct_ratio" | "nlp_uppercase_ratio"
15584 | "nlp_digit_ratio" | "nlp_emoji_ratio"
15585 | "nlp_url_count" | "nlp_email_count" | "nlp_phone_count"
15586 | "nlp_hashtag_count" | "nlp_mention_count"
15587 | "nlp_token_overlap_two" | "nlp_word_mover_dist"
15588 | "nlp_sif_weight_step" | "nlp_doc_embedding_avg"
15589 | "nlp_attention_pool_step" | "nlp_max_pool_step"
15590 | "nlp_avg_pool_step" | "nlp_sum_pool_step"
15591 | "nlp_self_attn_compute_step" | "nlp_cross_attn_compute_step"
15592 | "nlp_window_attn_step" | "nlp_strided_attn_step"
15593 | "nlp_block_attn_step" | "nlp_sliding_window_step"
15594 | "nlp_local_attn_step" | "nlp_dilated_attn_step"
15595 | "nlp_global_attn_step" | "nlp_sparse_attn_score"
15596 | "nlp_linformer_step" | "nlp_performer_step"
15597 | "nlp_reformer_step" | "nlp_longformer_step"
15598 | "nlp_bigbird_step" | "nlp_routing_attn_step"
15599 | "gfx_perspective_proj_x" | "gfx_perspective_proj_y"
15601 | "gfx_orthographic_proj" | "gfx_view_matrix_step"
15602 | "gfx_lookat_forward" | "gfx_lookat_right" | "gfx_lookat_up"
15603 | "gfx_quat_to_axis_angle" | "gfx_axis_angle_to_quat"
15604 | "gfx_quat_slerp_step" | "gfx_quat_nlerp_step"
15605 | "gfx_quat_dot_two" | "gfx_quat_inverse_step"
15606 | "gfx_quat_to_euler_pitch" | "gfx_quat_to_euler_yaw"
15607 | "gfx_quat_to_euler_roll" | "gfx_euler_to_quat_x"
15608 | "gfx_euler_to_quat_y" | "gfx_euler_to_quat_z"
15609 | "gfx_euler_to_quat_w" | "gfx_rotation_matrix_xx"
15610 | "gfx_rotation_matrix_yy" | "gfx_rotation_matrix_zz"
15611 | "gfx_translation_matrix_step" | "gfx_scale_matrix_step"
15612 | "gfx_shear_matrix_xy" | "gfx_homogeneous_divide"
15613 | "gfx_screen_space_x" | "gfx_screen_space_y"
15614 | "gfx_ndc_to_screen_x" | "gfx_ndc_to_screen_y"
15615 | "gfx_screen_to_ndc_x" | "gfx_screen_to_ndc_y"
15616 | "gfx_clip_polygon_step" | "gfx_sutherland_hodgman"
15617 | "gfx_cohen_sutherland_code" | "gfx_liang_barsky_t"
15618 | "gfx_bresenham_step_x" | "gfx_bresenham_step_y"
15619 | "gfx_xiaolin_wu_intensity" | "gfx_aabb_intersect_check"
15620 | "gfx_obb_overlap_step" | "gfx_sphere_intersect_t"
15621 | "gfx_ray_triangle_t" | "gfx_ray_plane_t" | "gfx_ray_box_t"
15622 | "gfx_ray_sphere_t" | "gfx_ray_disk_t"
15623 | "gfx_ray_cylinder_t" | "gfx_ray_cone_t"
15624 | "gfx_ray_ellipsoid_t" | "gfx_ray_torus_t_approx"
15625 | "gfx_barycentric_alpha" | "gfx_barycentric_beta"
15626 | "gfx_barycentric_gamma" | "gfx_phong_diffuse_step"
15627 | "gfx_phong_specular_step" | "gfx_phong_ambient_step"
15628 | "gfx_blinn_specular_step" | "gfx_lambert_term"
15629 | "gfx_oren_nayar_term" | "gfx_cook_torrance_d_ggx"
15630 | "gfx_cook_torrance_g_smith" | "gfx_cook_torrance_f_schlick"
15631 | "gfx_disney_principled_d" | "gfx_microfacet_brdf_step"
15632 | "gfx_subsurface_scattering_term" | "gfx_translucent_falloff"
15633 | "gfx_normal_distribution_ggx"
15634 | "gfx_geometric_attenuation_smith"
15635 | "gfx_fresnel_dielectric_step" | "gfx_fresnel_conductor_step"
15636 | "gfx_index_of_refraction" | "gfx_snells_law_angle"
15637 | "gfx_total_internal_reflection" | "gfx_refract_direction_x"
15638 | "gfx_reflect_direction_x" | "gfx_environment_map_uv_u"
15639 | "gfx_environment_map_uv_v" | "gfx_cube_map_face_index"
15640 | "gfx_octahedral_encode_x" | "gfx_octahedral_encode_y"
15641 | "gfx_spherical_harmonic_y00" | "gfx_spherical_harmonic_y10"
15642 | "gfx_spherical_harmonic_y11" | "gfx_spherical_harmonic_y20"
15643 | "gfx_zonal_harmonic_step" | "gfx_irradiance_sh_eval"
15644 | "gfx_radiance_sh_eval" | "gfx_skybox_uv_u" | "gfx_skybox_uv_v"
15645 | "gfx_tonemap_reinhard" | "gfx_tonemap_aces"
15646 | "gfx_tonemap_uncharted2" | "gfx_tonemap_filmic"
15647 | "gfx_gamma_correct_step" | "gfx_srgb_to_linear"
15648 | "gfx_linear_to_srgb" | "gfx_dither_bayer_4x4"
15649 | "gfx_dither_floyd_steinberg" | "gfx_oklab_l_step"
15650 | "gfx_oklab_a_step" | "gfx_oklab_b_step"
15651 | "gfx_oklch_chroma" | "gfx_oklch_hue"
15652 | "gfx_pcg_hash_step" | "gfx_xorshift_step"
15653 | "gfx_halton_step" | "gfx_sobol_step"
15654 | "gfx_van_der_corput" | "gfx_low_discrepancy_step"
15655 | "gfx_blue_noise_value" | "gfx_perlin_noise_step"
15656 | "gfx_simplex_noise_step" | "gfx_fbm_noise_step"
15657 | "gfx_worley_noise_step" | "gfx_voronoi_distance"
15658 | "gfx_curl_noise_step" | "gfx_gradient_noise_step"
15659 | "gfx_value_noise_step" | "gfx_signed_distance_box"
15660 | "gfx_signed_distance_sphere" | "gfx_signed_distance_capsule"
15661 | "db_b_tree_split" | "db_b_tree_merge"
15663 | "db_lsm_compaction_step" | "db_skiplist_height_pick"
15664 | "db_bloom_filter_bit_index" | "db_cuckoo_filter_fingerprint"
15665 | "db_quotient_filter_canonical" | "db_count_min_sketch_bin"
15666 | "db_hyperloglog_register_max" | "db_min_hash_value"
15667 | "db_simhash_bit" | "db_consistent_hash_index"
15668 | "db_rendezvous_hash_score" | "db_jump_hash_bucket"
15669 | "db_maglev_hash_step" | "db_lru_cache_eviction_age"
15670 | "db_lfu_cache_decay" | "db_arc_cache_score"
15671 | "db_clock_cache_hand" | "db_tinylfu_admit_score"
15672 | "db_w_tinylfu_freq" | "db_buffer_pool_score"
15673 | "db_query_plan_cost_step" | "db_join_selectivity_step"
15674 | "db_index_seek_cost" | "db_seq_scan_cost"
15675 | "db_index_scan_cost" | "db_sort_cost_estimate"
15676 | "db_hash_join_cost" | "db_merge_join_cost"
15677 | "db_nested_loop_cost" | "db_query_cardinality"
15678 | "db_histogram_bucket_index" | "db_quantile_estimate_p99"
15679 | "db_t_digest_centroid" | "db_kll_quantile_step"
15680 | "db_dd_sketch_bin" | "db_reservoir_sample_index"
15681 | "db_chao_estimator_step" | "db_jaccard_minhash_estimate"
15682 | "db_distinct_estimate_lpc" | "db_distinct_estimate_hll"
15683 | "db_throttle_token_step" | "db_leaky_bucket_step"
15684 | "db_token_bucket_step" | "db_circuit_breaker_step"
15685 | "db_two_phase_commit_step" | "db_three_phase_commit_step"
15686 | "db_paxos_propose_id" | "db_raft_term_advance"
15687 | "db_raft_log_match_check" | "db_zab_epoch_step"
15688 | "db_chubby_lease_step" | "db_logical_clock_step"
15689 | "db_lamport_timestamp" | "db_vector_clock_merge"
15690 | "db_hybrid_logical_clock" | "db_crdt_g_counter_merge"
15691 | "db_crdt_pn_counter_merge" | "db_crdt_lww_register_merge"
15692 | "db_crdt_set_or_merge" | "db_consensus_quorum_size"
15693 | "db_replication_lag_step" | "db_partitions_for_n"
15694 | "db_consistent_lookup_id" | "db_chord_finger_index"
15695 | "db_kademlia_xor_distance" | "db_pastry_routing_step"
15696 | "db_dht_replicate_factor" | "db_partition_failure_check"
15697 | "db_byzantine_quorum_size" | "db_pbft_view_change"
15698 | "db_honey_badger_step" | "db_avalanche_query_step"
15699 | "db_quorum_intersection_check" | "db_anti_entropy_step"
15700 | "db_merkle_node_hash" | "db_merkle_path_verify"
15701 | "db_gossip_fanout_step" | "db_anti_entropy_pull_step"
15702 | "db_split_brain_check" | "db_clock_skew_estimate"
15703 | "db_freshness_score" | "db_read_repair_step"
15704 | "db_hinted_handoff_step" | "db_compaction_score"
15705 | "db_levelled_compaction_step" | "db_size_tiered_compaction"
15706 | "db_universal_compaction_step" | "db_write_amplification"
15707 | "db_read_amplification" | "db_space_amplification"
15708 | "db_block_cache_hit_rate" | "db_page_cache_eviction_age"
15709 | "db_wal_fsync_cost" | "db_group_commit_count"
15710 | "db_replica_lag_threshold" | "db_synchronous_commit_check"
15711 | "db_async_commit_check" | "db_eventual_consistency_check"
15712 | "db_strong_consistency_check" | "db_linearizability_check"
15713 | "db_causal_consistency_check"
15714 | "net_tcp_cwnd_step" | "net_tcp_ssthresh_update"
15716 | "net_tcp_reno_step" | "net_tcp_cubic_step"
15717 | "net_tcp_bbr_step" | "net_tcp_vegas_step"
15718 | "net_tcp_westwood_step" | "net_tcp_compound_step"
15719 | "net_tcp_dctcp_step" | "net_tcp_yeah_step"
15720 | "net_tcp_htcp_step" | "net_tcp_hybla_step"
15721 | "net_tcp_illinois_step" | "net_tcp_lp_step"
15722 | "net_tcp_scalable_step" | "net_tcp_veno_step"
15723 | "net_aiad_step" | "net_aimd_step"
15724 | "net_miad_step" | "net_mimd_step"
15725 | "net_aqm_red_drop_prob" | "net_aqm_codel_target"
15726 | "net_aqm_pie_drop_rate" | "net_aqm_fq_codel_step"
15727 | "net_aqm_blue_step" | "net_aqm_choke_step"
15728 | "net_aqm_sfq_step" | "net_aqm_drr_step"
15729 | "net_aqm_wrr_step" | "net_token_rate_limit"
15730 | "net_traffic_shaper_step" | "net_priority_queue_index"
15731 | "net_packet_loss_estimate" | "net_jitter_estimate"
15732 | "net_latency_avg" | "net_rtt_smoothed"
15733 | "net_rtt_variation" | "net_rto_compute"
15734 | "net_bandwidth_delay_product" | "net_path_capacity_kleinrock"
15735 | "net_loss_rate_to_throughput" | "net_throughput_padhye"
15736 | "net_throughput_mathis" | "net_throughput_response"
15737 | "net_router_buffer_size" | "net_drop_tail_check"
15738 | "net_burst_size_compute" | "net_packet_pacing_step"
15739 | "net_link_capacity_share" | "net_proportional_fair_share"
15740 | "net_max_min_fair_step" | "net_alpha_fair_step"
15741 | "net_kelly_pricing_step" | "net_network_utility_max"
15742 | "net_lyapunov_drift_plus_penalty" | "net_backpressure_step"
15743 | "net_max_weight_match" | "net_qcsma_propose"
15744 | "net_csma_back_off" | "net_alohanet_throughput"
15745 | "net_slotted_aloha_throughput" | "net_csma_efficiency"
15746 | "net_token_ring_efficiency" | "net_polling_efficiency"
15747 | "net_radio_path_loss" | "net_friis_received_power"
15748 | "net_two_ray_ground_loss" | "net_okumura_hata_loss"
15749 | "net_log_distance_path" | "net_shadowing_normal"
15750 | "net_rician_k_factor" | "net_rayleigh_envelope"
15751 | "net_doppler_shift" | "net_capacity_shannon"
15752 | "net_mimo_capacity_step" | "net_zero_forcing_beam"
15753 | "net_mmse_beam_step" | "net_water_filling_power"
15754 | "net_amc_threshold_index" | "net_harq_combining_gain"
15755 | "net_turbo_decode_iter" | "net_ldpc_iteration_step"
15756 | "net_polar_decode_step" | "net_viterbi_step"
15757 | "net_bcjr_step" | "net_outage_probability"
15758 | "net_diversity_gain" | "net_array_gain"
15759 | "net_multiplexing_gain" | "net_coding_gain"
15760 | "net_pruning_gain" | "net_macro_diversity_step"
15761 | "net_micro_diversity_step" | "net_handoff_threshold"
15762 | "net_call_admission_check" | "net_blocking_probability"
15763 | "net_erlang_b_formula" | "net_erlang_c_formula"
15764 | "net_engset_formula" | "net_little_law_l"
15765 | "net_throughput_law" | "net_response_time_law"
15766 | "net_utilization_law" | "net_forced_flow_law"
15767 | "os_priority_aging_step" | "os_mlfq_demote_step"
15769 | "os_mlfq_promote_step" | "os_round_robin_quantum"
15770 | "os_completely_fair_vruntime" | "os_lottery_ticket_count"
15771 | "os_stride_pass_step" | "os_eevdf_eligible"
15772 | "os_cfs_load_balance_step" | "os_eas_energy_estimate"
15773 | "os_smt_threading_share" | "os_numa_node_distance"
15774 | "os_cpu_affinity_score" | "os_thread_migration_cost"
15775 | "os_load_average_decay" | "os_runqueue_depth"
15776 | "os_io_scheduler_deadline" | "os_io_scheduler_cfq_step"
15777 | "os_io_scheduler_noop_step" | "os_io_scheduler_bfq_step"
15778 | "os_io_scheduler_kyber_step" | "os_io_scheduler_mq_deadline"
15779 | "os_anticipation_window" | "os_elevator_step"
15780 | "os_disk_seek_time" | "os_disk_rotational_lat"
15781 | "os_disk_transfer_time" | "os_pre_fetch_window"
15782 | "os_buffer_cache_pages" | "os_dirty_page_threshold"
15783 | "os_writeback_step" | "os_swappiness_factor"
15784 | "os_kswapd_wake_threshold" | "os_oom_score_step"
15785 | "os_page_replacement_lru" | "os_page_replacement_clock"
15786 | "os_page_replacement_2q" | "os_working_set_size"
15787 | "os_thrashing_threshold" | "os_demand_paging_step"
15788 | "os_copy_on_write_check" | "os_zero_page_optimization"
15789 | "os_huge_page_threshold" | "os_transparent_hugepage"
15790 | "os_kasan_shadow_offset" | "os_kfence_check"
15791 | "os_kfence_alloc_index" | "os_slub_object_size_round"
15792 | "os_slab_color_offset" | "os_per_cpu_cache_size"
15793 | "os_buddy_order_pick" | "os_compact_memory_step"
15794 | "os_kvm_vmcs_field_offset" | "os_apic_irq_priority"
15795 | "os_msi_x_vector_count" | "os_iommu_domain_step"
15796 | "os_pci_bus_address" | "os_acpi_state_transition"
15797 | "os_cpufreq_governor_step" | "os_intel_pstate_target"
15798 | "os_amd_pstate_target" | "os_thermal_zone_trip"
15799 | "os_throttle_temperature" | "os_battery_capacity_pct"
15800 | "os_powertop_score" | "os_idle_state_select"
15801 | "os_c_state_residency" | "os_p_state_voltage"
15802 | "os_dvfs_step" | "os_voltage_scaling_step"
15803 | "os_frequency_scaling_step" | "os_inotify_event_count"
15804 | "os_epoll_ctl_count" | "os_io_uring_sqe_count"
15805 | "os_io_uring_cqe_count" | "os_kqueue_event_count"
15806 | "os_systemd_journal_size" | "os_dmesg_severity_level"
15807 | "os_audit_event_priority" | "os_apparmor_profile_active"
15808 | "os_selinux_context_match" | "os_smack_label_compare"
15809 | "os_capability_check" | "os_seccomp_filter_step"
15810 | "os_namespace_isolation" | "os_cgroup_v1_count"
15811 | "os_cgroup_v2_count" | "os_pid_max_value"
15812 | "os_thread_max_value" | "os_file_max_value"
15813 | "os_open_files_count" | "os_socket_max_value"
15814 | "os_inotify_max_watches" | "os_oom_kill_score"
15815 | "os_zswap_compress_ratio" | "os_zram_compress_ratio"
15816 | "os_swap_pressure_score" | "os_pressure_stall_step"
15817 | "os_psi_avg10_step" | "os_psi_avg60_step"
15818 | "os_psi_avg300_step" | "os_load_proc_avg"
15819 | "os_load_user_avg" | "os_load_iowait_avg"
15820 | "sec_argon2_memcost" | "sec_argon2_timecost"
15822 | "sec_argon2_parallelism" | "sec_argon2_block_step"
15823 | "sec_pbkdf2_iter" | "sec_scrypt_n_param"
15824 | "sec_scrypt_r_param" | "sec_scrypt_p_param"
15825 | "sec_balloon_hash_step" | "sec_yescrypt_step"
15826 | "sec_bcrypt_cost_factor" | "sec_bcrypt_round_step"
15827 | "sec_password_strength_zxcvbn" | "sec_haveibeenpwned_check"
15828 | "sec_diceware_word_index" | "sec_xkcd_passphrase_score"
15829 | "sec_passphrase_entropy" | "sec_chosen_charset_strength"
15830 | "sec_keystroke_timing_var" | "sec_2fa_totp_window"
15831 | "sec_totp_drift_check" | "sec_hotp_counter_step"
15832 | "sec_yubikey_otp_check" | "sec_webauthn_attestation_check"
15833 | "sec_fido2_assertion_check" | "sec_certificate_chain_depth"
15834 | "sec_revocation_ocsp_check" | "sec_crl_age_seconds"
15835 | "sec_pki_path_validate" | "sec_x509_subject_match"
15836 | "sec_san_match_count" | "sec_basic_constraints_ca"
15837 | "sec_pinning_compare" | "sec_certificate_transparency"
15838 | "sec_dane_tlsa_match" | "sec_hpkp_pin_match"
15839 | "sec_csp_directive_match" | "sec_csrf_token_match"
15840 | "sec_cors_origin_match" | "sec_xss_filter_score"
15841 | "sec_html_escape_check" | "sec_url_safe_encode_check"
15842 | "sec_path_traversal_detect" | "sec_sqli_pattern_score"
15843 | "sec_xxe_pattern_score" | "sec_xxe_dtd_check"
15844 | "sec_command_injection_score" | "sec_idor_check"
15845 | "sec_jwt_alg_safe" | "sec_jwt_kid_match"
15846 | "sec_jwt_signature_verify" | "sec_oauth2_state_validate"
15847 | "sec_oauth2_pkce_step" | "sec_oauth_nonce_check"
15848 | "sec_session_lifetime" | "sec_idle_timeout_step"
15849 | "sec_login_throttle_step" | "sec_account_lockout_step"
15850 | "sec_password_history_check" | "sec_complexity_policy_score"
15851 | "sec_dictionary_attack_check" | "sec_brute_force_attempts"
15852 | "sec_credential_stuffing_score" | "sec_kerberos_ticket_age"
15853 | "sec_kerberos_pac_check" | "sec_kerberos_pre_auth"
15854 | "sec_ldap_bind_step" | "sec_radius_auth_step"
15855 | "sec_diameter_avp_step" | "sec_saml_assertion_age"
15856 | "sec_oidc_id_token_age" | "sec_acme_dns_challenge"
15857 | "sec_dnssec_signature_check" | "sec_spf_pass_check"
15858 | "sec_dkim_signature_check" | "sec_dmarc_policy_check"
15859 | "sec_arc_chain_step" | "sec_smtp_ssl_check"
15860 | "sec_imap_starttls_check" | "sec_pop3_security_step"
15861 | "sec_tls_alert_severity" | "sec_tls13_handshake_step"
15862 | "sec_tls12_handshake_step" | "sec_tls11_deprecation_check"
15863 | "sec_ssl3_disabled_check" | "sec_cipher_suite_strength"
15864 | "sec_cbc_mac_block_count" | "sec_gcm_iv_unique_check"
15865 | "sec_chachapoly_nonce_check" | "sec_x25519_clamping_step"
15866 | "sec_ed25519_signature_step" | "sec_ed448_signature_step"
15867 | "sec_p384_curve_step" | "sec_secp256k1_step"
15868 | "sec_blake3_chunk_step" | "sec_keccak_round_step"
15869 | "sec_sha3_padding_step" | "sec_argon2_state_advance"
15870 | "sec_chacha20_quarterround" | "sec_aes_round_step"
15871 | "sec_aes_keyschedule_step" | "sec_des_round_step"
15872 | "sec_blowfish_round_step" | "sec_serpent_round_step"
15873 | "sec_twofish_round_step"
15874 | "fixed_from_gregorian" | "gregorian_from_fixed"
15876 | "fixed_from_julian" | "julian_from_fixed"
15877 | "iso_week_date" | "hebrew_leap_year"
15878 | "hebrew_year_length" | "fixed_from_hebrew"
15879 | "islamic_leap_year" | "fixed_from_islamic"
15880 | "persian_arithmetic_leap" | "fixed_from_persian"
15881 | "coptic_from_fixed" | "ethiopic_from_fixed"
15882 | "french_revolutionary_leap" | "fixed_from_french"
15883 | "chinese_year_zodiac" | "chinese_lunation_winter"
15884 | "hindu_solar_year" | "hindu_lunisolar_month"
15885 | "maya_long_count_from_fixed" | "mayan_haab_from_fixed"
15886 | "mayan_tzolkin_from_fixed" | "badi_year_from_fixed"
15887 | "bahai_from_fixed" | "easter_gregorian_year"
15888 | "easter_orthodox_year" | "easter_julian_year"
15889 | "day_of_week_zeller" | "iso_day_number"
15890 | "weekday_name_short" | "leap_year_gregorian"
15891
15892 | "dnorm" | "dt" | "df_dist" | "dchisq"
15894 | "glm" | "aov" | "shapiro_wilk" | "anderson_darling"
15895 | "kolmogorov_smirnov" | "spearmanr" | "kendalltau" | "pearsonr"
15896 | "mannwhitneyu" | "wilcoxon" | "kruskal_h"
15897
15898 | "iota_n" | "reduce_axis" | "scan_axis" | "fold_axis"
15900 | "rotate_axis" | "transpose_axis" | "reshape_dim"
15901 | "encode_base" | "decode_base" | "nub_list" | "nub_count"
15902 | "membership_idx" | "deal_n_k" | "roll_n"
15903 | "permute_idx" | "invert_perm"
15904
15905 | "julian_day" | "jd_to_calendar" | "tt_to_tdb"
15907 | "ra_dec_to_alt_az" | "alt_az_to_ra_dec"
15908 | "precession_iau2006" | "nutation_iau2000a"
15909 | "aberration_annual" | "proper_motion_apply"
15910 | "parallax_correction" | "sun_position_low" | "sun_distance_au"
15911 | "moon_position_low" | "moon_phase_age" | "lunation_index"
15912 | "eclipse_magnitude" | "saros_cycle" | "metonic_cycle"
15913 | "orbit_kepler3" | "orbital_period_au" | "orbit_eccentric_anomaly"
15914 | "escape_velocity_body" | "hill_sphere_radius" | "tisserand_param"
15915 | "tle_mean_motion" | "sgp4_propagate_step" | "airy_disk_radius"
15916 | "rayleigh_criterion" | "strehl_ratio" | "au_to_km"
15917
15918 | "elo_expected" | "elo_update" | "glicko_rating"
15920 | "trueskill_update" | "trueskill_match_quality"
15921 | "pythagorean_expectation" | "war_above_replacement"
15922 | "woba_weight" | "wrc_plus" | "ops_plus" | "era_plus"
15923 | "fip" | "xfip" | "siera" | "babip" | "wpa"
15924 | "win_probability" | "leverage_index" | "clutch_score"
15925 | "shooting_pct" | "save_pct" | "corsi_for" | "fenwick_for"
15926 | "goals_above_avg" | "tackle_efficiency" | "yards_per_attempt"
15927 | "qbr_metric" | "epa_per_play"
15928
15929 | "vlookup" | "hlookup" | "xlookup" | "index_match"
15931 | "indirect" | "choose" | "offset"
15932 | "sumif" | "countif" | "averageif"
15933 | "sumifs" | "countifs" | "averageifs"
15934 | "sumproduct" | "rank_eq" | "rank_avg" | "percentrank"
15935 | "quartile_inc" | "quartile_exc"
15936 | "xnpv" | "ppmt" | "ipmt" | "rate"
15937 | "macauley_duration" | "convexity" | "yield_to_maturity"
15938 | "accrued_interest" | "clean_price" | "dirty_price"
15939 | "coupon_count" | "skill_score" | "reliability_diagram"
15940 | "taylor_diagram_score"
15941
15942 | "geohash_neighbors" | "h3_index" | "h3_geo_to_h3"
15944 | "h3_h3_to_geo" | "h3_k_ring" | "h3_neighbor" | "h3_resolution"
15945 | "s2_cell_id" | "s2_cell_at_lat_lng" | "s2_cell_neighbors"
15946 | "utm_from_lat_lng" | "utm_to_lat_lng"
15947 | "mgrs_encode" | "mgrs_decode"
15948 | "lat_lng_to_xy_mercator" | "lat_lng_to_xy_lambert"
15949 | "haversine_dist" | "vincenty_dist" | "andoyer_dist"
15950 | "rhumb_line_bearing"
15951 | "destination_point" | "tile_xyz_to_lat_lng" | "lat_lng_to_tile_xyz"
15952 | "polygon_winding_order" | "point_in_polygon_ray"
15953 | "point_in_polygon_winding" | "segment_intersection"
15954 | "segment_distance_point" | "convex_hull_chan"
15955
15956 | "pid_anti_windup" | "pid_ziegler_nichols"
15958 | "smith_predictor_step" | "lqr_gain_continuous"
15959 | "lqr_gain_discrete" | "lqg_step" | "h_infinity_norm"
15960 | "bode_gain_margin" | "bode_phase_margin"
15961 | "nyquist_encirclement" | "nichols_chart_step"
15962 | "servo_position_velocity" | "servo_torque_step"
15963 | "imu_madgwick_step" | "imu_mahony_step" | "quaternion_from_imu"
15964 | "denavit_hartenberg_h" | "forward_kinematics_dh"
15965 | "inverse_kinematics_2link" | "jacobian_2dof"
15966 | "manipulability_yoshikawa" | "singularity_check_2link"
15967 | "path_dubins_lsl" | "path_dubins_rsr" | "path_reeds_shepp"
15968 | "rrt_extend" | "rrt_star_rewire" | "prm_node_connect"
15969
15970 | "life_expectancy_e0" | "force_of_mortality" | "select_ultimate"
15972 | "annuity_due_an" | "annuity_immediate_an"
15973 | "term_life_a_n_t" | "whole_life_a"
15974 | "endowment_pure_e" | "endowment_combined_a"
15975 | "premium_net" | "level_premium"
15976 | "reserve_prospective" | "reserve_retrospective"
15977 | "gross_premium_load" | "experience_factor"
15978 | "mortality_table_q" | "select_period_step"
15979 | "multi_decrement_q" | "multi_state_pij"
15980 | "credibility_buhlmann" | "loss_severity_lognormal"
15981 | "loss_frequency_poisson" | "ruin_probability_lundberg"
15982 | "cramer_lundberg_step" | "bornhuetter_ferguson"
15983 | "chain_ladder_step" | "ibnr_estimate" | "run_off_triangle_step"
15984
15985 | "r_naught_basic" | "r_effective_t" | "doubling_time_growth"
15987 | "sirs_step" | "seirs_step" | "susceptible_to_infected"
15988 | "attack_rate" | "vaccination_coverage_required"
15989 | "cfr_case_fatality" | "ifr_infection_fatality"
15990 | "dalys_disability_weight" | "qaly_lifetime" | "ylll_pml"
15991 | "rt_serial_interval" | "generation_time_step"
15992 | "gini_inequality_health" | "standardized_mortality_smr"
15993 | "indirect_age_adjusted" | "direct_age_adjusted"
15994 | "odds_ratio_2x2" | "risk_ratio_2x2" | "number_needed_to_treat"
15995 | "attributable_fraction_pop" | "preventive_fraction"
15996 | "contact_tracing_eff" | "cluster_attack_rate"
15997 | "transmission_pair_index"
15998
15999 | "tar_header_checksum" | "tar_pad_512" | "tar_member_record"
16001 | "zip_local_header" | "zip_central_dir" | "zip_eocd"
16002 | "gzip_member_step" | "gzip_crc32_init" | "gzip_isize"
16003 | "deflate_dynamic_huffman" | "deflate_static_block"
16004 | "lz4_block_step" | "lz4_match_offset"
16005 | "zstd_frame_header" | "brotli_huffman_table"
16006 | "brotli_meta_block" | "lzma_range_step"
16007 | "quoted_printable_encode" | "uuencode_step"
16008 | "modhex_encode" | "percent_encode_full"
16009 | "punycode_encode" | "idn_to_ascii" | "idn_to_unicode"
16010 | "msgpack_pack_int" | "msgpack_pack_str"
16011 | "cbor_encode_uint" | "cbor_encode_str"
16012
16013 | "molecular_weight_compound" | "molarity_dilution"
16015 | "gas_constant_value" | "eyring_rate" | "van_t_hoff_kp"
16016 | "henderson_buffer" | "titration_ph_endpoint"
16017 | "isoelectric_point_protein" | "ka_to_pka" | "pkb_to_kb"
16018 | "amphoteric_check" | "oxidation_number"
16019 | "half_reaction_balance" | "redox_potential_cell"
16020 | "electrolysis_mass" | "spectrophotometer_beer_lambert"
16021 | "epsilon_extinction" | "transmittance_to_a"
16022 | "crystal_field_ligand" | "jahn_teller_check"
16023 | "vsepr_geometry" | "lewis_dot_count"
16024 | "formal_charge" | "resonance_count"
16025 | "ramachandran_phi_psi" | "rg_radius_of_gyration"
16026 | "spectroscopic_factor" | "avogadro_constant"
16027
16028 | "cents_between_freqs" | "note_name_from_midi"
16030 | "interval_quality_size" | "scale_pitches_major"
16031 | "scale_pitches_minor" | "mode_pitches_dorian"
16032 | "mode_pitches_phrygian" | "mode_pitches_lydian"
16033 | "chord_root_inversion" | "chord_quality_classify"
16034 | "chord_voicing_close" | "key_signature_sharps"
16035 | "key_signature_flats" | "tempo_to_ms" | "beat_to_seconds"
16036 | "time_sig_subdivision" | "equal_tempered_freq"
16037 | "just_intonation_freq" | "pythagorean_freq"
16038 | "mean_tone_freq" | "werckmeister_iii" | "kirnberger_iii"
16039 | "dynamics_db_level" | "harmonics_partial"
16040
16041 | "moment_magnitude_mw" | "richter_local_ml"
16043 | "surface_wave_ms" | "body_wave_mb"
16044 | "gutenberg_richter_b" | "omori_aftershock"
16045 | "pga_attenuation" | "arias_intensity" | "shake_map_pga"
16046 | "liquefaction_potential_index" | "spt_n_correction"
16047 | "mineral_mohs_hardness" | "streak_color_index"
16048 | "specific_gravity_water" | "feldspar_classify"
16049 | "silicate_classify" | "igneous_qapf"
16050 | "metamorphic_grade" | "crustal_density_depth"
16051 | "pwave_velocity_depth" | "swave_velocity_depth"
16052 | "gradient_geothermal" | "heat_flow_radiogenic"
16053
16054 | "dgemm" | "sgemm" | "zgemm" | "cgemm"
16056 | "dgemv" | "sgemv" | "dtrsm" | "strsm"
16057 | "dgesv" | "dgetrf" | "dgeqrf" | "dgesvd"
16058 | "dsyevd" | "dpotrf" | "daxpy" | "ddot"
16059 | "dnrm2" | "dscal" | "dasum" | "idamax"
16060 | "dsyrk" | "dgerqf" | "dorgqr" | "dorglq"
16061 | "drot" | "drotg" | "dpbsv" | "dgbsv"
16062 | "dtbsv" | "dtrsv" | "ddrot" | "dgemm3m"
16063 | "dgels" | "dgelsd"
16064
16065 | "cnf_unit_propagate" | "cnf_pure_literal_elim"
16067 | "cnf_dpll_branch" | "dpll_clause_learning"
16068 | "two_watched_literals" | "walksat_step"
16069 | "resolution_step" | "subsumption_check"
16070 | "tableau_branch_close" | "sequent_left_intro"
16071 | "sequent_right_intro" | "nbe_normalize"
16072 | "church_numeral_n" | "encode_pair" | "encode_succ"
16073 | "simply_typed_check" | "hindley_milner_step"
16074 | "unification_robinson" | "bdd_apply" | "bdd_restrict"
16075 | "bdd_quantify" | "aig_simplify_step"
16076 | "smt_qf_lia_solve_step" | "smt_qf_uf_combine"
16077 | "model_checking_ctl" | "model_checking_ltl"
16078 | "bisimulation_step" | "coq_tactic_apply"
16079 | "coq_unify_term" | "refl_check" | "sym_check" | "trans_check"
16080
16081 | "nfa_to_dfa" | "subset_construction"
16083 | "dfa_minimize_hopcroft" | "regex_to_nfa_thompson"
16084 | "glushkov_construction" | "brzozowski_derivative"
16085 | "ll1_first_set" | "ll1_follow_set" | "ll1_predict_table"
16086 | "lr0_items_step" | "lalr_lookahead_compute"
16087 | "lr1_canonical_collection"
16088 | "earley_scan" | "earley_predict" | "earley_complete"
16089 | "packrat_parse_step" | "ascent_parser_step"
16090 | "pratt_parse_step" | "shunting_yard_step"
16091 | "regex_compile_thompson" | "regex_match_dfa"
16092 | "lex_keyword_classify"
16093 | "peg_seq" | "peg_choice" | "peg_repeat" | "peg_lookahead"
16094 | "dfa_simulate_step" | "bytecode_disasm_step"
16095 | "ssa_phi_insert" | "dom_tree_idom" | "dominance_frontier"
16096
16097 | "porter_stem_step" | "snowball_stem_english"
16099 | "snowball_stem_french" | "lemmatize_wordnet"
16100 | "lemmatize_lemmy" | "stem_lancaster"
16101 | "soundex_phonetic" | "metaphone_phonetic"
16102 | "caverphone_2" | "nysiis_phonetic"
16103 | "match_rating_codex" | "daitch_mokotoff"
16104 | "viterbi_pos_tag" | "forward_backward_pos"
16105 | "crf_log_likelihood" | "bigram_perplexity"
16106 | "trigram_perplexity" | "ner_bilou_decode"
16107 | "constituency_cyk" | "dependency_parse_eisner"
16108 | "transition_arc_eager" | "transition_arc_standard"
16109 | "word_alignment_ibm1" | "word_alignment_ibm2"
16110 | "lexicalized_parse" | "coreference_singleton"
16111 | "anaphora_distance" | "head_finding_collins"
16112 | "tree_kernel_collins"
16113
16114 | "btrim" | "translate" | "ascii"
16116 | "regexp_split" | "regexp_matches" | "regexp_replace"
16117 | "json_build_object" | "jsonb_set"
16118 | "json_array_length" | "json_extract_path"
16119 | "json_strip_nulls" | "jsonb_pretty"
16120 | "jsonb_path_query" | "json_each"
16121 | "jsonb_array_length" | "jsonb_object_keys"
16122 | "jsonb_typeof" | "array_to_jsonb"
16123 | "ts_match" | "ts_rank" | "ts_headline"
16124 | "substring_similarity" | "levenshtein_dist"
16125 | "word_similarity" | "strict_word_similarity"
16126 | "hstore_to_array" | "array_to_hstore"
16127 | "string_agg" | "array_agg"
16128 | "corr_agg" | "covar_pop" | "covar_samp"
16129 | "regr_slope" | "regr_intercept" | "regr_r2"
16130 | "percentile_cont" | "percentile_disc" | "mode_agg"
16131 | "array_to_string" | "array_position" | "array_positions"
16132 | "array_remove" | "array_replace"
16133 | "xmlforest" | "xmlagg"
16134
16135 | "zadd" | "zrem" | "zrangebyscore"
16137 | "zrank" | "zrevrank" | "zincrby"
16138 | "zcard" | "zcount" | "zlexcount"
16139 | "lpush" | "rpush" | "lrange" | "lrem"
16140 | "hset" | "hget" | "hgetall" | "hlen"
16141 | "hkeys" | "hvals" | "hmset" | "hincrby"
16142 | "sadd" | "srem" | "smembers"
16143 | "sinter" | "sunion" | "sdiff"
16144 | "scard" | "sismember" | "spop"
16145 | "setex" | "setnx" | "expire"
16146 | "ttl" | "pttl" | "persist"
16147 | "incr" | "decr" | "incrby" | "decrby"
16148 | "getset" | "mset" | "mget" | "renamenx"
16149 | "dbsize" | "type_redis" | "exists_key"
16150 | "strlen" | "getrange" | "setrange" | "append_redis"
16151 | "bitcount" | "bitop" | "bitpos"
16152 | "pfadd" | "pfcount"
16153 | "geoadd" | "geodist" | "geohash"
16154 | "xadd" | "xlen" | "xrange"
16155 | "object_encoding" | "debug_object" | "cluster_slots"
16156
16157 | "argpartition" | "bincount" | "nonzero_count"
16159 | "flatnonzero" | "searchsorted" | "digitize"
16160 | "histogram_bin_edges" | "unique_count"
16161 | "polyfit_rmse"
16162 | "ellipk" | "ellipe"
16163 | "hyp1f1" | "hyp2f1" | "mathieu_b"
16164 | "spherical_jn" | "spherical_yn"
16165 | "jv" | "yn" | "iv" | "kv"
16166 | "airyai" | "airybi"
16167 | "polygamma" | "trigamma" | "loggamma"
16168 | "factorial2" | "factorialk"
16169 | "owens_t" | "marcum_q" | "voigt_profile"
16170 | "chebyt" | "chebyu" | "sph_harm"
16171 | "wofz" | "erfcx" | "erfi" | "dawsn"
16172 | "interp1d"
16173 | "convolve_full" | "convolve_valid" | "correlate_full"
16174 | "kron_product"
16175 | "simpson_rule" | "romberg_quad" | "fixed_quad"
16176 | "ode45_step" | "ode_lsoda" | "solve_ivp_step"
16177 | "root_brentq" | "root_newton" | "root_secant"
16178 | "fmin_powell" | "fmin_cobyla"
16179
16180 | "cobb_douglas" | "ces_production"
16182 | "leontief_input" | "leontief_output"
16183 | "slutsky_decompose"
16184 | "marshallian_demand" | "hicksian_demand"
16185 | "expenditure_function" | "indirect_utility"
16186 | "gale_shapley_step" | "deferred_acceptance"
16187 | "top_trading_cycle" | "vcg_payment" | "myerson_optimal"
16188 | "gini_market" | "hhi_concentration"
16189 | "cournot_eq" | "stackelberg_eq" | "bertrand_eq"
16190 | "monopoly_lerner"
16191 | "consumer_surplus" | "producer_surplus"
16192 | "deadweight_loss" | "tax_incidence"
16193 | "pareto_efficiency" | "edgeworth_box_alloc"
16194 | "social_welfare_utilitarian"
16195 | "social_welfare_rawls" | "social_welfare_nash"
16196 | "arrow_independence"
16197 | "vickrey_auction" | "first_price_seal"
16198 | "english_auction" | "dutch_auction"
16199 | "core_coalition" | "stable_matching_count"
16200 | "gale_optimal" | "pareto_dominance"
16201 | "lerner_index"
16202 | "price_elasticity" | "supply_elasticity"
16203 | "income_elasticity" | "engel_curve" | "cross_elasticity"
16204 | "diff_in_diff" | "did_estimator" | "rdd_estimate"
16205 | "hann_w" | "hamming_w" | "blackman_w" | "barthann_w"
16207 | "nuttall_w" | "flattop_w" | "parzen_window" | "tukey_w"
16208 | "taylor_window" | "dpss_window" | "kaiserord_step"
16209 | "butter_lp_re" | "butter_hp_mag"
16210 | "cheby1_lp" | "cheby2_lp" | "ellip_lp" | "bessel_lp"
16211 | "notch_filter"
16212 | "sosfilt_step" | "lfilter_zi_init" | "filtfilt_pad"
16213 | "freqz_eval" | "freqs_eval" | "group_delay_eval"
16214 | "impulse_response_n"
16215 | "tf2zpk_step" | "zpk2tf_step" | "tf2sos_step"
16216 | "zpk2sos_step" | "sos2tf_step"
16217 | "bilinear_xform" | "bilinear_zpk_xform"
16218 | "firwin_lowpass" | "firwin_highpass"
16219 | "firwin_bandpass" | "firwin_bandstop"
16220 | "firwin2_freq" | "remez_design"
16221 | "stft_step" | "istft_step"
16222 | "cwt_morlet" | "ricker_wavelet" | "mexican_hat_wavelet"
16223 | "coherence_xy" | "csd_xy" | "welch_psd_avg"
16224 | "periodogram_basic" | "lombscargle_freq"
16225 | "hilbert_signal" | "envelope_amplitude"
16226 | "deconvolve_step" | "fftconvolve_step" | "oaconvolve_step"
16227 | "upfirdn_step" | "resample_poly_step" | "decimate_step"
16228 | "savgol_coef" | "detrend_linear"
16229 | "wiener_filter" | "medfilt_1d" | "peak_widths_at"
16230 | "dijkstra_relax" | "bellman_ford_relax"
16232 | "floyd_warshall_step" | "johnson_reweight"
16233 | "astar_search" | "bidirectional_dijkstra"
16234 | "yen_k_shortest" | "ida_star"
16235 | "bfs_count" | "dfs_postorder_done" | "topo_kahn_step"
16236 | "tarjan_scc_step" | "kosaraju_step"
16237 | "kruskal_step" | "prim_step" | "boruvka_step"
16238 | "reverse_delete_step"
16239 | "ford_fulkerson_step" | "edmonds_karp_bfs"
16240 | "dinic_step" | "push_relabel_relabel"
16241 | "stoer_wagner_step" | "karger_step"
16242 | "pagerank_iter" | "hits_authority" | "hits_hub"
16243 | "personalized_pagerank"
16244 | "centrality_degree" | "centrality_closeness"
16245 | "centrality_betweenness" | "centrality_eigenvector"
16246 | "centrality_katz" | "harmonic_centrality" | "load_centrality"
16247 | "clustering_coefficient" | "triangles_count" | "transitivity"
16248 | "modularity_score" | "louvain_gain"
16249 | "label_propagation" | "girvan_newman"
16250 | "articulation_point" | "bridge_edge"
16251 | "edge_connectivity" | "vertex_connectivity"
16252 | "biconnected_components"
16253 | "gx_diameter" | "gx_radius" | "gx_eccentricity"
16254 | "warshall_step"
16255 | "tsp_held_karp" | "tsp_nn_step" | "tsp_christofides"
16256 | "graph_coloring_greedy" | "welsh_powell"
16257 | "vf2_consistent" | "subgraph_isomorphism"
16258 | "hungarian_step" | "hopcroft_karp_step"
16259 | "bron_kerbosch"
16260 | "min_vertex_cover" | "max_independent_set"
16261 | "dominating_set_greedy" | "hamiltonian_path"
16262 | "min_steiner_tree" | "k_shortest_spanning"
16263 | "random_walk_hitting" | "simrank"
16264 | "df_groupby" | "df_aggregate" | "df_apply"
16266 | "df_transform" | "df_pivot" | "df_pivot_table"
16267 | "df_melt" | "df_stack" | "df_unstack"
16268 | "df_explode" | "df_get_dummies" | "df_crosstab"
16269 | "df_merge" | "df_join" | "df_concat"
16270 | "df_resample" | "df_rolling" | "df_expanding"
16271 | "df_ewm" | "df_shift" | "df_diff"
16272 | "df_pct_change" | "df_corr" | "df_cov"
16273 | "df_corrwith" | "df_describe" | "df_kurtosis"
16274 | "df_skew" | "df_sem" | "df_mad"
16275 | "df_dropna" | "df_fillna" | "df_interpolate"
16276 | "df_replace" | "df_isnull" | "df_notnull"
16277 | "df_sort_values" | "df_rank" | "df_quantile"
16278 | "df_value_counts" | "df_sample" | "df_nlargest"
16279 | "df_nsmallest" | "df_idxmax" | "df_idxmin"
16280 | "df_clip" | "df_round" | "df_to_datetime"
16281 | "df_to_timedelta" | "df_to_numeric" | "df_eval"
16282 | "df_query" | "df_filter" | "df_drop_duplicates"
16283 | "df_duplicated" | "df_set_index" | "df_reset_index"
16284 | "image_resize" | "image_grayscale" | "image_threshold"
16286 | "image_blur_gaussian" | "image_blur_box" | "image_sharpen"
16287 | "image_edge_canny" | "image_edge_sobel" | "image_edge_laplacian"
16288 | "image_dilate" | "image_erode" | "image_morphology_open"
16289 | "image_morphology_close" | "image_histogram" | "image_equalize"
16290 | "image_clahe" | "image_contrast" | "image_brightness"
16291 | "image_gamma" | "image_invert" | "image_sepia"
16292 | "image_posterize" | "image_solarize" | "convolve_2d"
16293 | "filter_median" | "filter_bilateral" | "filter_nlmeans"
16294 | "gabor_filter" | "hog_features" | "harris_corners"
16295 | "shi_tomasi_corners" | "sift_keypoints" | "orb_keypoints"
16296 | "surf_keypoints" | "template_match" | "face_detect_haar"
16297 | "watershed_segment" | "slic_superpixels" | "felzenszwalb_segment"
16298 | "graph_cut_segment" | "hough_lines" | "hough_circles"
16299 | "ransac_homography" | "optical_flow_lk" | "optical_flow_farneback"
16300 | "corner_subpix" | "image_rotate" | "image_flip_h"
16301 | "image_flip_v" | "image_emboss" | "image_motion_blur"
16302 | "arima_fit" | "arima_forecast" | "arma_order_select"
16304 | "sarimax_fit" | "garch_fit" | "ewma_smooth"
16305 | "holt_winters_additive" | "holt_winters_multiplicative" | "kalman_filter_step"
16306 | "kalman_smoother_step" | "var_fit" | "vecm_fit"
16307 | "johansen_test" | "phillips_perron" | "adfuller"
16308 | "kpss_test" | "breusch_godfrey" | "ljung_box_q"
16309 | "durbin_watson_d" | "granger_causality" | "cointegration_eg"
16310 | "seasonal_decompose" | "stl_decompose" | "acf_basis"
16311 | "pacf_basis" | "moving_average_filter" | "exp_smooth_simple"
16312 | "exp_smooth_double" | "markov_switching_ar" | "markov_switching_mr"
16313 | "arch_lm" | "state_space_kalman" | "ucm_unobserved_components"
16314 | "spectral_density_estimate" | "bayesian_step" | "pivoted_cholesky_var"
16315 | "sk_logistic_predict" | "sk_logistic_fit" | "sk_random_forest_fit"
16317 | "sk_gbt_fit" | "sk_xgb_fit" | "sk_lightgbm_fit"
16318 | "sk_svm_fit" | "sk_kmeans_fit" | "sk_dbscan_fit"
16319 | "sk_agglomerative_fit" | "sk_pca_fit" | "sk_tsne_fit"
16320 | "sk_umap_fit" | "sk_isolation_forest_fit" | "sk_lof_fit"
16321 | "sk_kfold_split" | "sk_stratified_kfold" | "sk_cross_val_score"
16322 | "sk_grid_search" | "sk_random_search" | "sk_bayes_search"
16323 | "sk_pipeline_fit" | "sk_standard_scaler" | "sk_min_max_scaler"
16324 | "sk_robust_scaler" | "sk_quantile_transform" | "sk_power_transform"
16325 | "sk_one_hot" | "sk_ordinal_encode" | "sk_label_encode"
16326 | "sk_tfidf" | "sk_count_vectorize" | "sk_silhouette"
16327 | "sk_calinski_harabasz" | "sk_davies_bouldin" | "sk_adjusted_rand"
16328 | "sk_mutual_info" | "sk_lda_topic" | "sk_nmf_topic"
16329 | "sk_word2vec_train" | "sk_doc2vec_train" | "sk_naive_bayes_predict"
16330 | "sk_knn_predict" | "sk_decision_tree_split"
16331 | "qubit_x" | "qubit_y" | "qubit_z"
16333 | "qubit_h" | "qubit_s" | "qubit_t"
16334 | "qubit_rx" | "qubit_ry" | "qubit_rz"
16335 | "qubit_u3" | "qubit_u2" | "qubit_u1"
16336 | "qubit_phase" | "qubit_cnot" | "qubit_cz"
16337 | "qubit_swap" | "qubit_ccx" | "qubit_measure"
16338 | "qubit_reset" | "bell_state" | "ghz_state"
16339 | "w_state" | "qft" | "inverse_qft"
16340 | "grover_iter" | "shor_period" | "vqe_step"
16341 | "qaoa_step" | "qpe_iteration" | "pauli_string_expect"
16342 | "circuit_depth" | "circuit_width" | "gate_decompose"
16343 | "ancilla_alloc" | "bloch_sphere_x" | "bloch_sphere_z"
16344 | "density_matrix_purity_q" | "entanglement_entropy" | "quantum_teleportation"
16345 | "superdense_coding" | "noise_model_depolarize"
16346 | "mirr_excel" | "accrint" | "cumipmt"
16348 | "cumprinc" | "dollarde" | "dollarfr"
16349 | "received" | "yieldmat" | "yielddisc"
16350 | "duration_macaulay" | "mduration" | "odddyield"
16351 | "disc_excel" | "effect" | "nominal"
16352 | "intrate" | "price_disc" | "cityhash64"
16353 | "farmhash_64" | "metro_hash_64" | "spookyhash_128"
16354 | "t1ha" | "highway_hash" | "fnv0_32"
16355 | "lose_lose"
16356 | "oat_hash" | "lz4_encode_block" | "snappy_encode"
16357 | "zstd_encode_step" | "brotli_encode_meta" | "lzma_encode_step"
16358 | "bz2_encode_step" | "lzo_encode_step" | "deflate_encode_huffman"
16359 | "lzw_encode" | "gzip_encode_step" | "uri_template_expand"
16360 | "uri_resolve" | "uri_normalize" | "percent_decode_url"
16361 | "url_encode_form" | "url_decode_form" | "punycode_decode_step"
16362 | "idn_normalize" | "url_origin" | "etag_validate"
16363 | "cache_control_parse" | "vary_match" | "content_negotiate"
16364 | "accept_lang_pick" | "range_header_parse" | "if_match_check"
16365 | "if_none_match_check" | "digest_auth_quote" | "www_auth_parse"
16366 | "iso8601_duration_parse" | "iso8601_duration_to_seconds" | "rrule_next_occurrence"
16368 | "cron_next_fire" | "date_round_iso" | "week_number_iso"
16369 | "fiscal_year_us" | "age_at_date" | "easter_western"
16370 | "easter_orthodox_year_2" | "chinese_new_year" | "solstice_winter"
16371 | "equinox_spring" | "rgb_to_oklab" | "oklab_to_rgb"
16372 | "rgb_to_cmyk" | "cmyk_to_rgb" | "rgb_to_xyz"
16373 | "xyz_to_rgb" | "rgb_to_yuv" | "yuv_to_rgb"
16374 | "luminance_relative" | "contrast_ratio" | "wcag_pass"
16375 | "color_temperature_kelvin" | "delta_e76" | "delta_e94"
16376 | "delta_e2000" | "color_blend_alpha" | "isbn10_check"
16377 | "isbn13_check" | "ean13_check" | "upc_check"
16378 | "eth_addr_check" | "btc_addr_check" | "ssn_check"
16379 | "vin_check" | "imei_check" | "iban_check"
16380 | "cusip_check" | "kde_silverman_bw" | "kde_scott_bw"
16381 | "kde_bandwidth_lscv" | "kde_epanechnikov" | "kde_gaussian_2d"
16382 | "kde_uniform" | "kde_triangular" | "kde_biweight"
16383 | "kde_triweight" | "kde_cosine" | "kde_logistic_kernel"
16384 | "mod_exp" | "modexp" | "powmod"
16386 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
16387 | "miller_rabin" | "millerrabin" | "is_probable_prime"
16388 | "derangements" | "stirling2" | "stirling_second"
16390 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
16391 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
16393 | "bs_delta" | "bsdelta" | "option_delta"
16395 | "bs_gamma" | "bsgamma" | "option_gamma"
16396 | "bs_vega" | "bsvega" | "option_vega"
16397 | "bs_theta" | "bstheta" | "option_theta"
16398 | "bs_rho" | "bsrho" | "option_rho"
16399 | "bond_duration" | "mac_duration"
16400 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
16402 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
16404 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
16405 | "pnorm" | "qnorm" | "pbinom" | "dbinom" | "ppois"
16407 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
16408 | "rbind" | "cbind"
16410 | "row_sums" | "rowSums" | "col_sums" | "colSums"
16411 | "row_means" | "rowMeans" | "col_means" | "colMeans"
16412 | "outer" | "crossprod" | "tcrossprod"
16413 | "nrow" | "ncol" | "prop_table" | "proptable"
16414 | "cummax" | "cummin" | "scale_vec" | "scale"
16416 | "which_fn" | "tabulate"
16417 | "duplicated" | "duped" | "rev_vec"
16418 | "seq_fn" | "rep_fn" | "rep"
16419 | "cut_bins" | "cut" | "find_interval" | "findInterval"
16420 | "ecdf_fn" | "ecdf" | "density_est" | "density"
16421 | "embed_ts" | "embed"
16422 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
16424 | "wilcox_test" | "wilcox" | "mann_whitney"
16425 | "prop_test" | "proptest" | "binom_test" | "binomtest"
16426 | "sapply" | "tapply" | "do_call" | "docall"
16428 | "kmeans" | "prcomp" | "pca"
16430 | "rnorm" | "runif" | "rexp" | "rbinom" | "rpois" | "rgeom"
16432 | "rgamma" | "rbeta" | "rchisq" | "rt" | "rf"
16433 | "rweibull" | "rlnorm" | "rcauchy"
16434 | "qunif" | "qexp" | "qweibull" | "qlnorm" | "qcauchy"
16436 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
16438 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
16440 | "lowess" | "loess" | "approx_fn" | "approx"
16442 | "lm_fit" | "lm"
16444 | "qgamma" | "qbeta" | "qchisq" | "qt_fn" | "qt" | "qf_fn" | "qf"
16446 | "qbinom" | "qpois"
16447 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
16449 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
16450 | "predict_lm" | "predict" | "confint_lm" | "confint"
16452 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
16454 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
16455 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
16456 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
16458 | "plot_svg" | "hist_svg" | "histogram_svg"
16459 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
16460 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
16461 | "donut_svg" | "donut" | "area_svg" | "area_chart"
16462 | "hbar_svg" | "hbar" | "radar_svg" | "radar" | "spider"
16463 | "candlestick_svg" | "candlestick" | "ohlc"
16464 | "violin_svg" | "violin" | "cor_heatmap" | "cor_matrix_svg"
16465 | "stacked_bar_svg" | "stacked_bar"
16466 | "wordcloud_svg" | "wordcloud" | "wcloud"
16467 | "treemap_svg" | "treemap"
16468 | "pvw"
16469 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
16471 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
16472 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
16473 | "ai" | "ai_agent" | "prompt" | "stream_prompt" | "stream_prompt_cb"
16475 | "tokens_of"
16476 | "ai_estimate" | "ai_cost" | "ai_history" | "ai_history_clear"
16477 | "ai_cache_clear" | "ai_cache_size"
16478 | "ai_mock_install" | "ai_mock_clear"
16479 | "ai_config_get" | "ai_config_set" | "ai_routing_get" | "ai_routing_set"
16480 | "ai_register_tool" | "ai_unregister_tool" | "ai_clear_tools" | "ai_tools_list"
16481 | "ai_filter" | "ai_map" | "ai_classify" | "ai_match" | "ai_sort" | "ai_dedupe"
16482 | "ai_extract" | "ai_summarize" | "ai_translate" | "ai_template"
16483 | "ai_session_new" | "ai_session_send" | "ai_session_history"
16484 | "ai_session_close" | "ai_session_reset"
16485 | "ai_session_export" | "ai_session_import"
16486 | "ai_memory_save" | "ai_memory_recall" | "ai_memory_forget"
16487 | "ai_memory_count" | "ai_memory_clear"
16488 | "ai_vision" | "ai_pdf" | "ai_grounded" | "ai_citations"
16489 | "ai_transcribe" | "ai_speak" | "ai_image" | "ai_image_edit" | "ai_image_variation"
16490 | "ai_models" | "ai_describe" | "ai_pricing" | "ai_dashboard"
16491 | "ai_moderate" | "ai_chunk" | "ai_warm" | "ai_compare"
16492 | "ai_last_thinking" | "ai_budget" | "ai_batch" | "ai_pmap"
16493 | "ai_file_upload" | "ai_file_list" | "ai_file_get" | "ai_file_delete"
16494 | "ai_file_anthropic_upload" | "ai_file_anthropic_list" | "ai_file_anthropic_delete"
16495 | "vec_cosine" | "vec_search" | "vec_topk"
16496 | "web_search_tool" | "fetch_url_tool" | "read_file_tool" | "run_code_tool"
16498 | "mcp_connect" | "mcp_close" | "mcp_tools" | "mcp_call"
16500 | "mcp_resource" | "mcp_resources" | "mcp_prompt" | "mcp_prompts"
16501 | "mcp_attach_to_ai" | "mcp_detach_from_ai" | "mcp_attached"
16502 | "mcp_server_start" | "mcp_serve_registered_tools"
16503 | "pty_spawn" | "pty_send" | "pty_read" | "pty_expect" | "pty_expect_table"
16505 | "pty_buffer" | "pty_alive" | "pty_eof" | "pty_close" | "pty_interact"
16506 | "pty_strip_ansi" | "pty_after_eof" | "pty_pending_events"
16507 | "stress_fp" | "stress_int" | "stress_cache" | "stress_branch"
16509 | "stress_sort" | "stress_alloc" | "stress_mmap" | "stress_disk"
16510 | "stress_iops" | "stress_net" | "stress_http" | "stress_dns"
16511 | "stress_fork" | "stress_thread" | "stress_aes" | "stress_compress"
16512 | "stress_regex" | "stress_json" | "stress_burst" | "stress_ramp"
16513 | "stress_oscillate" | "stress_all" | "stress_temp" | "stress_thermal_zones"
16514 | "stress_freq" | "stress_throttled" | "stress_load" | "stress_meminfo"
16515 | "stress_cores" | "stress_arm_kill_switch" | "stress_killed"
16516 | "stress_disarm_kill_switch"
16517 | "stress_metrics_record" | "stress_metrics_clear" | "stress_metrics_count"
16518 | "stress_metrics_export" | "stress_metrics_prometheus"
16519 | "stress_metrics_json" | "stress_metrics_csv" | "stress_metrics_watch"
16520 | "audit_log" | "audit_log_path"
16522 | "secrets_encrypt" | "secrets_decrypt" | "secrets_random_key" | "secrets_kdf"
16523 | "web_route" | "web_resources" | "web_root" | "web_routes_table"
16525 | "web_application_config" | "web_boot_application"
16526 | "web_render" | "web_render_partial" | "web_redirect"
16527 | "web_json" | "web_text" | "web_csv" | "web_markdown"
16528 | "web_params" | "web_request" | "web_set_header" | "web_status"
16529 | "web_before_action" | "web_after_action"
16530 | "web_session" | "web_session_set" | "web_session_get" | "web_session_clear"
16531 | "web_signed" | "web_unsigned"
16532 | "web_cookies" | "web_set_cookie"
16533 | "web_flash" | "web_flash_set" | "web_flash_get"
16534 | "web_validate" | "web_permit"
16535 | "web_password_hash" | "web_password_verify"
16536 | "web_token_for" | "web_token_consume" | "web_csrf_meta_tag"
16537 | "web_security_headers" | "web_can"
16538 | "web_h" | "web_truncate" | "web_pluralize" | "web_time_ago_in_words"
16539 | "web_image_tag" | "web_link_to" | "web_button_to"
16540 | "web_form_with" | "web_form_close"
16541 | "web_text_field" | "web_text_area" | "web_check_box"
16542 | "web_stylesheet_link_tag" | "web_javascript_link_tag"
16543 | "web_yield_content" | "web_content_for"
16544 | "web_etag" | "web_cache_get" | "web_cache_set"
16545 | "web_cache_delete" | "web_cache_clear"
16546 | "web_db_connect" | "web_db_execute" | "web_db_query"
16547 | "web_db_begin" | "web_db_commit" | "web_db_rollback"
16548 | "web_create_table" | "web_drop_table"
16549 | "web_add_column" | "web_remove_column"
16550 | "web_migrate" | "web_rollback"
16551 | "web_model_all" | "web_model_find" | "web_model_first" | "web_model_last"
16552 | "web_model_where" | "web_model_create" | "web_model_update"
16553 | "web_model_destroy" | "web_model_count" | "web_model_increment"
16554 | "web_model_paginate" | "web_model_search" | "web_model_soft_destroy"
16555 | "web_model_with"
16556 | "web_jobs_init" | "web_job_enqueue" | "web_job_dequeue"
16557 | "web_job_complete" | "web_job_fail"
16558 | "web_jobs_list" | "web_jobs_stats" | "web_job_purge"
16559 | "web_jsonapi_resource" | "web_jsonapi_collection" | "web_jsonapi_error"
16560 | "web_bearer_token" | "web_jwt_encode" | "web_jwt_decode"
16561 | "web_otp_secret" | "web_otp_generate" | "web_otp_verify"
16562 | "web_uuid" | "web_now" | "web_log" | "web_rate_limit"
16563 | "web_t" | "web_load_locale" | "web_openapi"
16564 | "web_faker_int" | "web_faker_email" | "web_faker_name"
16565 | "web_faker_sentence" | "web_faker_paragraph"
16566 => Some(name),
16567 _ => None,
16568 }
16569 }
16570
16571 fn is_reserved_hash_name(name: &str) -> bool {
16574 matches!(
16575 name,
16576 "b" | "pc"
16577 | "e"
16578 | "a"
16579 | "d"
16580 | "c"
16581 | "p"
16582 | "all"
16583 | "stryke::builtins"
16584 | "stryke::perl_compats"
16585 | "stryke::extensions"
16586 | "stryke::aliases"
16587 | "stryke::descriptions"
16588 | "stryke::categories"
16589 | "stryke::primaries"
16590 | "stryke::all"
16591 )
16592 }
16593
16594 const RESERVED_FUNCTION_NAMES: &'static [&'static str] = &[
16599 "y",
16600 "tr",
16601 "s",
16602 "m",
16603 "q",
16604 "qq",
16605 "qw",
16606 "qx",
16607 "qr",
16608 "if",
16609 "unless",
16610 "while",
16611 "until",
16612 "for",
16613 "foreach",
16614 "given",
16615 "when",
16616 "else",
16617 "elsif",
16618 "do",
16619 "eval",
16620 "return",
16621 "last",
16622 "next",
16623 "redo",
16624 "goto",
16625 "my",
16626 "our",
16627 "local",
16628 "state",
16629 "sub",
16630 "fn",
16631 "class",
16632 "struct",
16633 "enum",
16634 "trait",
16635 "use",
16636 "no",
16637 "require",
16638 "package",
16639 "BEGIN",
16640 "END",
16641 "CHECK",
16642 "INIT",
16643 "UNITCHECK",
16644 "and",
16645 "or",
16646 "not",
16647 "x",
16648 "eq",
16649 "ne",
16650 "lt",
16651 "gt",
16652 "le",
16653 "ge",
16654 "cmp",
16655 ];
16656
16657 fn check_udf_shadows_builtin(&self, name: &str, line: usize) -> PerlResult<()> {
16658 if !name.contains("::") {
16660 if Self::RESERVED_FUNCTION_NAMES.contains(&name) {
16661 return Err(self.syntax_err(
16662 format!("`{name}` is a reserved word and cannot be used as a function name"),
16663 line,
16664 ));
16665 }
16666 if Self::is_known_bareword(name)
16667 || Self::is_try_builtin_name(name)
16668 || crate::list_builtins::is_list_builtin_name(name)
16669 {
16670 return Err(self.syntax_err(
16671 format!(
16672"`{name}` is a stryke builtin and cannot be redefined (this is not Perl 5; use `fn` not `sub`, or pass --compat)"
16673 ),
16674 line,
16675 ));
16676 }
16677 }
16678 Ok(())
16679 }
16680
16681 fn check_hash_shadows_reserved(&self, name: &str, line: usize) -> PerlResult<()> {
16684 if Self::is_reserved_hash_name(name) {
16685 return Err(self.syntax_err(
16686 format!(
16687"`%{name}` is a stryke reserved hash and cannot be redefined (this is not Perl 5; pass --compat for Perl 5 mode)"
16688 ),
16689 line,
16690 ));
16691 }
16692 Ok(())
16693 }
16694
16695 fn validate_hash_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
16698 match &value.kind {
16699 ExprKind::Integer(_) | ExprKind::Float(_) => {
16700 return Err(self.syntax_err(
16701 "cannot assign scalar to hash — use %h = (key => value) or %h = %{$hashref}",
16702 line,
16703 ));
16704 }
16705 ExprKind::String(_) | ExprKind::InterpolatedString(_) | ExprKind::Bareword(_) => {
16706 return Err(self.syntax_err(
16707 "cannot assign string to hash — use %h = (key => value) or %h = %{$hashref}",
16708 line,
16709 ));
16710 }
16711 ExprKind::ArrayRef(_) => {
16712 return Err(self.syntax_err(
16713 "cannot assign arrayref to hash — use %h = @{$arrayref} for even-length list",
16714 line,
16715 ));
16716 }
16717 ExprKind::ScalarRef(inner) => {
16718 if matches!(inner.kind, ExprKind::ArrayVar(_)) {
16719 return Err(self.syntax_err(
16720 "cannot assign \\@array to hash — use %h = @array for even-length list",
16721 line,
16722 ));
16723 }
16724 if matches!(inner.kind, ExprKind::HashVar(_)) {
16725 return Err(self.syntax_err(
16726 "cannot assign \\%hash to hash — use %h = %other directly",
16727 line,
16728 ));
16729 }
16730 }
16731 ExprKind::HashRef(_) => {
16732 return Err(self.syntax_err(
16733 "cannot assign hashref to hash — use %h = %{$hashref} to dereference",
16734 line,
16735 ));
16736 }
16737 ExprKind::CodeRef { .. } => {
16738 return Err(self.syntax_err("cannot assign coderef to hash", line));
16739 }
16740 ExprKind::Undef => {
16741 return Err(
16742 self.syntax_err("cannot assign undef to hash — use %h = () to empty", line)
16743 );
16744 }
16745 ExprKind::List(items)
16746 if items.len() % 2 != 0
16747 && !items.iter().any(|e| {
16748 matches!(
16749 e.kind,
16750 ExprKind::ArrayVar(_)
16751 | ExprKind::HashVar(_)
16752 | ExprKind::FuncCall { .. }
16753 | ExprKind::Deref { .. }
16754 | ExprKind::ScalarVar(_)
16755 )
16756 }) =>
16757 {
16758 return Err(self.syntax_err(
16759 format!(
16760 "odd-length list ({} elements) in hash assignment — missing value for last key",
16761 items.len()
16762 ),
16763 line,
16764 ));
16765 }
16766 _ => {}
16767 }
16768 Ok(())
16769 }
16770
16771 fn validate_array_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
16776 if let ExprKind::Undef = &value.kind {
16777 return Err(
16778 self.syntax_err("cannot assign undef to array — use @a = () to empty", line)
16779 );
16780 }
16781 Ok(())
16782 }
16783
16784 fn validate_scalar_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
16787 if let ExprKind::List(items) = &value.kind {
16788 if items.len() > 1 {
16789 return Err(self.syntax_err(
16790 format!(
16791 "cannot assign {}-element list to scalar — Perl 5 silently takes last element; use ($x) = (list) or $x = $list[-1]",
16792 items.len()
16793 ),
16794 line,
16795 ));
16796 }
16797 }
16798 Ok(())
16799 }
16800
16801 fn validate_assignment(&self, target: &Expr, value: &Expr, line: usize) -> PerlResult<()> {
16803 if crate::compat_mode() {
16804 return Ok(());
16805 }
16806 match &target.kind {
16807 ExprKind::HashVar(_) => self.validate_hash_assignment(value, line),
16808 ExprKind::ArrayVar(_) => self.validate_array_assignment(value, line),
16809 ExprKind::ScalarVar(_) => self.validate_scalar_assignment(value, line),
16810 _ => Ok(()),
16811 }
16812 }
16813
16814 fn parse_block_or_bareword_cmp_block(&mut self) -> PerlResult<Block> {
16818 if matches!(self.peek(), Token::LBrace) {
16819 return self.parse_block();
16820 }
16821 let line = self.peek_line();
16822 if let Token::Ident(ref name) = self.peek().clone() {
16824 if matches!(
16825 self.peek_at(1),
16826 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
16827 ) {
16828 let name = name.clone();
16829 self.advance();
16830 let body = Expr {
16831 kind: ExprKind::FuncCall {
16832 name,
16833 args: vec![
16834 Expr {
16835 kind: ExprKind::ScalarVar("a".to_string()),
16836 line,
16837 },
16838 Expr {
16839 kind: ExprKind::ScalarVar("b".to_string()),
16840 line,
16841 },
16842 ],
16843 },
16844 line,
16845 };
16846 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
16847 }
16848 }
16849 let expr = self.parse_assign_expr_stop_at_pipe()?;
16851 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
16852 }
16853
16854 fn parse_fan_optional_progress(
16856 &mut self,
16857 which: &'static str,
16858 ) -> PerlResult<Option<Box<Expr>>> {
16859 let line = self.peek_line();
16860 if self.eat(&Token::Comma) {
16861 match self.peek() {
16862 Token::Ident(ref kw)
16863 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
16864 {
16865 self.advance();
16866 self.expect(&Token::FatArrow)?;
16867 return Ok(Some(Box::new(self.parse_assign_expr()?)));
16868 }
16869 _ => {
16870 return Err(self.syntax_err(
16871 format!("{which}: expected `progress => EXPR` after comma"),
16872 line,
16873 ));
16874 }
16875 }
16876 }
16877 if let Token::Ident(ref kw) = self.peek().clone() {
16878 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
16879 self.advance();
16880 self.expect(&Token::FatArrow)?;
16881 return Ok(Some(Box::new(self.parse_assign_expr()?)));
16882 }
16883 }
16884 Ok(None)
16885 }
16886
16887 fn parse_assign_expr_list_optional_progress(&mut self) -> PerlResult<(Expr, Option<Expr>)> {
16894 if self.in_pipe_rhs()
16900 && matches!(
16901 self.peek(),
16902 Token::Semicolon
16903 | Token::RBrace
16904 | Token::RParen
16905 | Token::Eof
16906 | Token::PipeForward
16907 | Token::Comma
16908 )
16909 {
16910 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
16911 }
16912 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
16913 loop {
16914 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
16915 break;
16916 }
16917 if matches!(
16918 self.peek(),
16919 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
16920 ) {
16921 break;
16922 }
16923 if self.peek_is_postfix_stmt_modifier_keyword() {
16924 break;
16925 }
16926 if let Token::Ident(ref kw) = self.peek().clone() {
16927 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
16928 self.advance();
16929 self.expect(&Token::FatArrow)?;
16930 let prog = self.parse_assign_expr_stop_at_pipe()?;
16931 return Ok((merge_expr_list(parts), Some(prog)));
16932 }
16933 }
16934 parts.push(self.parse_assign_expr_stop_at_pipe()?);
16935 }
16936 Ok((merge_expr_list(parts), None))
16937 }
16938
16939 fn parse_one_arg(&mut self) -> PerlResult<Expr> {
16940 if matches!(self.peek(), Token::LParen) {
16941 self.advance();
16942 let expr = self.parse_expression()?;
16943 self.expect(&Token::RParen)?;
16944 Ok(expr)
16945 } else {
16946 self.parse_assign_expr_stop_at_pipe()
16947 }
16948 }
16949
16950 fn parse_named_unary_arg(&mut self) -> PerlResult<Expr> {
16959 if matches!(self.peek(), Token::LParen) {
16960 self.advance();
16961 let expr = self.parse_expression()?;
16962 self.expect(&Token::RParen)?;
16963 Ok(expr)
16964 } else {
16965 self.parse_shift()
16966 }
16967 }
16968
16969 fn parse_one_arg_or_default(&mut self) -> PerlResult<Expr> {
16970 if matches!(
16977 self.peek(),
16978 Token::Semicolon
16980 | Token::RBrace
16981 | Token::RParen
16982 | Token::RBracket
16983 | Token::Eof
16984 | Token::Comma
16985 | Token::FatArrow
16986 | Token::PipeForward
16987 | Token::Question
16989 | Token::Colon
16990 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
16992 | Token::NumLe | Token::NumGe | Token::Spaceship
16993 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
16994 | Token::StrLe | Token::StrGe | Token::StrCmp
16995 | Token::LogAnd | Token::LogOr | Token::LogNot
16997 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
16998 | Token::DefinedOr
16999 | Token::Range | Token::RangeExclusive
17001 | Token::Assign | Token::PlusAssign | Token::MinusAssign
17003 | Token::MulAssign | Token::DivAssign | Token::ModAssign
17004 | Token::PowAssign | Token::DotAssign | Token::AndAssign
17005 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
17006 | Token::ShiftLeftAssign | Token::ShiftRightAssign
17007 | Token::BitAndAssign | Token::BitOrAssign
17008 ) {
17009 return Ok(Expr {
17010 kind: ExprKind::ScalarVar("_".into()),
17011 line: self.peek_line(),
17012 });
17013 }
17014 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
17018 let line = self.peek_line();
17019 self.advance(); self.advance(); return Ok(Expr {
17022 kind: ExprKind::ScalarVar("_".into()),
17023 line,
17024 });
17025 }
17026 self.parse_named_unary_arg()
17031 }
17032
17033 fn parse_one_arg_or_argv(&mut self) -> PerlResult<Expr> {
17035 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
17037 self.advance();
17038 if matches!(self.peek(), Token::RParen) {
17039 self.advance();
17040 return Ok(Expr {
17041 kind: ExprKind::ArrayVar("_".into()),
17042 line: self.peek_line(),
17043 });
17044 }
17045 let expr = self.parse_expression()?;
17046 self.expect(&Token::RParen)?;
17047 return Ok(expr);
17048 }
17049 if matches!(
17051 self.peek(),
17052 Token::Semicolon
17053 | Token::RBrace
17054 | Token::RParen
17055 | Token::Eof
17056 | Token::Comma
17057 | Token::PipeForward
17058 ) || self.peek_line() > line
17059 {
17060 Ok(Expr {
17061 kind: ExprKind::ArrayVar("_".into()),
17062 line,
17063 })
17064 } else {
17065 self.parse_assign_expr()
17066 }
17067 }
17068
17069 fn parse_builtin_args(&mut self) -> PerlResult<Vec<Expr>> {
17070 if matches!(self.peek(), Token::LParen) {
17071 self.advance();
17072 let args = self.parse_arg_list()?;
17073 self.expect(&Token::RParen)?;
17074 Ok(args)
17075 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
17076 Ok(vec![])
17079 } else {
17080 self.parse_list_until_terminator()
17081 }
17082 }
17083
17084 #[inline]
17088 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
17089 if matches!(self.peek(), Token::FatArrow) {
17090 Some(Expr {
17091 kind: ExprKind::String(name.to_string()),
17092 line,
17093 })
17094 } else {
17095 None
17096 }
17097 }
17098
17099 fn parse_hash_subscript_key(&mut self) -> PerlResult<Expr> {
17110 let line = self.peek_line();
17111 if let Token::Ident(ref k) = self.peek().clone() {
17112 if matches!(self.peek_at(1), Token::RBrace) && !Self::is_underscore_topic_slot(k) {
17113 let s = k.clone();
17114 self.advance();
17115 return Ok(Expr {
17116 kind: ExprKind::String(s),
17117 line,
17118 });
17119 }
17120 }
17121 if matches!(self.peek_at(1), Token::RBrace) {
17122 if let Some(s) = Self::operator_keyword_to_ident_str(self.peek()) {
17123 self.advance();
17124 return Ok(Expr {
17125 kind: ExprKind::String(s.to_string()),
17126 line,
17127 });
17128 }
17129 }
17130 self.parse_expression()
17131 }
17132
17133 #[inline]
17135 fn peek_is_glob_par_progress_kw(&self) -> bool {
17136 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
17137 && matches!(self.peek_at(1), Token::FatArrow)
17138 }
17139
17140 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> PerlResult<Vec<Expr>> {
17142 let mut args = Vec::new();
17143 loop {
17144 if matches!(self.peek(), Token::RParen | Token::Eof) {
17145 break;
17146 }
17147 if self.peek_is_glob_par_progress_kw() {
17148 break;
17149 }
17150 args.push(self.parse_assign_expr()?);
17151 match self.peek() {
17152 Token::RParen => break,
17153 Token::Comma => {
17154 self.advance();
17155 if matches!(self.peek(), Token::RParen) {
17156 break;
17157 }
17158 if self.peek_is_glob_par_progress_kw() {
17159 break;
17160 }
17161 }
17162 _ => {
17163 return Err(self.syntax_err(
17164 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
17165 self.peek_line(),
17166 ));
17167 }
17168 }
17169 }
17170 Ok(args)
17171 }
17172
17173 fn parse_pattern_list_glob_par_bare(&mut self) -> PerlResult<Vec<Expr>> {
17175 let mut args = Vec::new();
17176 loop {
17177 if matches!(
17178 self.peek(),
17179 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
17180 ) {
17181 break;
17182 }
17183 if self.peek_is_postfix_stmt_modifier_keyword() {
17184 break;
17185 }
17186 if self.peek_is_glob_par_progress_kw() {
17187 break;
17188 }
17189 args.push(self.parse_assign_expr()?);
17190 if !self.eat(&Token::Comma) {
17191 break;
17192 }
17193 if self.peek_is_glob_par_progress_kw() {
17194 break;
17195 }
17196 }
17197 Ok(args)
17198 }
17199
17200 fn parse_glob_par_or_par_sed_args(&mut self) -> PerlResult<(Vec<Expr>, Option<Box<Expr>>)> {
17202 if matches!(self.peek(), Token::LParen) {
17203 self.advance();
17204 let args = self.parse_pattern_list_until_rparen_or_progress()?;
17205 let progress = if self.peek_is_glob_par_progress_kw() {
17206 self.advance();
17207 self.expect(&Token::FatArrow)?;
17208 Some(Box::new(self.parse_assign_expr()?))
17209 } else {
17210 None
17211 };
17212 self.expect(&Token::RParen)?;
17213 Ok((args, progress))
17214 } else {
17215 let args = self.parse_pattern_list_glob_par_bare()?;
17216 let progress = if self.peek_is_glob_par_progress_kw() {
17218 self.advance();
17219 self.expect(&Token::FatArrow)?;
17220 Some(Box::new(self.parse_assign_expr()?))
17221 } else {
17222 None
17223 };
17224 Ok((args, progress))
17225 }
17226 }
17227
17228 pub(crate) fn parse_arg_list(&mut self) -> PerlResult<Vec<Expr>> {
17229 let mut args = Vec::new();
17230 let saved_no_pf = self.no_pipe_forward_depth;
17234 self.no_pipe_forward_depth = 0;
17235 while !matches!(
17236 self.peek(),
17237 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
17238 ) {
17239 let arg = match self.parse_assign_expr() {
17240 Ok(e) => e,
17241 Err(err) => {
17242 self.no_pipe_forward_depth = saved_no_pf;
17243 return Err(err);
17244 }
17245 };
17246 args.push(arg);
17247 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
17248 break;
17249 }
17250 }
17251 self.no_pipe_forward_depth = saved_no_pf;
17252 Ok(args)
17253 }
17254
17255 pub(crate) fn parse_slice_arg_list(&mut self, is_hash: bool) -> PerlResult<Vec<Expr>> {
17264 let mut args = Vec::new();
17265 let saved_no_pf = self.no_pipe_forward_depth;
17266 self.no_pipe_forward_depth = 0;
17267 while !matches!(
17268 self.peek(),
17269 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
17270 ) {
17271 let arg = match self.parse_slice_arg(is_hash) {
17272 Ok(e) => e,
17273 Err(err) => {
17274 self.no_pipe_forward_depth = saved_no_pf;
17275 return Err(err);
17276 }
17277 };
17278 args.push(arg);
17279 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
17280 break;
17281 }
17282 }
17283 self.no_pipe_forward_depth = saved_no_pf;
17284 Ok(args)
17285 }
17286
17287 fn parse_slice_arg(&mut self, is_hash: bool) -> PerlResult<Expr> {
17289 let line = self.peek_line();
17290
17291 if matches!(self.peek(), Token::Colon) {
17293 self.advance();
17294 return self.finish_slice_range(None, false, is_hash, line);
17295 }
17296 if matches!(self.peek(), Token::PackageSep) {
17297 self.advance();
17298 return self.finish_slice_range(None, true, is_hash, line);
17299 }
17300
17301 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
17304 let result = self.parse_slice_endpoint(is_hash);
17305 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
17306 let from_expr = result?;
17307
17308 if matches!(self.peek(), Token::Colon) {
17310 self.advance();
17311 return self.finish_slice_range(Some(Box::new(from_expr)), false, is_hash, line);
17312 }
17313 if matches!(self.peek(), Token::PackageSep) {
17314 self.advance();
17315 return self.finish_slice_range(Some(Box::new(from_expr)), true, is_hash, line);
17316 }
17317
17318 Ok(from_expr)
17319 }
17320
17321 fn finish_slice_range(
17328 &mut self,
17329 from: Option<Box<Expr>>,
17330 double: bool,
17331 is_hash: bool,
17332 line: usize,
17333 ) -> PerlResult<Expr> {
17334 let (to, step) = if double {
17335 let step_v = self.parse_slice_optional_endpoint(is_hash)?;
17337 (None, step_v)
17338 } else {
17339 let to_v = self.parse_slice_optional_endpoint(is_hash)?;
17341 let step_v = if matches!(self.peek(), Token::Colon) {
17342 self.advance();
17343 self.parse_slice_optional_endpoint(is_hash)?
17344 } else if matches!(self.peek(), Token::PackageSep) {
17345 return Err(
17346 self.syntax_err("Unexpected `::` after slice TO endpoint".to_string(), line)
17347 );
17348 } else {
17349 None
17350 };
17351 (to_v, step_v)
17352 };
17353
17354 if let (Some(f), Some(t)) = (from.as_ref(), to.as_ref()) {
17357 return Ok(Expr {
17358 kind: ExprKind::Range {
17359 from: f.clone(),
17360 to: t.clone(),
17361 exclusive: false,
17362 step,
17363 },
17364 line,
17365 });
17366 }
17367
17368 Ok(Expr {
17369 kind: ExprKind::SliceRange { from, to, step },
17370 line,
17371 })
17372 }
17373
17374 fn parse_slice_optional_endpoint(&mut self, is_hash: bool) -> PerlResult<Option<Box<Expr>>> {
17377 if matches!(
17378 self.peek(),
17379 Token::Colon
17380 | Token::PackageSep
17381 | Token::Comma
17382 | Token::RBracket
17383 | Token::RBrace
17384 | Token::Eof
17385 ) {
17386 return Ok(None);
17387 }
17388 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
17389 let r = self.parse_slice_endpoint(is_hash);
17390 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
17391 Ok(Some(Box::new(r?)))
17392 }
17393
17394 fn parse_slice_endpoint(&mut self, is_hash: bool) -> PerlResult<Expr> {
17398 if is_hash {
17399 if let Token::Ident(name) = self.peek().clone() {
17400 if matches!(
17401 self.peek_at(1),
17402 Token::Colon
17403 | Token::PackageSep
17404 | Token::Comma
17405 | Token::RBracket
17406 | Token::RBrace
17407 ) {
17408 let line = self.peek_line();
17409 self.advance();
17410 return Ok(Expr {
17411 kind: ExprKind::String(name),
17412 line,
17413 });
17414 }
17415 }
17416 }
17417 self.parse_assign_expr()
17418 }
17419
17420 fn parse_method_arg_list_no_paren(&mut self) -> PerlResult<Vec<Expr>> {
17424 let mut args = Vec::new();
17425 let call_line = self.prev_line();
17426 loop {
17427 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
17430 break;
17431 }
17432 if matches!(
17433 self.peek(),
17434 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
17435 ) {
17436 break;
17437 }
17438 if let Token::Ident(ref kw) = self.peek().clone() {
17439 if matches!(
17440 kw.as_str(),
17441 "if" | "unless" | "while" | "until" | "for" | "foreach"
17442 ) {
17443 break;
17444 }
17445 }
17446 if args.is_empty()
17449 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
17450 {
17451 break;
17452 }
17453 if args.is_empty() && self.peek_line() > call_line {
17456 break;
17457 }
17458 args.push(self.parse_assign_expr()?);
17459 if !self.eat(&Token::Comma) {
17460 break;
17461 }
17462 }
17463 Ok(args)
17464 }
17465
17466 fn peek_method_arg_infix_terminator(&self) -> bool {
17469 matches!(
17470 self.peek(),
17471 Token::Plus
17472 | Token::Minus
17473 | Token::Star
17474 | Token::Slash
17475 | Token::Percent
17476 | Token::Power
17477 | Token::Dot
17478 | Token::X
17479 | Token::NumEq
17480 | Token::NumNe
17481 | Token::NumLt
17482 | Token::NumGt
17483 | Token::NumLe
17484 | Token::NumGe
17485 | Token::Spaceship
17486 | Token::StrEq
17487 | Token::StrNe
17488 | Token::StrLt
17489 | Token::StrGt
17490 | Token::StrLe
17491 | Token::StrGe
17492 | Token::StrCmp
17493 | Token::LogAnd
17494 | Token::LogOr
17495 | Token::LogAndWord
17496 | Token::LogOrWord
17497 | Token::DefinedOr
17498 | Token::BitAnd
17499 | Token::BitOr
17500 | Token::BitXor
17501 | Token::ShiftLeft
17502 | Token::ShiftRight
17503 | Token::Range
17504 | Token::RangeExclusive
17505 | Token::BindMatch
17506 | Token::BindNotMatch
17507 | Token::Arrow
17508 | Token::Question
17510 | Token::Colon
17511 | Token::Assign
17513 | Token::PlusAssign
17514 | Token::MinusAssign
17515 | Token::MulAssign
17516 | Token::DivAssign
17517 | Token::ModAssign
17518 | Token::PowAssign
17519 | Token::DotAssign
17520 | Token::AndAssign
17521 | Token::OrAssign
17522 | Token::XorAssign
17523 | Token::DefinedOrAssign
17524 | Token::ShiftLeftAssign
17525 | Token::ShiftRightAssign
17526 | Token::BitAndAssign
17527 | Token::BitOrAssign
17528 )
17529 }
17530
17531 fn parse_list_until_terminator(&mut self) -> PerlResult<Vec<Expr>> {
17532 let mut args = Vec::new();
17533 let call_line = self.prev_line();
17538 loop {
17539 if matches!(
17540 self.peek(),
17541 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
17542 ) {
17543 break;
17544 }
17545 if let Token::Ident(ref kw) = self.peek().clone() {
17547 if matches!(
17548 kw.as_str(),
17549 "if" | "unless" | "while" | "until" | "for" | "foreach"
17550 ) {
17551 break;
17552 }
17553 }
17554 if args.is_empty() && self.peek_line() > call_line {
17561 break;
17562 }
17563 args.push(self.parse_assign_expr_stop_at_pipe()?);
17566 if !self.eat(&Token::Comma) {
17567 break;
17568 }
17569 }
17570 Ok(args)
17571 }
17572
17573 fn parse_forced_hashref_body(&mut self, line: usize) -> PerlResult<Expr> {
17580 let saved = self.pos;
17581 if let Ok(pairs) = self.try_parse_hash_ref() {
17582 return Ok(Expr {
17583 kind: ExprKind::HashRef(pairs),
17584 line,
17585 });
17586 }
17587 self.pos = saved;
17589 if matches!(self.peek(), Token::RBrace) {
17590 self.advance();
17591 return Ok(Expr {
17592 kind: ExprKind::HashRef(vec![]),
17593 line,
17594 });
17595 }
17596 let inner = self.parse_expression()?;
17600 self.expect(&Token::RBrace)?;
17601 let sentinel_key = Expr {
17602 kind: ExprKind::String("__HASH_SPREAD__".into()),
17603 line,
17604 };
17605 Ok(Expr {
17606 kind: ExprKind::HashRef(vec![(sentinel_key, inner)]),
17607 line,
17608 })
17609 }
17610
17611 fn try_parse_hash_ref(&mut self) -> PerlResult<Vec<(Expr, Expr)>> {
17612 let mut pairs = Vec::new();
17613 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
17614 let line = self.peek_line();
17619 let key = if let Token::Ident(ref name) = self.peek().clone() {
17620 if matches!(self.peek_at(1), Token::FatArrow)
17621 && !Self::is_underscore_topic_slot(name)
17622 {
17623 self.advance();
17624 Expr {
17625 kind: ExprKind::String(name.clone()),
17626 line,
17627 }
17628 } else {
17629 self.parse_assign_expr()?
17630 }
17631 } else {
17632 self.parse_assign_expr()?
17633 };
17634 if matches!(self.peek(), Token::RBrace | Token::Comma)
17638 && matches!(
17639 key.kind,
17640 ExprKind::HashVar(_)
17641 | ExprKind::Deref {
17642 kind: Sigil::Hash,
17643 ..
17644 }
17645 )
17646 {
17647 let sentinel_key = Expr {
17651 kind: ExprKind::String("__HASH_SPREAD__".into()),
17652 line,
17653 };
17654 pairs.push((sentinel_key, key));
17655 self.eat(&Token::Comma);
17656 continue;
17657 }
17658 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
17660 let val = self.parse_assign_expr()?;
17661 pairs.push((key, val));
17662 self.eat(&Token::Comma);
17663 } else {
17664 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
17665 }
17666 }
17667 self.expect(&Token::RBrace)?;
17668 Ok(pairs)
17669 }
17670
17671 fn parse_hashref_pairs_until(&mut self, term: &Token) -> PerlResult<Vec<(Expr, Expr)>> {
17676 let mut pairs = Vec::new();
17677 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
17678 && !matches!(self.peek(), Token::Eof)
17679 {
17680 let line = self.peek_line();
17681 let key = if let Token::Ident(ref name) = self.peek().clone() {
17682 if matches!(self.peek_at(1), Token::FatArrow)
17683 && !Self::is_underscore_topic_slot(name)
17684 {
17685 self.advance();
17686 Expr {
17687 kind: ExprKind::String(name.clone()),
17688 line,
17689 }
17690 } else {
17691 self.parse_assign_expr()?
17692 }
17693 } else {
17694 self.parse_assign_expr()?
17695 };
17696 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
17697 let val = self.parse_assign_expr()?;
17698 pairs.push((key, val));
17699 self.eat(&Token::Comma);
17700 } else {
17701 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
17702 }
17703 }
17704 Ok(pairs)
17705 }
17706
17707 fn interp_chain_subscripts(
17713 &self,
17714 chars: &[char],
17715 i: &mut usize,
17716 mut base: Expr,
17717 line: usize,
17718 ) -> Expr {
17719 loop {
17720 let (after, requires_subscript) =
17722 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
17723 (*i + 2, true)
17724 } else {
17725 (*i, false)
17726 };
17727 if after >= chars.len() {
17728 break;
17729 }
17730 match chars[after] {
17731 '[' => {
17732 *i = after + 1;
17733 let mut idx_str = String::new();
17734 while *i < chars.len() && chars[*i] != ']' {
17735 idx_str.push(chars[*i]);
17736 *i += 1;
17737 }
17738 if *i < chars.len() {
17739 *i += 1;
17740 }
17741 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
17742 Expr {
17743 kind: ExprKind::ScalarVar(rest.to_string()),
17744 line,
17745 }
17746 } else if let Ok(n) = idx_str.parse::<i64>() {
17747 Expr {
17748 kind: ExprKind::Integer(n),
17749 line,
17750 }
17751 } else {
17752 Expr {
17753 kind: ExprKind::String(idx_str),
17754 line,
17755 }
17756 };
17757 base = Expr {
17758 kind: ExprKind::ArrowDeref {
17759 expr: Box::new(base),
17760 index: Box::new(idx_expr),
17761 kind: DerefKind::Array,
17762 },
17763 line,
17764 };
17765 }
17766 '{' => {
17767 *i = after + 1;
17768 let mut key = String::new();
17769 let mut depth = 1usize;
17770 while *i < chars.len() && depth > 0 {
17771 if chars[*i] == '{' {
17772 depth += 1;
17773 } else if chars[*i] == '}' {
17774 depth -= 1;
17775 if depth == 0 {
17776 break;
17777 }
17778 }
17779 key.push(chars[*i]);
17780 *i += 1;
17781 }
17782 if *i < chars.len() {
17783 *i += 1;
17784 }
17785 let key_expr = if let Some(rest) = key.strip_prefix('$') {
17786 Expr {
17787 kind: ExprKind::ScalarVar(rest.to_string()),
17788 line,
17789 }
17790 } else {
17791 Expr {
17792 kind: ExprKind::String(key),
17793 line,
17794 }
17795 };
17796 base = Expr {
17797 kind: ExprKind::ArrowDeref {
17798 expr: Box::new(base),
17799 index: Box::new(key_expr),
17800 kind: DerefKind::Hash,
17801 },
17802 line,
17803 };
17804 }
17805 _ => {
17806 if requires_subscript {
17807 }
17809 break;
17810 }
17811 }
17812 }
17813 base
17814 }
17815
17816 fn no_interop_check_scalar_var_name(&self, name: &str, line: usize) -> PerlResult<()> {
17820 if crate::no_interop_mode() && (name == "a" || name == "b") {
17821 return Err(self.syntax_err(
17822 format!(
17823 "stryke uses `$_0` / `$_1` instead of `${}` (--no-interop is active)",
17824 name
17825 ),
17826 line,
17827 ));
17828 }
17829 Ok(())
17830 }
17831
17832 fn parse_interpolated_string(&self, s: &str, line: usize) -> PerlResult<Expr> {
17833 let mut parts = Vec::new();
17835 let mut literal = String::new();
17836 let chars: Vec<char> = s.chars().collect();
17837 let mut i = 0;
17838
17839 'istr: while i < chars.len() {
17840 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
17841 literal.push('$');
17842 i += 1;
17843 continue;
17844 }
17845 if chars[i] == LITERAL_AT_IN_DQUOTE {
17846 literal.push('@');
17847 i += 1;
17848 continue;
17849 }
17850 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
17852 literal.push('\\');
17853 i += 1;
17854 }
17856 if chars[i] == '$' && i + 1 < chars.len() {
17857 if !literal.is_empty() {
17858 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
17859 }
17860 i += 1; while i < chars.len() && chars[i].is_whitespace() {
17863 i += 1;
17864 }
17865 if i >= chars.len() {
17866 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
17867 }
17868 if chars[i] == '#' {
17870 i += 1;
17871 let mut sname = String::from("#");
17872 while i < chars.len()
17873 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
17874 {
17875 sname.push(chars[i]);
17876 i += 1;
17877 }
17878 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
17879 sname.push_str("::");
17880 i += 2;
17881 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
17882 sname.push(chars[i]);
17883 i += 1;
17884 }
17885 }
17886 self.no_interop_check_scalar_var_name(&sname, line)?;
17887 parts.push(StringPart::ScalarVar(sname));
17888 continue;
17889 }
17890 if chars[i] == '$' {
17894 let next_c = chars.get(i + 1).copied();
17895 let is_pid = match next_c {
17896 None => true,
17897 Some(c)
17898 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
17899 {
17900 true
17901 }
17902 _ => false,
17903 };
17904 if is_pid {
17905 parts.push(StringPart::ScalarVar("$$".to_string()));
17906 i += 1; continue;
17908 }
17909 i += 1; }
17911 if chars[i] == '{' {
17912 i += 1;
17918 let mut inner = String::new();
17919 let mut depth = 1usize;
17920 while i < chars.len() && depth > 0 {
17921 match chars[i] {
17922 '{' => depth += 1,
17923 '}' => {
17924 depth -= 1;
17925 if depth == 0 {
17926 break;
17927 }
17928 }
17929 _ => {}
17930 }
17931 inner.push(chars[i]);
17932 i += 1;
17933 }
17934 if i < chars.len() {
17935 i += 1; }
17937
17938 let trimmed = inner.trim();
17942 let is_expr = trimmed.starts_with('$')
17943 || trimmed.starts_with('\\')
17944 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|']);
17947 let mut base: Expr = if is_expr {
17948 match parse_expression_from_str(trimmed, "<interp>") {
17952 Ok(e) => Expr {
17953 kind: ExprKind::Deref {
17954 expr: Box::new(e),
17955 kind: Sigil::Scalar,
17956 },
17957 line,
17958 },
17959 Err(_) => Expr {
17960 kind: ExprKind::ScalarVar(inner.clone()),
17961 line,
17962 },
17963 }
17964 } else {
17965 self.no_interop_check_scalar_var_name(&inner, line)?;
17967 Expr {
17968 kind: ExprKind::ScalarVar(inner),
17969 line,
17970 }
17971 };
17972
17973 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
17977 parts.push(StringPart::Expr(base));
17978 } else if chars[i] == '^' {
17979 let mut name = String::from("^");
17981 i += 1;
17982 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
17983 name.push(chars[i]);
17984 i += 1;
17985 }
17986 if i < chars.len() && chars[i] == '{' {
17987 i += 1; let mut key = String::new();
17989 let mut depth = 1;
17990 while i < chars.len() && depth > 0 {
17991 if chars[i] == '{' {
17992 depth += 1;
17993 } else if chars[i] == '}' {
17994 depth -= 1;
17995 if depth == 0 {
17996 break;
17997 }
17998 }
17999 key.push(chars[i]);
18000 i += 1;
18001 }
18002 if i < chars.len() {
18003 i += 1;
18004 }
18005 let key_expr = if let Some(rest) = key.strip_prefix('$') {
18006 Expr {
18007 kind: ExprKind::ScalarVar(rest.to_string()),
18008 line,
18009 }
18010 } else {
18011 Expr {
18012 kind: ExprKind::String(key),
18013 line,
18014 }
18015 };
18016 parts.push(StringPart::Expr(Expr {
18017 kind: ExprKind::HashElement {
18018 hash: name,
18019 key: Box::new(key_expr),
18020 },
18021 line,
18022 }));
18023 } else if i < chars.len() && chars[i] == '[' {
18024 i += 1;
18025 let mut idx_str = String::new();
18026 while i < chars.len() && chars[i] != ']' {
18027 idx_str.push(chars[i]);
18028 i += 1;
18029 }
18030 if i < chars.len() {
18031 i += 1;
18032 }
18033 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
18034 Expr {
18035 kind: ExprKind::ScalarVar(rest.to_string()),
18036 line,
18037 }
18038 } else if let Ok(n) = idx_str.parse::<i64>() {
18039 Expr {
18040 kind: ExprKind::Integer(n),
18041 line,
18042 }
18043 } else {
18044 Expr {
18045 kind: ExprKind::String(idx_str),
18046 line,
18047 }
18048 };
18049 parts.push(StringPart::Expr(Expr {
18050 kind: ExprKind::ArrayElement {
18051 array: name,
18052 index: Box::new(idx_expr),
18053 },
18054 line,
18055 }));
18056 } else {
18057 self.no_interop_check_scalar_var_name(&name, line)?;
18058 parts.push(StringPart::ScalarVar(name));
18059 }
18060 } else if chars[i].is_alphabetic() || chars[i] == '_' {
18061 let mut name = String::new();
18062 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18063 name.push(chars[i]);
18064 i += 1;
18065 }
18066 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18071 name.push_str("::");
18072 i += 2;
18073 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18074 name.push(chars[i]);
18075 i += 1;
18076 }
18077 }
18078 let is_topic_slot = name == "_"
18083 || (name.len() > 1
18084 && name.starts_with('_')
18085 && name[1..].bytes().all(|b| b.is_ascii_digit()));
18086 if is_topic_slot {
18087 let try_indexed = chars.get(i) == Some(&'<')
18089 && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit());
18090 let mut handled_indexed = false;
18091 if try_indexed {
18092 let mut j = i + 1;
18093 while j < chars.len() && chars[j].is_ascii_digit() {
18094 j += 1;
18095 }
18096 let digits: String = chars[i + 1..j].iter().collect();
18097 if let Ok(n) = digits.parse::<usize>() {
18098 if n >= 1 {
18099 for _ in 0..n {
18100 name.push('<');
18101 }
18102 i = j;
18103 handled_indexed = true;
18104 }
18105 }
18106 }
18107 if !handled_indexed {
18108 while i < chars.len() && chars[i] == '<' {
18109 name.push('<');
18110 i += 1;
18111 }
18112 }
18113 }
18114 self.no_interop_check_scalar_var_name(&name, line)?;
18119 let mut base = if i < chars.len() && chars[i] == '{' {
18124 i += 1; let mut key = String::new();
18127 let mut depth = 1;
18128 while i < chars.len() && depth > 0 {
18129 if chars[i] == '{' {
18130 depth += 1;
18131 } else if chars[i] == '}' {
18132 depth -= 1;
18133 if depth == 0 {
18134 break;
18135 }
18136 }
18137 key.push(chars[i]);
18138 i += 1;
18139 }
18140 if i < chars.len() {
18141 i += 1;
18142 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
18144 Expr {
18145 kind: ExprKind::ScalarVar(rest.to_string()),
18146 line,
18147 }
18148 } else {
18149 Expr {
18150 kind: ExprKind::String(key),
18151 line,
18152 }
18153 };
18154 Expr {
18155 kind: ExprKind::HashElement {
18156 hash: name,
18157 key: Box::new(key_expr),
18158 },
18159 line,
18160 }
18161 } else if i < chars.len() && chars[i] == '[' {
18162 i += 1;
18164 let mut idx_str = String::new();
18165 while i < chars.len() && chars[i] != ']' {
18166 idx_str.push(chars[i]);
18167 i += 1;
18168 }
18169 if i < chars.len() {
18170 i += 1;
18171 }
18172 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
18173 Expr {
18174 kind: ExprKind::ScalarVar(rest.to_string()),
18175 line,
18176 }
18177 } else if let Ok(n) = idx_str.parse::<i64>() {
18178 Expr {
18179 kind: ExprKind::Integer(n),
18180 line,
18181 }
18182 } else {
18183 Expr {
18184 kind: ExprKind::String(idx_str),
18185 line,
18186 }
18187 };
18188 Expr {
18189 kind: ExprKind::ArrayElement {
18190 array: name,
18191 index: Box::new(idx_expr),
18192 },
18193 line,
18194 }
18195 } else {
18196 Expr {
18198 kind: ExprKind::ScalarVar(name),
18199 line,
18200 }
18201 };
18202
18203 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18207 parts.push(StringPart::Expr(base));
18208 } else if chars[i].is_ascii_digit() {
18209 if chars[i] == '0' {
18211 i += 1;
18212 if i < chars.len() && chars[i].is_ascii_digit() {
18213 return Err(self.syntax_err(
18214 "Numeric variables with more than one digit may not start with '0'",
18215 line,
18216 ));
18217 }
18218 parts.push(StringPart::ScalarVar("0".into()));
18219 } else {
18220 let start = i;
18221 while i < chars.len() && chars[i].is_ascii_digit() {
18222 i += 1;
18223 }
18224 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
18225 }
18226 } else {
18227 let c = chars[i];
18228 let probe = c.to_string();
18229 if VMHelper::is_special_scalar_name_for_get(&probe)
18235 || matches!(c, '\'' | '`' | '&')
18236 {
18237 i += 1;
18238 if i < chars.len() && chars[i] == '{' {
18240 i += 1; let mut key = String::new();
18242 let mut depth = 1;
18243 while i < chars.len() && depth > 0 {
18244 if chars[i] == '{' {
18245 depth += 1;
18246 } else if chars[i] == '}' {
18247 depth -= 1;
18248 if depth == 0 {
18249 break;
18250 }
18251 }
18252 key.push(chars[i]);
18253 i += 1;
18254 }
18255 if i < chars.len() {
18256 i += 1;
18257 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
18259 Expr {
18260 kind: ExprKind::ScalarVar(rest.to_string()),
18261 line,
18262 }
18263 } else {
18264 Expr {
18265 kind: ExprKind::String(key),
18266 line,
18267 }
18268 };
18269 let mut base = Expr {
18270 kind: ExprKind::HashElement {
18271 hash: probe,
18272 key: Box::new(key_expr),
18273 },
18274 line,
18275 };
18276 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18277 parts.push(StringPart::Expr(base));
18278 } else {
18279 let mut base = Expr {
18281 kind: ExprKind::ScalarVar(probe),
18282 line,
18283 };
18284 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18285 if matches!(base.kind, ExprKind::ScalarVar(_)) {
18286 if let ExprKind::ScalarVar(name) = base.kind {
18288 self.no_interop_check_scalar_var_name(&name, line)?;
18289 parts.push(StringPart::ScalarVar(name));
18290 }
18291 } else {
18292 parts.push(StringPart::Expr(base));
18293 }
18294 }
18295 } else {
18296 literal.push('$');
18297 literal.push(c);
18298 i += 1;
18299 }
18300 }
18301 } else if chars[i] == '@' && i + 1 < chars.len() {
18302 let next = chars[i + 1];
18303 if next == '$' {
18305 if !literal.is_empty() {
18306 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18307 }
18308 i += 1; debug_assert_eq!(chars[i], '$');
18310 i += 1; while i < chars.len() && chars[i].is_whitespace() {
18312 i += 1;
18313 }
18314 if i >= chars.len() {
18315 return Err(self.syntax_err(
18316 "Expected variable or block after `@$` in double-quoted string",
18317 line,
18318 ));
18319 }
18320 let inner_expr = if chars[i] == '{' {
18321 i += 1;
18322 let start = i;
18323 let mut depth = 1usize;
18324 while i < chars.len() && depth > 0 {
18325 match chars[i] {
18326 '{' => depth += 1,
18327 '}' => {
18328 depth -= 1;
18329 if depth == 0 {
18330 break;
18331 }
18332 }
18333 _ => {}
18334 }
18335 i += 1;
18336 }
18337 if depth != 0 {
18338 return Err(self.syntax_err(
18339 "Unterminated `${ ... }` after `@` in double-quoted string",
18340 line,
18341 ));
18342 }
18343 let inner: String = chars[start..i].iter().collect();
18344 i += 1; parse_expression_from_str(inner.trim(), "-e")?
18346 } else {
18347 let mut name = String::new();
18348 if chars[i] == '^' {
18349 name.push('^');
18350 i += 1;
18351 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
18352 {
18353 name.push(chars[i]);
18354 i += 1;
18355 }
18356 } else {
18357 while i < chars.len()
18358 && (chars[i].is_alphanumeric()
18359 || chars[i] == '_'
18360 || chars[i] == ':')
18361 {
18362 name.push(chars[i]);
18363 i += 1;
18364 }
18365 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18366 name.push_str("::");
18367 i += 2;
18368 while i < chars.len()
18369 && (chars[i].is_alphanumeric() || chars[i] == '_')
18370 {
18371 name.push(chars[i]);
18372 i += 1;
18373 }
18374 }
18375 }
18376 if name.is_empty() {
18377 return Err(self.syntax_err(
18378 "Expected identifier after `@$` in double-quoted string",
18379 line,
18380 ));
18381 }
18382 Expr {
18383 kind: ExprKind::ScalarVar(name),
18384 line,
18385 }
18386 };
18387 parts.push(StringPart::Expr(Expr {
18388 kind: ExprKind::Deref {
18389 expr: Box::new(inner_expr),
18390 kind: Sigil::Array,
18391 },
18392 line,
18393 }));
18394 continue 'istr;
18395 }
18396 if next == '{' {
18397 if !literal.is_empty() {
18398 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18399 }
18400 i += 2; let start = i;
18402 let mut depth = 1usize;
18403 while i < chars.len() && depth > 0 {
18404 match chars[i] {
18405 '{' => depth += 1,
18406 '}' => {
18407 depth -= 1;
18408 if depth == 0 {
18409 break;
18410 }
18411 }
18412 _ => {}
18413 }
18414 i += 1;
18415 }
18416 if depth != 0 {
18417 return Err(
18418 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
18419 );
18420 }
18421 let inner: String = chars[start..i].iter().collect();
18422 i += 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
18424 parts.push(StringPart::Expr(Expr {
18425 kind: ExprKind::Deref {
18426 expr: Box::new(inner_expr),
18427 kind: Sigil::Array,
18428 },
18429 line,
18430 }));
18431 continue 'istr;
18432 }
18433 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
18434 literal.push(chars[i]);
18435 i += 1;
18436 } else {
18437 if !literal.is_empty() {
18438 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18439 }
18440 i += 1;
18441 let mut name = String::new();
18442 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
18443 name.push(chars[i]);
18444 i += 1;
18445 } else {
18446 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18447 name.push(chars[i]);
18448 i += 1;
18449 }
18450 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18451 name.push_str("::");
18452 i += 2;
18453 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
18454 {
18455 name.push(chars[i]);
18456 i += 1;
18457 }
18458 }
18459 }
18460 if i < chars.len() && chars[i] == '[' {
18461 i += 1;
18462 let start_inner = i;
18463 let mut depth = 1usize;
18464 while i < chars.len() && depth > 0 {
18465 match chars[i] {
18466 '[' => depth += 1,
18467 ']' => depth -= 1,
18468 _ => {}
18469 }
18470 if depth == 0 {
18471 let inner: String = chars[start_inner..i].iter().collect();
18472 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
18474 parts.push(StringPart::Expr(Expr {
18475 kind: ExprKind::ArraySlice {
18476 array: name.clone(),
18477 indices,
18478 },
18479 line,
18480 }));
18481 continue 'istr;
18482 }
18483 i += 1;
18484 }
18485 return Err(self.syntax_err(
18486 "Unterminated [ in array slice inside quoted string",
18487 line,
18488 ));
18489 }
18490 parts.push(StringPart::ArrayVar(name));
18491 }
18492 } else if chars[i] == '#'
18493 && i + 1 < chars.len()
18494 && chars[i + 1] == '{'
18495 && !crate::compat_mode()
18496 {
18497 if !literal.is_empty() {
18499 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18500 }
18501 i += 2; let mut inner = String::new();
18503 let mut depth = 1usize;
18504 while i < chars.len() && depth > 0 {
18505 match chars[i] {
18506 '{' => depth += 1,
18507 '}' => {
18508 depth -= 1;
18509 if depth == 0 {
18510 break;
18511 }
18512 }
18513 _ => {}
18514 }
18515 inner.push(chars[i]);
18516 i += 1;
18517 }
18518 if i < chars.len() {
18519 i += 1; }
18521 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
18522 parts.push(StringPart::Expr(expr));
18523 } else {
18524 literal.push(chars[i]);
18525 i += 1;
18526 }
18527 }
18528 if !literal.is_empty() {
18529 parts.push(StringPart::Literal(literal));
18530 }
18531
18532 if parts.len() == 1 {
18533 if let StringPart::Literal(s) = &parts[0] {
18534 return Ok(Expr {
18535 kind: ExprKind::String(s.clone()),
18536 line,
18537 });
18538 }
18539 }
18540 if parts.is_empty() {
18541 return Ok(Expr {
18542 kind: ExprKind::String(String::new()),
18543 line,
18544 });
18545 }
18546
18547 Ok(Expr {
18548 kind: ExprKind::InterpolatedString(parts),
18549 line,
18550 })
18551 }
18552
18553 fn expr_to_overload_key(&self, e: &Expr) -> PerlResult<String> {
18554 match &e.kind {
18555 ExprKind::String(s) => Ok(s.clone()),
18556 _ => Err(self.syntax_err(
18557 "overload key must be a string literal (e.g. '\"\"' or '+')",
18558 e.line,
18559 )),
18560 }
18561 }
18562
18563 fn expr_to_overload_sub(&mut self, e: &Expr) -> PerlResult<String> {
18564 match &e.kind {
18565 ExprKind::String(s) => Ok(s.clone()),
18566 ExprKind::Integer(n) => Ok(n.to_string()),
18567 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
18568 ExprKind::CodeRef { params, body } => {
18572 let id = self.next_overload_anon_id;
18573 self.next_overload_anon_id = self.next_overload_anon_id.saturating_add(1);
18574 let name = format!("__overload_anon_{}", id);
18575 self.pending_synthetic_subs.push(Statement {
18576 label: None,
18577 kind: StmtKind::SubDecl {
18578 name: name.clone(),
18579 params: params.clone(),
18580 body: body.clone(),
18581 prototype: None,
18582 },
18583 line: e.line,
18584 });
18585 Ok(name)
18586 }
18587 _ => Err(self.syntax_err(
18588 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
18589 e.line,
18590 )),
18591 }
18592 }
18593}
18594
18595fn merge_expr_list(parts: Vec<Expr>) -> Expr {
18596 if parts.len() == 1 {
18597 parts.into_iter().next().unwrap()
18598 } else {
18599 let line = parts.first().map(|e| e.line).unwrap_or(0);
18600 Expr {
18601 kind: ExprKind::List(parts),
18602 line,
18603 }
18604 }
18605}
18606
18607pub fn parse_expression_from_str(s: &str, file: &str) -> PerlResult<Expr> {
18609 let mut lexer = Lexer::new_with_file(s, file);
18610 let tokens = lexer.tokenize()?;
18611 let mut parser = Parser::new_with_file(tokens, file);
18612 let e = parser.parse_expression()?;
18613 if !parser.at_eof() {
18614 return Err(parser.syntax_err(
18615 "Extra tokens in embedded string expression",
18616 parser.peek_line(),
18617 ));
18618 }
18619 Ok(e)
18620}
18621
18622pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> PerlResult<Expr> {
18624 let mut lexer = Lexer::new_with_file(s, file);
18625 let tokens = lexer.tokenize()?;
18626 let mut parser = Parser::new_with_file(tokens, file);
18627 let stmts = parser.parse_statements()?;
18628 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
18629 let inner = Expr {
18630 kind: ExprKind::CodeRef {
18631 params: vec![],
18632 body: stmts,
18633 },
18634 line: inner_line,
18635 };
18636 Ok(Expr {
18637 kind: ExprKind::Do(Box::new(inner)),
18638 line,
18639 })
18640}
18641
18642pub fn parse_slice_indices_from_str(s: &str, file: &str) -> PerlResult<Vec<Expr>> {
18645 let mut lexer = Lexer::new_with_file(s, file);
18646 let tokens = lexer.tokenize()?;
18647 let mut parser = Parser::new_with_file(tokens, file);
18648 parser.parse_arg_list()
18649}
18650
18651pub fn parse_format_value_line(line: &str) -> PerlResult<Vec<Expr>> {
18652 let trimmed = line.trim();
18653 if trimmed.is_empty() {
18654 return Ok(vec![]);
18655 }
18656 let mut lexer = Lexer::new(trimmed);
18657 let tokens = lexer.tokenize()?;
18658 let mut parser = Parser::new(tokens);
18659 let mut exprs = Vec::new();
18660 loop {
18661 if parser.at_eof() {
18662 break;
18663 }
18664 exprs.push(parser.parse_assign_expr()?);
18666 if parser.eat(&Token::Comma) {
18667 continue;
18668 }
18669 if !parser.at_eof() {
18670 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
18671 }
18672 break;
18673 }
18674 Ok(exprs)
18675}
18676
18677#[cfg(test)]
18678mod tests {
18679 use super::*;
18680
18681 fn parse_ok(code: &str) -> Program {
18682 let mut lexer = Lexer::new(code);
18683 let tokens = lexer.tokenize().expect("tokenize");
18684 let mut parser = Parser::new(tokens);
18685 parser.parse_program().expect("parse")
18686 }
18687
18688 fn parse_err(code: &str) -> String {
18689 let mut lexer = Lexer::new(code);
18690 let tokens = match lexer.tokenize() {
18691 Ok(t) => t,
18692 Err(e) => return e.message,
18693 };
18694 let mut parser = Parser::new(tokens);
18695 parser.parse_program().unwrap_err().message
18696 }
18697
18698 #[test]
18699 fn parse_empty_program() {
18700 let p = parse_ok("");
18701 assert!(p.statements.is_empty());
18702 }
18703
18704 #[test]
18705 fn parse_semicolons_only() {
18706 let p = parse_ok(";;");
18707 assert!(p.statements.len() <= 3);
18708 }
18709
18710 #[test]
18711 fn parse_simple_scalar_assignment() {
18712 let p = parse_ok("$x = 1");
18713 assert_eq!(p.statements.len(), 1);
18714 }
18715
18716 #[test]
18717 fn parse_simple_array_assignment() {
18718 let p = parse_ok("@arr = (1, 2, 3)");
18719 assert_eq!(p.statements.len(), 1);
18720 }
18721
18722 #[test]
18723 fn parse_simple_hash_assignment() {
18724 let p = parse_ok("%h = (a => 1, b => 2)");
18725 assert_eq!(p.statements.len(), 1);
18726 }
18727
18728 #[test]
18729 fn parse_subroutine_decl() {
18730 let p = parse_ok("fn foo { 1 }");
18731 assert_eq!(p.statements.len(), 1);
18732 match &p.statements[0].kind {
18733 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
18734 _ => panic!("expected SubDecl"),
18735 }
18736 }
18737
18738 #[test]
18739 fn parse_subroutine_with_prototype() {
18740 let p = parse_ok("fn foo ($$) { 1 }");
18741 assert_eq!(p.statements.len(), 1);
18742 match &p.statements[0].kind {
18743 StmtKind::SubDecl { prototype, .. } => {
18744 assert!(prototype.is_some());
18745 }
18746 _ => panic!("expected SubDecl"),
18747 }
18748 }
18749
18750 #[test]
18751 fn parse_anonymous_fn() {
18752 let p = parse_ok("my $f = fn { 1 }");
18753 assert_eq!(p.statements.len(), 1);
18754 }
18755
18756 #[test]
18757 fn parse_if_statement() {
18758 let p = parse_ok("if (1) { 2 }");
18759 assert_eq!(p.statements.len(), 1);
18760 matches!(&p.statements[0].kind, StmtKind::If { .. });
18761 }
18762
18763 #[test]
18764 fn parse_if_elsif_else() {
18765 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
18766 assert_eq!(p.statements.len(), 1);
18767 }
18768
18769 #[test]
18770 fn parse_unless_statement() {
18771 let p = parse_ok("unless (0) { 1 }");
18772 assert_eq!(p.statements.len(), 1);
18773 }
18774
18775 #[test]
18776 fn parse_while_loop() {
18777 let p = parse_ok("while ($x) { $x-- }");
18778 assert_eq!(p.statements.len(), 1);
18779 }
18780
18781 #[test]
18782 fn parse_until_loop() {
18783 let p = parse_ok("until ($x) { $x++ }");
18784 assert_eq!(p.statements.len(), 1);
18785 }
18786
18787 #[test]
18788 fn parse_for_c_style() {
18789 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
18790 assert_eq!(p.statements.len(), 1);
18791 }
18792
18793 #[test]
18794 fn parse_foreach_loop() {
18795 let p = parse_ok("foreach my $x (@arr) { 1 }");
18796 assert_eq!(p.statements.len(), 1);
18797 }
18798
18799 #[test]
18800 fn parse_loop_with_label() {
18801 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
18802 assert_eq!(p.statements.len(), 1);
18803 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
18804 }
18805
18806 #[test]
18807 fn parse_begin_block() {
18808 let p = parse_ok("BEGIN { 1 }");
18809 assert_eq!(p.statements.len(), 1);
18810 matches!(&p.statements[0].kind, StmtKind::Begin(_));
18811 }
18812
18813 #[test]
18814 fn parse_end_block() {
18815 let p = parse_ok("END { 1 }");
18816 assert_eq!(p.statements.len(), 1);
18817 matches!(&p.statements[0].kind, StmtKind::End(_));
18818 }
18819
18820 #[test]
18821 fn parse_package_statement() {
18822 let p = parse_ok("package Foo::Bar");
18823 assert_eq!(p.statements.len(), 1);
18824 match &p.statements[0].kind {
18825 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
18826 _ => panic!("expected Package"),
18827 }
18828 }
18829
18830 #[test]
18831 fn parse_use_statement() {
18832 let p = parse_ok("use strict");
18833 assert_eq!(p.statements.len(), 1);
18834 }
18835
18836 #[test]
18837 fn parse_no_statement() {
18838 let p = parse_ok("no warnings");
18839 assert_eq!(p.statements.len(), 1);
18840 }
18841
18842 #[test]
18843 fn parse_require_bareword() {
18844 let p = parse_ok("require Foo::Bar");
18845 assert_eq!(p.statements.len(), 1);
18846 }
18847
18848 #[test]
18849 fn parse_require_string() {
18850 let p = parse_ok(r#"require "foo.pl""#);
18851 assert_eq!(p.statements.len(), 1);
18852 }
18853
18854 #[test]
18855 fn parse_eval_block() {
18856 let p = parse_ok("eval { 1 }");
18857 assert_eq!(p.statements.len(), 1);
18858 }
18859
18860 #[test]
18861 fn parse_eval_string() {
18862 let p = parse_ok(r#"eval "1 + 2""#);
18863 assert_eq!(p.statements.len(), 1);
18864 }
18865
18866 #[test]
18867 fn parse_qw_word_list() {
18868 let p = parse_ok("my @a = qw(foo bar baz)");
18869 assert_eq!(p.statements.len(), 1);
18870 }
18871
18872 #[test]
18873 fn parse_q_string() {
18874 let p = parse_ok("my $s = q{hello}");
18875 assert_eq!(p.statements.len(), 1);
18876 }
18877
18878 #[test]
18879 fn parse_qq_string() {
18880 let p = parse_ok(r#"my $s = qq(hello $x)"#);
18881 assert_eq!(p.statements.len(), 1);
18882 }
18883
18884 #[test]
18885 fn parse_regex_match() {
18886 let p = parse_ok(r#"$x =~ /foo/"#);
18887 assert_eq!(p.statements.len(), 1);
18888 }
18889
18890 #[test]
18891 fn parse_regex_substitution() {
18892 let p = parse_ok(r#"$x =~ s/foo/bar/g"#);
18893 assert_eq!(p.statements.len(), 1);
18894 }
18895
18896 #[test]
18897 fn parse_transliterate() {
18898 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/"#);
18899 assert_eq!(p.statements.len(), 1);
18900 }
18901
18902 #[test]
18903 fn parse_ternary_operator() {
18904 let p = parse_ok("my $x = $a ? 1 : 2");
18905 assert_eq!(p.statements.len(), 1);
18906 }
18907
18908 #[test]
18909 fn parse_arrow_method_call() {
18910 let p = parse_ok("$obj->method()");
18911 assert_eq!(p.statements.len(), 1);
18912 }
18913
18914 #[test]
18915 fn parse_arrow_deref_hash() {
18916 let p = parse_ok("$r->{key}");
18917 assert_eq!(p.statements.len(), 1);
18918 }
18919
18920 #[test]
18921 fn parse_arrow_deref_array() {
18922 let p = parse_ok("$r->[0]");
18923 assert_eq!(p.statements.len(), 1);
18924 }
18925
18926 #[test]
18927 fn parse_chained_arrow_deref() {
18928 let p = parse_ok("$r->{a}[0]{b}");
18929 assert_eq!(p.statements.len(), 1);
18930 }
18931
18932 #[test]
18933 fn parse_my_multiple_vars() {
18934 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3)");
18935 assert_eq!(p.statements.len(), 1);
18936 }
18937
18938 #[test]
18939 fn parse_our_scalar() {
18940 let p = parse_ok("our $VERSION = '1.0'");
18941 assert_eq!(p.statements.len(), 1);
18942 }
18943
18944 #[test]
18945 fn parse_local_scalar() {
18946 let p = parse_ok("local $/ = undef");
18947 assert_eq!(p.statements.len(), 1);
18948 }
18949
18950 #[test]
18951 fn parse_state_variable() {
18952 let p = parse_ok("fn Test::counter { state $n = 0; $n++ }");
18953 assert_eq!(p.statements.len(), 1);
18954 }
18955
18956 #[test]
18957 fn parse_postfix_if() {
18958 let p = parse_ok("print 1 if $x");
18959 assert_eq!(p.statements.len(), 1);
18960 }
18961
18962 #[test]
18963 fn parse_postfix_unless() {
18964 let p = parse_ok("die 'error' unless $ok");
18965 assert_eq!(p.statements.len(), 1);
18966 }
18967
18968 #[test]
18969 fn parse_postfix_while() {
18970 let p = parse_ok("$x++ while $x < 10");
18971 assert_eq!(p.statements.len(), 1);
18972 }
18973
18974 #[test]
18975 fn parse_postfix_for() {
18976 let p = parse_ok("print for @arr");
18977 assert_eq!(p.statements.len(), 1);
18978 }
18979
18980 #[test]
18981 fn parse_last_next_redo() {
18982 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
18983 assert_eq!(p.statements.len(), 1);
18984 }
18985
18986 #[test]
18987 fn parse_return_statement() {
18988 let p = parse_ok("fn foo { return 42 }");
18989 assert_eq!(p.statements.len(), 1);
18990 }
18991
18992 #[test]
18993 fn parse_wantarray() {
18994 let p = parse_ok("fn foo { wantarray ? @a : $a }");
18995 assert_eq!(p.statements.len(), 1);
18996 }
18997
18998 #[test]
18999 fn parse_caller_builtin() {
19000 let p = parse_ok("my @c = caller");
19001 assert_eq!(p.statements.len(), 1);
19002 }
19003
19004 #[test]
19005 fn parse_ref_to_array() {
19006 let p = parse_ok("my $r = \\@arr");
19007 assert_eq!(p.statements.len(), 1);
19008 }
19009
19010 #[test]
19011 fn parse_ref_to_hash() {
19012 let p = parse_ok("my $r = \\%hash");
19013 assert_eq!(p.statements.len(), 1);
19014 }
19015
19016 #[test]
19017 fn parse_ref_to_scalar() {
19018 let p = parse_ok("my $r = \\$x");
19019 assert_eq!(p.statements.len(), 1);
19020 }
19021
19022 #[test]
19023 fn parse_deref_scalar() {
19024 let p = parse_ok("my $v = $$r");
19025 assert_eq!(p.statements.len(), 1);
19026 }
19027
19028 #[test]
19029 fn parse_deref_array() {
19030 let p = parse_ok("my @a = @$r");
19031 assert_eq!(p.statements.len(), 1);
19032 }
19033
19034 #[test]
19035 fn parse_deref_hash() {
19036 let p = parse_ok("my %h = %$r");
19037 assert_eq!(p.statements.len(), 1);
19038 }
19039
19040 #[test]
19041 fn parse_blessed_ref() {
19042 let p = parse_ok("bless $r, 'Foo'");
19043 assert_eq!(p.statements.len(), 1);
19044 }
19045
19046 #[test]
19047 fn parse_heredoc_basic() {
19048 let p = parse_ok("my $s = <<END;\nfoo\nEND");
19049 assert_eq!(p.statements.len(), 1);
19050 }
19051
19052 #[test]
19053 fn parse_heredoc_quoted() {
19054 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
19055 assert_eq!(p.statements.len(), 1);
19056 }
19057
19058 #[test]
19059 fn parse_do_block() {
19060 let p = parse_ok("my $x = do { 1 + 2 }");
19061 assert_eq!(p.statements.len(), 1);
19062 }
19063
19064 #[test]
19065 fn parse_do_file() {
19066 let p = parse_ok(r#"do "foo.pl""#);
19067 assert_eq!(p.statements.len(), 1);
19068 }
19069
19070 #[test]
19071 fn parse_map_expression() {
19072 let p = parse_ok("my @b = map { $_ * 2 } @a");
19073 assert_eq!(p.statements.len(), 1);
19074 }
19075
19076 #[test]
19077 fn parse_grep_expression() {
19078 let p = parse_ok("my @b = grep { $_ > 0 } @a");
19079 assert_eq!(p.statements.len(), 1);
19080 }
19081
19082 #[test]
19083 fn parse_sort_expression() {
19084 let p = parse_ok("my @b = sort { $a <=> $b } @a");
19085 assert_eq!(p.statements.len(), 1);
19086 }
19087
19088 #[test]
19089 fn parse_pipe_forward() {
19090 let p = parse_ok("@a |> map { $_ * 2 }");
19091 assert_eq!(p.statements.len(), 1);
19092 }
19093
19094 #[test]
19095 fn parse_expression_from_str_simple() {
19096 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
19097 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
19098 }
19099
19100 #[test]
19101 fn parse_expression_from_str_extra_tokens_error() {
19102 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
19103 assert!(err.message.contains("Extra tokens"));
19104 }
19105
19106 #[test]
19107 fn parse_slice_indices_from_str_basic() {
19108 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
19109 assert_eq!(indices.len(), 3);
19110 }
19111
19112 #[test]
19113 fn parse_format_value_line_empty() {
19114 let exprs = parse_format_value_line("").unwrap();
19115 assert!(exprs.is_empty());
19116 }
19117
19118 #[test]
19119 fn parse_format_value_line_single() {
19120 let exprs = parse_format_value_line("$x").unwrap();
19121 assert_eq!(exprs.len(), 1);
19122 }
19123
19124 #[test]
19125 fn parse_format_value_line_multiple() {
19126 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
19127 assert_eq!(exprs.len(), 3);
19128 }
19129
19130 #[test]
19131 fn parse_unclosed_brace_error() {
19132 let err = parse_err("fn foo {");
19133 assert!(!err.is_empty());
19134 }
19135
19136 #[test]
19137 fn parse_unclosed_paren_error() {
19138 let err = parse_err("print (1, 2");
19139 assert!(!err.is_empty());
19140 }
19141
19142 #[test]
19143 fn parse_invalid_statement_error() {
19144 let err = parse_err("???");
19145 assert!(!err.is_empty());
19146 }
19147
19148 #[test]
19149 fn merge_expr_list_single() {
19150 let e = Expr {
19151 kind: ExprKind::Integer(1),
19152 line: 1,
19153 };
19154 let merged = merge_expr_list(vec![e.clone()]);
19155 matches!(merged.kind, ExprKind::Integer(1));
19156 }
19157
19158 #[test]
19159 fn merge_expr_list_multiple() {
19160 let e1 = Expr {
19161 kind: ExprKind::Integer(1),
19162 line: 1,
19163 };
19164 let e2 = Expr {
19165 kind: ExprKind::Integer(2),
19166 line: 1,
19167 };
19168 let merged = merge_expr_list(vec![e1, e2]);
19169 matches!(merged.kind, ExprKind::List(_));
19170 }
19171}