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 pending_thread_input: Option<Expr>,
129 suppress_slash_as_div: u32,
132 pub suppress_m_regex: u32,
135 suppress_colon_range: u32,
139 suppress_tilde_range: u32,
145 thread_last_mode: bool,
148 pub parsing_module: bool,
151 list_construct_close_pos: Option<usize>,
159 pending_synthetic_subs: Vec<Statement>,
164 next_overload_anon_id: u32,
166 pub bare_positional_indices: std::collections::HashSet<usize>,
173}
174
175impl Parser {
176 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
177 Self::new_with_file(tokens, "-e")
178 }
179
180 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
181 Self {
182 tokens,
183 pos: 0,
184 next_rate_limit_slot: 0,
185 suppress_indirect_paren_call: 0,
186 pipe_rhs_depth: 0,
187 no_pipe_forward_depth: 0,
188 suppress_scalar_hash_brace: 0,
189 next_desugar_tmp: 0,
190 error_file: file.into(),
191 declared_subs: std::collections::HashSet::new(),
192 suppress_parenless_call: 0,
193 pending_thread_input: None,
194 suppress_slash_as_div: 0,
195 suppress_m_regex: 0,
196 suppress_colon_range: 0,
197 suppress_tilde_range: 0,
198 thread_last_mode: false,
199 pending_synthetic_subs: Vec::new(),
200 next_overload_anon_id: 0,
201 parsing_module: false,
202 list_construct_close_pos: None,
203 bare_positional_indices: std::collections::HashSet::new(),
204 block_depth: 0,
205 }
206 }
207
208 fn alloc_desugar_tmp(&mut self) -> u32 {
209 let n = self.next_desugar_tmp;
210 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
211 n
212 }
213
214 #[inline]
218 fn in_pipe_rhs(&self) -> bool {
219 self.pipe_rhs_depth > 0
220 }
221
222 fn pipe_supplies_slurped_list_operand(&self) -> bool {
225 self.in_pipe_rhs()
226 && (matches!(
227 self.peek(),
228 Token::Semicolon
229 | Token::RBrace
230 | Token::RParen
231 | Token::Eof
232 | Token::Comma
233 | Token::PipeForward
234 ) || self.peek_line() > self.prev_line())
235 }
236
237 #[inline]
242 fn pipe_placeholder_list(&self, line: usize) -> Expr {
243 Expr {
244 kind: ExprKind::List(vec![]),
245 line,
246 }
247 }
248
249 fn is_block_then_list_pipe_builtin(name: &str) -> bool {
254 matches!(
255 name,
256 "pfirst"
257 | "pany"
258 | "any"
259 | "all"
260 | "none"
261 | "first"
262 | "take_while"
263 | "drop_while"
264 | "skip_while"
265 | "reject"
266 | "grepv"
267 | "tap"
268 | "peek"
269 | "group_by"
270 | "chunk_by"
271 | "partition"
272 | "min_by"
273 | "max_by"
274 | "zip_with"
275 | "count_by"
276 )
277 }
278
279 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
290 let line = expr.line;
291 let topic = || Expr {
292 kind: ExprKind::ScalarVar("_".into()),
293 line,
294 };
295 match expr.kind {
296 ExprKind::Bareword(ref name) => Expr {
297 kind: ExprKind::FuncCall {
298 name: name.clone(),
299 args: vec![topic()],
300 },
301 line,
302 },
303 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
305 kind: ExprKind::Unlink(vec![topic()]),
306 line,
307 },
308 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
309 kind: ExprKind::Chmod(vec![topic()]),
310 line,
311 },
312 ExprKind::Stat(_) => expr,
314 ExprKind::Lstat(_) => expr,
315 ExprKind::Readlink(_) => expr,
316 ExprKind::Rev(ref inner) => {
318 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
319 Expr {
320 kind: ExprKind::Rev(Box::new(topic())),
321 line,
322 }
323 } else {
324 expr
325 }
326 }
327 _ => expr,
328 }
329 }
330
331 fn parse_assign_expr_stop_at_pipe(&mut self) -> PerlResult<Expr> {
339 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
340 let r = self.parse_assign_expr();
341 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
342 r
343 }
344
345 fn syntax_err(&self, message: impl Into<String>, line: usize) -> PerlError {
346 PerlError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
347 }
348
349 fn try_parse_coderef_listop_args(&mut self, line: usize) -> PerlResult<Option<Vec<Expr>>> {
356 if !matches!(self.peek(), Token::ScalarVar(_) | Token::Backslash) {
357 return Ok(None);
358 }
359 let f = self.parse_assign_expr_stop_at_pipe()?;
360 let _ = self.eat(&Token::Comma);
361 let list = if self.in_pipe_rhs()
362 && matches!(
363 self.peek(),
364 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
365 ) {
366 self.pipe_placeholder_list(line)
367 } else {
368 self.parse_expression()?
369 };
370 Ok(Some(vec![f, list]))
371 }
372
373 fn alloc_rate_limit_slot(&mut self) -> u32 {
374 let s = self.next_rate_limit_slot;
375 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
376 s
377 }
378
379 fn peek(&self) -> &Token {
380 self.tokens
381 .get(self.pos)
382 .map(|(t, _)| t)
383 .unwrap_or(&Token::Eof)
384 }
385
386 fn peek_line(&self) -> usize {
387 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
388 }
389
390 fn peek_at(&self, offset: usize) -> &Token {
391 self.tokens
392 .get(self.pos + offset)
393 .map(|(t, _)| t)
394 .unwrap_or(&Token::Eof)
395 }
396
397 fn advance(&mut self) -> (Token, usize) {
398 let tok = self
399 .tokens
400 .get(self.pos)
401 .cloned()
402 .unwrap_or((Token::Eof, 0));
403 self.pos += 1;
404 tok
405 }
406
407 fn prev_line(&self) -> usize {
409 if self.pos > 0 {
410 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
411 } else {
412 0
413 }
414 }
415
416 fn looks_like_hashref(&self) -> bool {
425 debug_assert!(matches!(self.peek(), Token::LBrace));
426 let tok1 = self.peek_at(1);
427 let tok2 = self.peek_at(2);
428 match tok1 {
429 Token::RBrace => true,
430 Token::Ident(_)
431 | Token::SingleString(_)
432 | Token::DoubleString(_)
433 | Token::ScalarVar(_)
434 | Token::Integer(_) => matches!(tok2, Token::FatArrow),
435 Token::HashVar(_) => matches!(tok2, Token::RBrace | Token::Comma),
436 _ => false,
437 }
438 }
439
440 fn expect(&mut self, expected: &Token) -> PerlResult<usize> {
441 let (tok, line) = self.advance();
442 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
443 Ok(line)
444 } else {
445 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
446 }
447 }
448
449 fn eat(&mut self, expected: &Token) -> bool {
450 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
451 self.advance();
452 true
453 } else {
454 false
455 }
456 }
457
458 fn at_eof(&self) -> bool {
459 matches!(self.peek(), Token::Eof)
460 }
461
462 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
464 matches!(
465 tok,
466 Token::RParen
467 | Token::Semicolon
468 | Token::Comma
469 | Token::RBrace
470 | Token::Eof
471 | Token::LogAnd
472 | Token::LogOr
473 | Token::LogAndWord
474 | Token::LogOrWord
475 | Token::PipeForward
476 )
477 }
478
479 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
483 if crate::compat_mode() {
485 return false;
486 }
487 if self.peek_line() == stmt_line {
488 return false;
489 }
490 matches!(
491 self.peek(),
492 Token::Ident(ref kw) if matches!(kw.as_str(),
493 "use" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
494 | "if" | "unless" | "while" | "until" | "for" | "foreach"
495 | "return" | "last" | "next" | "redo" | "package" | "require"
496 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
497 | "fn" | "class" | "abstract" | "final" | "trait"
502 | "state" | "mysync" | "oursync"
503 )
504 )
505 }
506
507 fn next_is_new_statement_start(&self, stmt_line: usize) -> bool {
511 if crate::compat_mode() {
512 return false;
513 }
514 if self.peek_line() == stmt_line {
515 return false;
516 }
517 matches!(
518 self.peek(),
519 Token::ScalarVar(_)
520 | Token::DerefScalarVar(_)
521 | Token::ArrayVar(_)
522 | Token::HashVar(_)
523 | Token::LBrace
524 ) || self.next_is_new_stmt_keyword(stmt_line)
525 }
526
527 pub fn parse_program(&mut self) -> PerlResult<Program> {
530 let mut statements = self.parse_statements()?;
531 if !self.pending_synthetic_subs.is_empty() {
535 let synthetics = std::mem::take(&mut self.pending_synthetic_subs);
536 let mut combined = Vec::with_capacity(synthetics.len() + statements.len());
537 combined.extend(synthetics);
538 combined.append(&mut statements);
539 statements = combined;
540 }
541 Ok(Program { statements })
542 }
543
544 pub fn parse_statements(&mut self) -> PerlResult<Vec<Statement>> {
546 let mut statements = Vec::new();
547 while !self.at_eof() {
548 if matches!(self.peek(), Token::Semicolon) {
549 let line = self.peek_line();
550 self.advance();
551 statements.push(Statement {
552 label: None,
553 kind: StmtKind::Empty,
554 line,
555 });
556 continue;
557 }
558 statements.push(self.parse_statement()?);
559 }
560 Ok(statements)
561 }
562
563 fn parse_statement(&mut self) -> PerlResult<Statement> {
566 let line = self.peek_line();
567
568 let label = match self.peek().clone() {
571 Token::Ident(_) => {
572 if matches!(self.peek_at(1), Token::Colon)
573 && !matches!(self.peek_at(2), Token::Colon)
574 {
575 let (tok, _) = self.advance();
576 let l = match tok {
577 Token::Ident(l) => l,
578 _ => unreachable!(),
579 };
580 self.advance(); Some(l)
582 } else {
583 None
584 }
585 }
586 _ => None,
587 };
588
589 let mut stmt = match self.peek().clone() {
590 Token::FormatDecl { .. } => {
591 let tok_line = self.peek_line();
592 let (tok, _) = self.advance();
593 match tok {
594 Token::FormatDecl { name, lines } => Statement {
595 label: label.clone(),
596 kind: StmtKind::FormatDecl { name, lines },
597 line: tok_line,
598 },
599 _ => unreachable!(),
600 }
601 }
602 Token::Ident(ref kw) => match kw.as_str() {
603 "if" => self.parse_if()?,
604 "unless" => self.parse_unless()?,
605 "while" => {
606 let mut s = self.parse_while()?;
607 if let StmtKind::While {
608 label: ref mut lbl, ..
609 } = s.kind
610 {
611 *lbl = label.clone();
612 }
613 s
614 }
615 "until" => {
616 let mut s = self.parse_until()?;
617 if let StmtKind::Until {
618 label: ref mut lbl, ..
619 } = s.kind
620 {
621 *lbl = label.clone();
622 }
623 s
624 }
625 "for" => {
626 let mut s = self.parse_for_or_foreach()?;
627 match s.kind {
628 StmtKind::For {
629 label: ref mut lbl, ..
630 }
631 | StmtKind::Foreach {
632 label: ref mut lbl, ..
633 } => *lbl = label.clone(),
634 _ => {}
635 }
636 s
637 }
638 "foreach" => {
639 let mut s = self.parse_foreach()?;
640 if let StmtKind::Foreach {
641 label: ref mut lbl, ..
642 } = s.kind
643 {
644 *lbl = label.clone();
645 }
646 s
647 }
648 "sub" => {
649 if crate::no_interop_mode() {
650 return Err(self.syntax_err(
651 "stryke uses `fn` instead of `sub` (--no-interop is active)",
652 self.peek_line(),
653 ));
654 }
655 self.parse_sub_decl(true)?
656 }
657 "fn" => self.parse_sub_decl(false)?,
658 "struct" => {
659 if crate::compat_mode() {
660 return Err(self.syntax_err(
661 "`struct` is a stryke extension (disabled by --compat)",
662 self.peek_line(),
663 ));
664 }
665 self.parse_struct_decl()?
666 }
667 "enum" => {
668 if crate::compat_mode() {
669 return Err(self.syntax_err(
670 "`enum` is a stryke extension (disabled by --compat)",
671 self.peek_line(),
672 ));
673 }
674 self.parse_enum_decl()?
675 }
676 "class" => {
677 if crate::compat_mode() {
678 return Err(self.syntax_err(
680 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
681 self.peek_line(),
682 ));
683 }
684 self.parse_class_decl(false, false)?
685 }
686 "abstract" => {
687 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
689 return Err(self.syntax_err(
690 "`abstract` must be followed by `class`",
691 self.peek_line(),
692 ));
693 }
694 self.parse_class_decl(true, false)?
695 }
696 "final" => {
697 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
699 return Err(self
700 .syntax_err("`final` must be followed by `class`", self.peek_line()));
701 }
702 self.parse_class_decl(false, true)?
703 }
704 "trait" => {
705 if crate::compat_mode() {
706 return Err(self.syntax_err(
707 "`trait` is a stryke extension (disabled by --compat)",
708 self.peek_line(),
709 ));
710 }
711 self.parse_trait_decl()?
712 }
713 "my" => self.parse_my_our_local("my", false)?,
714 "state" => self.parse_my_our_local("state", false)?,
715 "mysync" => {
716 if crate::compat_mode() {
717 return Err(self.syntax_err(
718 "`mysync` is a stryke extension (disabled by --compat)",
719 self.peek_line(),
720 ));
721 }
722 self.parse_my_our_local("mysync", false)?
723 }
724 "oursync" => {
725 if crate::compat_mode() {
726 return Err(self.syntax_err(
727 "`oursync` is a stryke extension (disabled by --compat)",
728 self.peek_line(),
729 ));
730 }
731 self.parse_my_our_local("oursync", false)?
732 }
733 "frozen" | "const" => {
734 let leading = kw.as_str().to_string();
735 if crate::compat_mode() {
736 return Err(self.syntax_err(
737 format!("`{leading}` is a stryke extension (disabled by --compat)"),
738 self.peek_line(),
739 ));
740 }
741 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
747 if kw == "my" {
748 let mut stmt = self.parse_my_our_local("my", true)?;
753 if let StmtKind::My(ref mut decls) = stmt.kind {
754 for decl in decls.iter_mut() {
755 decl.frozen = true;
756 }
757 }
758 stmt
759 } else {
760 return Err(self.syntax_err(
761 format!("Expected 'my' after '{leading}'"),
762 self.peek_line(),
763 ));
764 }
765 } else {
766 return Err(self.syntax_err(
767 format!("Expected 'my' after '{leading}'"),
768 self.peek_line(),
769 ));
770 }
771 }
772 "typed" => {
773 if crate::compat_mode() {
774 return Err(self.syntax_err(
775 "`typed` is a stryke extension (disabled by --compat)",
776 self.peek_line(),
777 ));
778 }
779 self.advance();
780 if let Token::Ident(ref kw) = self.peek().clone() {
781 if kw == "my" {
782 self.parse_my_our_local("my", true)?
783 } else {
784 return Err(
785 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
786 );
787 }
788 } else {
789 return Err(
790 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
791 );
792 }
793 }
794 "our" => self.parse_my_our_local("our", false)?,
795 "local" => self.parse_my_our_local("local", false)?,
796 "package" => self.parse_package()?,
797 "use" => self.parse_use()?,
798 "no" => self.parse_no()?,
799 "return" => self.parse_return()?,
800 "last" => {
801 self.advance();
802 let lbl = if let Token::Ident(ref s) = self.peek() {
803 if s.chars().all(|c| c.is_uppercase() || c == '_') {
804 let (Token::Ident(l), _) = self.advance() else {
805 unreachable!()
806 };
807 Some(l)
808 } else {
809 None
810 }
811 } else {
812 None
813 };
814 let stmt = Statement {
815 label: None,
816 kind: StmtKind::Last(lbl.or(label.clone())),
817 line,
818 };
819 self.parse_stmt_postfix_modifier(stmt)?
820 }
821 "next" => {
822 self.advance();
823 let lbl = if let Token::Ident(ref s) = self.peek() {
824 if s.chars().all(|c| c.is_uppercase() || c == '_') {
825 let (Token::Ident(l), _) = self.advance() else {
826 unreachable!()
827 };
828 Some(l)
829 } else {
830 None
831 }
832 } else {
833 None
834 };
835 let stmt = Statement {
836 label: None,
837 kind: StmtKind::Next(lbl.or(label.clone())),
838 line,
839 };
840 self.parse_stmt_postfix_modifier(stmt)?
841 }
842 "redo" => {
843 self.advance();
844 self.eat(&Token::Semicolon);
845 Statement {
846 label: None,
847 kind: StmtKind::Redo(label.clone()),
848 line,
849 }
850 }
851 "BEGIN" => {
852 self.advance();
853 let block = self.parse_block()?;
854 Statement {
855 label: None,
856 kind: StmtKind::Begin(block),
857 line,
858 }
859 }
860 "END" => {
861 self.advance();
862 let block = self.parse_block()?;
863 Statement {
864 label: None,
865 kind: StmtKind::End(block),
866 line,
867 }
868 }
869 "UNITCHECK" => {
870 self.advance();
871 let block = self.parse_block()?;
872 Statement {
873 label: None,
874 kind: StmtKind::UnitCheck(block),
875 line,
876 }
877 }
878 "CHECK" => {
879 self.advance();
880 let block = self.parse_block()?;
881 Statement {
882 label: None,
883 kind: StmtKind::Check(block),
884 line,
885 }
886 }
887 "INIT" => {
888 self.advance();
889 let block = self.parse_block()?;
890 Statement {
891 label: None,
892 kind: StmtKind::Init(block),
893 line,
894 }
895 }
896 "goto" => {
897 self.advance();
898 let target = self.parse_expression()?;
899 let stmt = Statement {
900 label: None,
901 kind: StmtKind::Goto {
902 target: Box::new(target),
903 },
904 line,
905 };
906 self.parse_stmt_postfix_modifier(stmt)?
908 }
909 "continue" => {
910 self.advance();
911 let block = self.parse_block()?;
912 Statement {
913 label: None,
914 kind: StmtKind::Continue(block),
915 line,
916 }
917 }
918 "before"
919 if matches!(
920 self.peek_at(1),
921 Token::SingleString(_) | Token::DoubleString(_)
922 ) =>
923 {
924 self.parse_advice_decl(crate::ast::AdviceKind::Before)?
925 }
926 "after"
927 if matches!(
928 self.peek_at(1),
929 Token::SingleString(_) | Token::DoubleString(_)
930 ) =>
931 {
932 self.parse_advice_decl(crate::ast::AdviceKind::After)?
933 }
934 "around"
935 if matches!(
936 self.peek_at(1),
937 Token::SingleString(_) | Token::DoubleString(_)
938 ) =>
939 {
940 self.parse_advice_decl(crate::ast::AdviceKind::Around)?
941 }
942 "try" => self.parse_try_catch()?,
943 "defer" => self.parse_defer_stmt()?,
944 "tie" => self.parse_tie_stmt()?,
945 "given" => self.parse_given()?,
946 "when" => self.parse_when_stmt()?,
947 "default" => self.parse_default_stmt()?,
948 "eval_timeout" => self.parse_eval_timeout()?,
949 "do" => {
950 if matches!(self.peek_at(1), Token::LBrace) {
951 self.advance();
952 let body = self.parse_block()?;
953 if let Token::Ident(ref w) = self.peek().clone() {
954 if w == "while" {
955 self.advance();
956 self.expect(&Token::LParen)?;
957 let mut condition = self.parse_expression()?;
958 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
959 self.expect(&Token::RParen)?;
960 self.eat(&Token::Semicolon);
961 Statement {
962 label: label.clone(),
963 kind: StmtKind::DoWhile { body, condition },
964 line,
965 }
966 } else {
967 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
968 let inner = Expr {
969 kind: ExprKind::CodeRef {
970 params: vec![],
971 body,
972 },
973 line: inner_line,
974 };
975 let expr = Expr {
976 kind: ExprKind::Do(Box::new(inner)),
977 line,
978 };
979 let stmt = Statement {
980 label: label.clone(),
981 kind: StmtKind::Expression(expr),
982 line,
983 };
984 self.parse_stmt_postfix_modifier(stmt)?
986 }
987 } else {
988 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
989 let inner = Expr {
990 kind: ExprKind::CodeRef {
991 params: vec![],
992 body,
993 },
994 line: inner_line,
995 };
996 let expr = Expr {
997 kind: ExprKind::Do(Box::new(inner)),
998 line,
999 };
1000 let stmt = Statement {
1001 label: label.clone(),
1002 kind: StmtKind::Expression(expr),
1003 line,
1004 };
1005 self.parse_stmt_postfix_modifier(stmt)?
1006 }
1007 } else {
1008 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1009 let stmt = self.maybe_postfix_modifier(expr)?;
1010 self.parse_stmt_postfix_modifier(stmt)?
1011 } else {
1012 let expr = self.parse_expression()?;
1013 let stmt = self.maybe_postfix_modifier(expr)?;
1014 self.parse_stmt_postfix_modifier(stmt)?
1015 }
1016 }
1017 }
1018 _ => {
1019 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1021 let stmt = self.maybe_postfix_modifier(expr)?;
1022 self.parse_stmt_postfix_modifier(stmt)?
1023 } else {
1024 let expr = self.parse_expression()?;
1025 let stmt = self.maybe_postfix_modifier(expr)?;
1026 self.parse_stmt_postfix_modifier(stmt)?
1027 }
1028 }
1029 },
1030 Token::LBrace => {
1031 if self.looks_like_hashref() {
1034 let expr = self.parse_expression()?;
1035 let stmt = self.maybe_postfix_modifier(expr)?;
1036 self.parse_stmt_postfix_modifier(stmt)?
1037 } else {
1038 let block = self.parse_block()?;
1039 let stmt = Statement {
1040 label: None,
1041 kind: StmtKind::Block(block),
1042 line,
1043 };
1044 self.parse_stmt_postfix_modifier(stmt)?
1046 }
1047 }
1048 _ => {
1049 let expr = self.parse_expression()?;
1050 let stmt = self.maybe_postfix_modifier(expr)?;
1051 self.parse_stmt_postfix_modifier(stmt)?
1052 }
1053 };
1054
1055 stmt.label = label;
1056 Ok(stmt)
1057 }
1058
1059 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> PerlResult<Statement> {
1061 let line = stmt.line;
1062 if self.peek_line() > self.prev_line() {
1067 self.eat(&Token::Semicolon);
1068 return Ok(stmt);
1069 }
1070 if let Token::Ident(ref kw) = self.peek().clone() {
1071 match kw.as_str() {
1072 "if" => {
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::If {
1080 condition: cond,
1081 body: vec![stmt],
1082 elsifs: vec![],
1083 else_block: None,
1084 },
1085 line,
1086 });
1087 }
1088 "unless" => {
1089 self.advance();
1090 let mut cond = self.parse_expression()?;
1091 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1092 self.eat(&Token::Semicolon);
1093 return Ok(Statement {
1094 label: None,
1095 kind: StmtKind::Unless {
1096 condition: cond,
1097 body: vec![stmt],
1098 else_block: None,
1099 },
1100 line,
1101 });
1102 }
1103 "while" | "until" | "for" | "foreach" => {
1104 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
1107 let out = self.maybe_postfix_modifier(expr)?;
1108 self.eat(&Token::Semicolon);
1109 return Ok(out);
1110 }
1111 return Err(self.syntax_err(
1112 format!("postfix `{}` is not supported on this statement form", kw),
1113 self.peek_line(),
1114 ));
1115 }
1116 "pmap" | "pflat_map" | "pgrep" | "pfor" | "preduce" | "pcache" => {
1118 let line = stmt.line;
1119 let block = self.stmt_into_parallel_block(stmt)?;
1120 let which = kw.as_str();
1121 self.advance();
1122 self.eat(&Token::Comma);
1123 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
1124 self.eat(&Token::Semicolon);
1125 let list = Box::new(list);
1126 let progress = progress.map(Box::new);
1127 let kind = match which {
1128 "pmap" => ExprKind::PMapExpr {
1129 block,
1130 list,
1131 progress,
1132 flat_outputs: false,
1133 on_cluster: None,
1134 stream: false,
1135 },
1136 "pflat_map" => ExprKind::PMapExpr {
1137 block,
1138 list,
1139 progress,
1140 flat_outputs: true,
1141 on_cluster: None,
1142 stream: false,
1143 },
1144 "pgrep" => ExprKind::PGrepExpr {
1145 block,
1146 list,
1147 progress,
1148 stream: false,
1149 },
1150 "pfor" => ExprKind::PForExpr {
1151 block,
1152 list,
1153 progress,
1154 },
1155 "preduce" => ExprKind::PReduceExpr {
1156 block,
1157 list,
1158 progress,
1159 },
1160 "pcache" => ExprKind::PcacheExpr {
1161 block,
1162 list,
1163 progress,
1164 },
1165 _ => unreachable!(),
1166 };
1167 return Ok(Statement {
1168 label: None,
1169 kind: StmtKind::Expression(Expr { kind, line }),
1170 line,
1171 });
1172 }
1173 _ => {}
1174 }
1175 }
1176 self.eat(&Token::Semicolon);
1177 Ok(stmt)
1178 }
1179
1180 fn stmt_into_parallel_block(&self, stmt: Statement) -> PerlResult<Block> {
1183 let line = stmt.line;
1184 match stmt.kind {
1185 StmtKind::Block(block) => Ok(block),
1186 StmtKind::Expression(expr) => {
1187 if let ExprKind::Do(ref inner) = expr.kind {
1188 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
1189 return Ok(body.clone());
1190 }
1191 }
1192 Ok(vec![Statement {
1193 label: None,
1194 kind: StmtKind::Expression(expr),
1195 line,
1196 }])
1197 }
1198 _ => Err(self.syntax_err(
1199 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
1200 line,
1201 )),
1202 }
1203 }
1204
1205 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
1208 match stmt.kind {
1209 StmtKind::Expression(expr) => Some(expr),
1210 StmtKind::Block(block) => {
1211 let line = stmt.line;
1212 let inner = Expr {
1213 kind: ExprKind::CodeRef {
1214 params: vec![],
1215 body: block,
1216 },
1217 line,
1218 };
1219 Some(Expr {
1220 kind: ExprKind::Do(Box::new(inner)),
1221 line,
1222 })
1223 }
1224 _ => None,
1225 }
1226 }
1227
1228 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
1231 matches!(
1232 self.peek(),
1233 Token::Ident(ref kw)
1234 if matches!(
1235 kw.as_str(),
1236 "if" | "unless" | "while" | "until" | "for" | "foreach"
1237 )
1238 )
1239 }
1240
1241 fn peek_is_named_unary_terminator(&self) -> bool {
1248 matches!(
1249 self.peek(),
1250 Token::Semicolon
1251 | Token::RBrace
1252 | Token::RParen
1253 | Token::RBracket
1254 | Token::Eof
1255 | Token::Comma
1256 | Token::FatArrow
1257 | Token::PipeForward
1258 | Token::Question
1259 | Token::Colon
1260 | Token::NumEq
1261 | Token::NumNe
1262 | Token::NumLt
1263 | Token::NumGt
1264 | Token::NumLe
1265 | Token::NumGe
1266 | Token::Spaceship
1267 | Token::StrEq
1268 | Token::StrNe
1269 | Token::StrLt
1270 | Token::StrGt
1271 | Token::StrLe
1272 | Token::StrGe
1273 | Token::StrCmp
1274 | Token::LogAnd
1275 | Token::LogOr
1276 | Token::LogAndWord
1277 | Token::LogOrWord
1278 | Token::DefinedOr
1279 | Token::Range
1280 | Token::RangeExclusive
1281 | Token::Assign
1282 | Token::PlusAssign
1283 | Token::MinusAssign
1284 | Token::MulAssign
1285 | Token::DivAssign
1286 | Token::ModAssign
1287 | Token::PowAssign
1288 | Token::DotAssign
1289 | Token::AndAssign
1290 | Token::OrAssign
1291 | Token::XorAssign
1292 | Token::DefinedOrAssign
1293 | Token::ShiftLeftAssign
1294 | Token::ShiftRightAssign
1295 | Token::BitAndAssign
1296 | Token::BitOrAssign
1297 )
1298 }
1299
1300 fn maybe_postfix_modifier(&mut self, expr: Expr) -> PerlResult<Statement> {
1301 let line = expr.line;
1302 if self.peek_line() > self.prev_line() {
1304 return Ok(Statement {
1305 label: None,
1306 kind: StmtKind::Expression(expr),
1307 line,
1308 });
1309 }
1310 match self.peek() {
1311 Token::Ident(ref kw) => match kw.as_str() {
1312 "if" => {
1313 self.advance();
1314 let cond = self.parse_expression()?;
1315 Ok(Statement {
1316 label: None,
1317 kind: StmtKind::Expression(Expr {
1318 kind: ExprKind::PostfixIf {
1319 expr: Box::new(expr),
1320 condition: Box::new(cond),
1321 },
1322 line,
1323 }),
1324 line,
1325 })
1326 }
1327 "unless" => {
1328 self.advance();
1329 let cond = self.parse_expression()?;
1330 Ok(Statement {
1331 label: None,
1332 kind: StmtKind::Expression(Expr {
1333 kind: ExprKind::PostfixUnless {
1334 expr: Box::new(expr),
1335 condition: Box::new(cond),
1336 },
1337 line,
1338 }),
1339 line,
1340 })
1341 }
1342 "while" => {
1343 self.advance();
1344 let cond = self.parse_expression()?;
1345 Ok(Statement {
1346 label: None,
1347 kind: StmtKind::Expression(Expr {
1348 kind: ExprKind::PostfixWhile {
1349 expr: Box::new(expr),
1350 condition: Box::new(cond),
1351 },
1352 line,
1353 }),
1354 line,
1355 })
1356 }
1357 "until" => {
1358 self.advance();
1359 let cond = self.parse_expression()?;
1360 Ok(Statement {
1361 label: None,
1362 kind: StmtKind::Expression(Expr {
1363 kind: ExprKind::PostfixUntil {
1364 expr: Box::new(expr),
1365 condition: Box::new(cond),
1366 },
1367 line,
1368 }),
1369 line,
1370 })
1371 }
1372 "for" | "foreach" => {
1373 self.advance();
1374 let list = self.parse_expression()?;
1375 Ok(Statement {
1376 label: None,
1377 kind: StmtKind::Expression(Expr {
1378 kind: ExprKind::PostfixForeach {
1379 expr: Box::new(expr),
1380 list: Box::new(list),
1381 },
1382 line,
1383 }),
1384 line,
1385 })
1386 }
1387 _ => Ok(Statement {
1388 label: None,
1389 kind: StmtKind::Expression(expr),
1390 line,
1391 }),
1392 },
1393 _ => Ok(Statement {
1394 label: None,
1395 kind: StmtKind::Expression(expr),
1396 line,
1397 }),
1398 }
1399 }
1400
1401 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1403 let saved = self.pos;
1404 let line = self.peek_line();
1405 let mut name = match self.peek() {
1406 Token::Ident(n) => n.clone(),
1407 _ => return None,
1408 };
1409 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1411 return None;
1412 }
1413 self.advance();
1414 while self.eat(&Token::PackageSep) {
1415 match self.advance() {
1416 (Token::Ident(part), _) => {
1417 name = format!("{}::{}", name, part);
1418 }
1419 _ => {
1420 self.pos = saved;
1421 return None;
1422 }
1423 }
1424 }
1425 match self.peek() {
1426 Token::Semicolon | Token::RBrace => Some(Expr {
1427 kind: ExprKind::FuncCall { name, args: vec![] },
1428 line,
1429 }),
1430 _ => {
1431 self.pos = saved;
1432 None
1433 }
1434 }
1435 }
1436
1437 pub(crate) fn operator_keyword_to_ident_str(tok: &Token) -> Option<&'static str> {
1441 Some(match tok {
1442 Token::StrEq => "eq",
1443 Token::StrNe => "ne",
1444 Token::StrLt => "lt",
1445 Token::StrGt => "gt",
1446 Token::StrLe => "le",
1447 Token::StrGe => "ge",
1448 Token::StrCmp => "cmp",
1449 Token::LogAndWord => "and",
1450 Token::LogOrWord => "or",
1451 Token::LogNotWord => "not",
1452 Token::X => "x",
1453 _ => return None,
1454 })
1455 }
1456
1457 pub(crate) fn is_underscore_topic_slot(name: &str) -> bool {
1461 if name == "_" {
1462 return true;
1463 }
1464 if !name.starts_with('_') || name.len() < 2 {
1465 return false;
1466 }
1467 let bytes = name.as_bytes();
1468 let mut i = 1;
1469 while i < bytes.len() && bytes[i].is_ascii_digit() {
1471 i += 1;
1472 }
1473 let chevrons_start = i;
1475 while i < bytes.len() && bytes[i] == b'<' {
1476 i += 1;
1477 }
1478 i == bytes.len() && (i > 1 || chevrons_start > 1)
1480 }
1481
1482 pub(crate) fn is_reserved_special_var_name(name: &str) -> bool {
1492 matches!(
1493 name,
1494 "STDIN" | "STDOUT" | "STDERR" | "ARGV" | "ARGVOUT" | "DATA"
1496 | "ENV" | "INC" | "SIG" | "ISA"
1504 | "EXPORT" | "EXPORT_OK" | "EXPORT_TAGS"
1505 | "VERSION"
1506 | "__FILE__" | "__LINE__" | "__PACKAGE__" | "__SUB__"
1508 | "__DATA__" | "__END__"
1509 )
1510 }
1511
1512 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1514 if Self::is_underscore_topic_slot(name) {
1519 return false;
1520 }
1521 !matches!(
1522 name,
1523 "__FILE__"
1524 | "__LINE__"
1525 | "abs"
1526 | "async"
1527 | "spawn"
1528 | "atan2"
1529 | "await"
1530 | "barrier"
1531 | "bless"
1532 | "caller"
1533 | "capture"
1534 | "cat"
1535 | "chdir"
1536 | "chmod"
1537 | "chomp"
1538 | "chop"
1539 | "chr"
1540 | "chown"
1541 | "closedir"
1542 | "close"
1543 | "collect"
1544 | "cos"
1545 | "crypt"
1546 | "defined"
1547 | "dec"
1548 | "delete"
1549 | "die"
1550 | "deque"
1551 | "do"
1552 | "each"
1553 | "eof"
1554 | "fore"
1555 | "eval"
1556 | "exec"
1557 | "exists"
1558 | "exit"
1559 | "exp"
1560 | "fan"
1561 | "fan_cap"
1562 | "fc"
1563 | "fetch_url"
1564 | "d"
1565 | "dirs"
1566 | "dr"
1567 | "f"
1568 | "fi"
1569 | "files"
1570 | "filesf"
1571 | "filter"
1572 | "fr"
1573 | "getcwd"
1574 | "glob_par"
1575 | "par_sed"
1576 | "glob"
1577 | "grep"
1578 | "greps"
1579 | "heap"
1580 | "hex"
1581 | "inc"
1582 | "index"
1583 | "int"
1584 | "join"
1585 | "keys"
1586 | "lcfirst"
1587 | "lc"
1588 | "length"
1589 | "link"
1590 | "log"
1591 | "lstat"
1592 | "map"
1593 | "flat_map"
1594 | "maps"
1595 | "flat_maps"
1596 | "flatten"
1597 | "frequencies"
1598 | "freq"
1599 | "pfrequencies"
1600 | "pfreq"
1601 | "interleave"
1602 | "ddump"
1603 | "stringify"
1604 | "str"
1605 | "s"
1606 | "input"
1607 | "lines"
1608 | "words"
1609 | "chars"
1610 | "digits"
1611 | "letters"
1612 | "letters_uc"
1613 | "letters_lc"
1614 | "punctuation"
1615 | "sentences"
1616 | "paragraphs"
1617 | "sections"
1618 | "numbers"
1619 | "graphemes"
1620 | "columns"
1621 | "trim"
1622 | "avg"
1623 | "top"
1624 | "pager"
1625 | "pg"
1626 | "less"
1627 | "count_by"
1628 | "to_file"
1629 | "to_json"
1630 | "to_csv"
1631 | "grep_v"
1632 | "select_keys"
1633 | "pluck"
1634 | "clamp"
1635 | "normalize"
1636 | "stddev"
1637 | "squared"
1638 | "square"
1639 | "cubed"
1640 | "cube"
1641 | "expt"
1642 | "pow"
1643 | "pw"
1644 | "snake_case"
1645 | "camel_case"
1646 | "kebab_case"
1647 | "to_toml"
1648 | "to_yaml"
1649 | "to_xml"
1650 | "to_html"
1651 | "to_markdown"
1652 | "xopen"
1653 | "clip"
1654 | "paste"
1655 | "to_table"
1656 | "sparkline"
1657 | "bar_chart"
1658 | "flame"
1659 | "set"
1660 | "list_count"
1661 | "list_size"
1662 | "count"
1663 | "size"
1664 | "cnt"
1665 | "len"
1666 | "all"
1667 | "any"
1668 | "none"
1669 | "take_while"
1670 | "drop_while"
1671 | "skip_while"
1672 | "skip"
1673 | "first_or"
1674 | "tap"
1675 | "peek"
1676 | "partition"
1677 | "min_by"
1678 | "max_by"
1679 | "zip_with"
1680 | "group_by"
1681 | "chunk_by"
1682 | "with_index"
1683 | "puniq"
1684 | "pfirst"
1685 | "pany"
1686 | "uniq"
1687 | "distinct"
1688 | "shuffle"
1689 | "shuffled"
1690 | "chunked"
1691 | "windowed"
1692 | "match"
1693 | "mkdir"
1694 | "every"
1695 | "gen"
1696 | "oct"
1697 | "open"
1698 | "p"
1699 | "opendir"
1700 | "ord"
1701 | "par_lines"
1702 | "par_walk"
1703 | "pipe"
1704 | "pipes"
1705 | "block_devices"
1706 | "char_devices"
1707 | "exe"
1708 | "executables"
1709 | "rate_limit"
1710 | "retry"
1711 | "pcache"
1712 | "pchannel"
1713 | "pfor"
1714 | "pgrep"
1715 | "pgreps"
1716 | "pipeline"
1717 | "pmap_chunked"
1718 | "pmap_reduce"
1719 | "par_reduce"
1720 | "pmap_on"
1721 | "pflat_map_on"
1722 | "pmap"
1723 | "pmaps"
1724 | "pflat_map"
1725 | "pflat_maps"
1726 | "pop"
1727 | "pos"
1728 | "ppool"
1729 | "preduce_init"
1730 | "preduce"
1731 | "pselect"
1732 | "printf"
1733 | "print"
1734 | "pr"
1735 | "psort"
1736 | "push"
1737 | "pwatch"
1738 | "rand"
1739 | "readdir"
1740 | "readlink"
1741 | "reduce"
1742 | "fold"
1743 | "inject"
1744 | "first"
1745 | "detect"
1746 | "find"
1747 | "find_all"
1748 | "ref"
1749 | "rename"
1750 | "require"
1751 | "rev"
1752 | "reverse"
1753 | "reversed"
1754 | "rewinddir"
1755 | "rindex"
1756 | "rmdir"
1757 | "rm"
1758 | "say"
1759 | "scalar"
1760 | "seekdir"
1761 | "shift"
1762 | "sin"
1763 | "slurp"
1764 | "sockets"
1765 | "sort"
1766 | "splice"
1767 | "splice_last"
1768 | "splice1"
1769 | "spl_last"
1770 | "split"
1771 | "sprintf"
1772 | "sqrt"
1773 | "srand"
1774 | "stat"
1775 | "study"
1776 | "substr"
1777 | "symlink"
1778 | "sym_links"
1779 | "system"
1780 | "telldir"
1781 | "timer"
1782 | "trace"
1783 | "ucfirst"
1784 | "uc"
1785 | "undef"
1786 | "umask"
1787 | "unlink"
1788 | "unshift"
1789 | "utime"
1790 | "values"
1791 | "wantarray"
1792 | "warn"
1793 | "watch"
1794 | "yield"
1795 | "sub"
1796 )
1797 }
1798
1799 fn parse_block(&mut self) -> PerlResult<Block> {
1800 self.expect(&Token::LBrace)?;
1801 let saved_pipe_rhs_depth = self.pipe_rhs_depth;
1804 self.pipe_rhs_depth = 0;
1805 self.block_depth += 1;
1806 let mut stmts = Vec::new();
1807 if let Some(param_stmts) = self.try_parse_block_params()? {
1811 stmts.extend(param_stmts);
1812 }
1813 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1814 if self.eat(&Token::Semicolon) {
1815 continue;
1816 }
1817 stmts.push(self.parse_statement()?);
1818 }
1819 self.expect(&Token::RBrace)?;
1820 self.pipe_rhs_depth = saved_pipe_rhs_depth;
1821 self.block_depth -= 1;
1822 Self::default_topic_for_sole_bareword(&mut stmts);
1823 Ok(stmts)
1824 }
1825
1826 fn try_parse_block_params(&mut self) -> PerlResult<Option<Vec<Statement>>> {
1831 if !matches!(self.peek(), Token::BitOr) {
1832 return Ok(None);
1833 }
1834 let mut i = 1; loop {
1837 match self.peek_at(i) {
1838 Token::ScalarVar(_) => i += 1,
1839 _ => return Ok(None), }
1841 match self.peek_at(i) {
1842 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
1846 }
1847 let line = self.peek_line();
1849 self.advance(); let mut names = Vec::new();
1851 loop {
1852 if let Token::ScalarVar(ref name) = self.peek().clone() {
1853 names.push(name.clone());
1854 self.advance();
1855 }
1856 if self.eat(&Token::BitOr) {
1857 break;
1858 }
1859 self.expect(&Token::Comma)?;
1860 }
1861 let sources: Vec<&str> = match names.len() {
1866 1 => vec!["_"],
1867 2 => vec!["a", "b"],
1868 n => {
1869 let _ = n;
1871 vec![] }
1873 };
1874 let mut stmts = Vec::with_capacity(names.len());
1875 if !sources.is_empty() {
1876 for (name, src) in names.iter().zip(sources.iter()) {
1877 stmts.push(Statement {
1878 label: None,
1879 kind: StmtKind::My(vec![VarDecl {
1880 sigil: Sigil::Scalar,
1881 name: name.clone(),
1882 initializer: Some(Expr {
1883 kind: ExprKind::ScalarVar(src.to_string()),
1884 line,
1885 }),
1886 frozen: false,
1887 type_annotation: None,
1888 }]),
1889 line,
1890 });
1891 }
1892 } else {
1893 for (idx, name) in names.iter().enumerate() {
1895 let src = if idx == 0 {
1896 "_".to_string()
1897 } else {
1898 format!("_{idx}")
1899 };
1900 stmts.push(Statement {
1901 label: None,
1902 kind: StmtKind::My(vec![VarDecl {
1903 sigil: Sigil::Scalar,
1904 name: name.clone(),
1905 initializer: Some(Expr {
1906 kind: ExprKind::ScalarVar(src),
1907 line,
1908 }),
1909 frozen: false,
1910 type_annotation: None,
1911 }]),
1912 line,
1913 });
1914 }
1915 }
1916 Ok(Some(stmts))
1917 }
1918
1919 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
1934 let [only] = stmts else { return };
1935 let StmtKind::Expression(ref mut expr) = only.kind else {
1936 return;
1937 };
1938 let topic_line = expr.line;
1939 let topic_arg = || Expr {
1940 kind: ExprKind::ScalarVar("_".to_string()),
1941 line: topic_line,
1942 };
1943 match expr.kind {
1944 ExprKind::FuncCall {
1946 ref name,
1947 ref mut args,
1948 } if args.is_empty()
1949 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1950 {
1951 args.push(topic_arg());
1952 }
1953 ExprKind::Bareword(ref name)
1957 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
1958 {
1959 let n = name.clone();
1960 expr.kind = ExprKind::FuncCall {
1961 name: n,
1962 args: vec![topic_arg()],
1963 };
1964 }
1965 _ => {}
1966 }
1967 }
1968
1969 fn parse_defer_stmt(&mut self) -> PerlResult<Statement> {
1973 let line = self.peek_line();
1974 self.advance(); let body = self.parse_block()?;
1976 self.eat(&Token::Semicolon);
1977 let coderef = Expr {
1979 kind: ExprKind::CodeRef {
1980 params: vec![],
1981 body,
1982 },
1983 line,
1984 };
1985 Ok(Statement {
1986 label: None,
1987 kind: StmtKind::Expression(Expr {
1988 kind: ExprKind::FuncCall {
1989 name: "defer__internal".to_string(),
1990 args: vec![coderef],
1991 },
1992 line,
1993 }),
1994 line,
1995 })
1996 }
1997
1998 fn parse_try_catch(&mut self) -> PerlResult<Statement> {
2000 let line = self.peek_line();
2001 self.advance(); let try_block = self.parse_block()?;
2003 match self.peek() {
2004 Token::Ident(ref k) if k == "catch" => {
2005 self.advance();
2006 }
2007 _ => {
2008 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
2009 }
2010 }
2011 self.expect(&Token::LParen)?;
2012 let catch_var = self.parse_scalar_var_name()?;
2013 self.expect(&Token::RParen)?;
2014 let catch_block = self.parse_block()?;
2015 let finally_block = match self.peek() {
2016 Token::Ident(ref k) if k == "finally" => {
2017 self.advance();
2018 Some(self.parse_block()?)
2019 }
2020 _ => None,
2021 };
2022 self.eat(&Token::Semicolon);
2023 Ok(Statement {
2024 label: None,
2025 kind: StmtKind::TryCatch {
2026 try_block,
2027 catch_var,
2028 catch_block,
2029 finally_block,
2030 },
2031 line,
2032 })
2033 }
2034
2035 fn parse_thread_macro(&mut self, _line: usize, thread_last: bool) -> PerlResult<Expr> {
2049 self.parse_thread_macro_inner(_line, thread_last, None)
2050 }
2051
2052 fn parse_thread_macro_inner(
2061 &mut self,
2062 _line: usize,
2063 thread_last: bool,
2064 mut parallel_collector: Option<&mut Vec<Expr>>,
2065 ) -> PerlResult<Expr> {
2066 let saved_thread_last = self.thread_last_mode;
2068 self.thread_last_mode = thread_last;
2069
2070 let pipe_rhs_wrap = self.in_pipe_rhs();
2071 let mut result = if let Some(pre) = self.pending_thread_input.take() {
2075 pre
2076 } else if pipe_rhs_wrap {
2077 Expr {
2078 kind: ExprKind::ArrayElement {
2079 array: "_".to_string(),
2080 index: Box::new(Expr {
2081 kind: ExprKind::Integer(0),
2082 line: _line,
2083 }),
2084 },
2085 line: _line,
2086 }
2087 } else {
2088 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
2091 let expr = self.parse_thread_input();
2092 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
2093 expr?
2094 };
2095 let source_for_par = if parallel_collector.is_some() {
2099 let src = std::mem::replace(
2100 &mut result,
2101 Expr {
2102 kind: ExprKind::ScalarVar("_".into()),
2103 line: _line,
2104 },
2105 );
2106 Some(src)
2107 } else {
2108 None
2109 };
2110
2111 let mut last_stage_end_line = self.prev_line();
2113
2114 loop {
2116 if self.peek_line() > last_stage_end_line {
2121 break;
2122 }
2123
2124 match self.peek() {
2128 Token::Semicolon
2129 | Token::RBrace
2130 | Token::RParen
2131 | Token::RBracket
2132 | Token::PipeForward
2133 | Token::Eof
2134 | Token::ScalarVar(_)
2135 | Token::ArrayVar(_)
2136 | Token::HashVar(_)
2137 | Token::Comma => break,
2138 Token::LogOr if matches!(self.peek_at(1), Token::NumGt) => break,
2143 Token::BitOr
2145 if matches!(self.peek_at(1), Token::Ident(ref n) if n == "then")
2146 && matches!(self.peek_at(2), Token::BitOr) =>
2147 {
2148 break
2149 }
2150 Token::Ident(ref kw)
2151 if matches!(
2152 kw.as_str(),
2153 "my" | "our"
2154 | "local"
2155 | "state"
2156 | "if"
2157 | "unless"
2158 | "while"
2159 | "until"
2160 | "for"
2161 | "foreach"
2162 | "return"
2163 | "last"
2164 | "next"
2165 | "redo"
2166 ) =>
2167 {
2168 break
2169 }
2170 _ => {}
2171 }
2172
2173 let stage_line = self.peek_line();
2174
2175 match self.peek().clone() {
2177 Token::ArrowBrace => {
2179 self.advance(); let mut stmts = Vec::new();
2181 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2182 if self.eat(&Token::Semicolon) {
2183 continue;
2184 }
2185 stmts.push(self.parse_statement()?);
2186 }
2187 self.expect(&Token::RBrace)?;
2188 let code_ref = Expr {
2189 kind: ExprKind::CodeRef {
2190 params: vec![],
2191 body: stmts,
2192 },
2193 line: stage_line,
2194 };
2195 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2196 }
2197 Token::Ident(ref name) if name == "sub" => {
2199 if crate::no_interop_mode() {
2200 return Err(self.syntax_err(
2201 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
2202 stage_line,
2203 ));
2204 }
2205 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2207 let body = self.parse_block()?;
2208 let code_ref = Expr {
2209 kind: ExprKind::CodeRef { params, body },
2210 line: stage_line,
2211 };
2212 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2213 }
2214 Token::Ident(ref name) if name == "fn" => {
2216 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2218 self.parse_sub_attributes()?;
2219 let body = self.parse_fn_eq_body_or_block(false)?;
2220 let code_ref = Expr {
2221 kind: ExprKind::CodeRef { params, body },
2222 line: stage_line,
2223 };
2224 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2225 }
2226 Token::Ident(ref name) => {
2228 let mut func_name = name.clone();
2229 self.advance();
2230
2231 while matches!(self.peek(), Token::PackageSep) {
2233 self.advance(); if let Token::Ident(ref part) = self.peek().clone() {
2235 func_name.push_str("::");
2236 func_name.push_str(part);
2237 self.advance();
2238 } else {
2239 return Err(self.syntax_err(
2240 format!(
2241 "Expected identifier after `::` in thread stage, got {:?}",
2242 self.peek()
2243 ),
2244 stage_line,
2245 ));
2246 }
2247 }
2248
2249 if func_name.starts_with('\x00') {
2251 let parts: Vec<&str> = func_name.split('\x00').collect();
2252 if parts.len() >= 4 && parts[1] == "s" {
2253 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2254 let stage = Expr {
2255 kind: ExprKind::Substitution {
2256 expr: Box::new(result.clone()),
2257 pattern: parts[2].to_string(),
2258 replacement: parts[3].to_string(),
2259 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2260 delim,
2261 },
2262 line: stage_line,
2263 };
2264 result = stage;
2265 last_stage_end_line = self.prev_line();
2266 continue;
2267 }
2268 if parts.len() >= 4 && parts[1] == "tr" {
2269 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2270 let stage = Expr {
2271 kind: ExprKind::Transliterate {
2272 expr: Box::new(result.clone()),
2273 from: parts[2].to_string(),
2274 to: parts[3].to_string(),
2275 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2276 delim,
2277 },
2278 line: stage_line,
2279 };
2280 result = stage;
2281 last_stage_end_line = self.prev_line();
2282 continue;
2283 }
2284 return Err(
2285 self.syntax_err("Unexpected encoded token in thread", stage_line)
2286 );
2287 }
2288
2289 if matches!(self.peek(), Token::Plus)
2294 && matches!(self.peek_at(1), Token::LBrace)
2295 {
2296 self.advance(); self.expect(&Token::LBrace)?;
2298 let pairs = self.try_parse_hash_ref()?;
2300 let hashref_expr = Expr {
2301 kind: ExprKind::HashRef(pairs),
2302 line: stage_line,
2303 };
2304 let flatten_array_refs =
2305 matches!(func_name.as_str(), "flat_map" | "flat_maps");
2306 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
2307 let placeholder = Expr {
2309 kind: ExprKind::Undef,
2310 line: stage_line,
2311 };
2312 let map_node = Expr {
2313 kind: ExprKind::MapExprComma {
2314 expr: Box::new(hashref_expr),
2315 list: Box::new(placeholder),
2316 flatten_array_refs,
2317 stream,
2318 },
2319 line: stage_line,
2320 };
2321 result = self.pipe_forward_apply(result, map_node, stage_line)?;
2322 } else if func_name == "pmap_chunked" {
2324 let chunk_size = self.parse_assign_expr()?;
2325 let block = self.parse_block_or_bareword_block()?;
2326 let placeholder = self.pipe_placeholder_list(stage_line);
2327 let stage = Expr {
2328 kind: ExprKind::PMapChunkedExpr {
2329 chunk_size: Box::new(chunk_size),
2330 block,
2331 list: Box::new(placeholder),
2332 progress: None,
2333 },
2334 line: stage_line,
2335 };
2336 result = self.pipe_forward_apply(result, stage, stage_line)?;
2337 } else if func_name == "preduce_init" {
2339 let init = self.parse_assign_expr()?;
2340 let block = self.parse_block_or_bareword_block()?;
2341 let placeholder = self.pipe_placeholder_list(stage_line);
2342 let stage = Expr {
2343 kind: ExprKind::PReduceInitExpr {
2344 init: Box::new(init),
2345 block,
2346 list: Box::new(placeholder),
2347 progress: None,
2348 },
2349 line: stage_line,
2350 };
2351 result = self.pipe_forward_apply(result, stage, stage_line)?;
2352 } else if func_name == "pmap_reduce" {
2354 let map_block = self.parse_block_or_bareword_block()?;
2355 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2356 self.parse_block()?
2357 } else {
2358 self.expect(&Token::Comma)?;
2359 self.parse_block_or_bareword_cmp_block()?
2360 };
2361 let placeholder = self.pipe_placeholder_list(stage_line);
2362 let stage = Expr {
2363 kind: ExprKind::PMapReduceExpr {
2364 map_block,
2365 reduce_block,
2366 list: Box::new(placeholder),
2367 progress: None,
2368 },
2369 line: stage_line,
2370 };
2371 result = self.pipe_forward_apply(result, stage, stage_line)?;
2372 } else if func_name == "par_reduce" {
2377 let extract_block = self.parse_block_or_bareword_block()?;
2378 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2379 Some(self.parse_block()?)
2380 } else {
2381 None
2382 };
2383 let placeholder = self.pipe_placeholder_list(stage_line);
2384 let stage = Expr {
2385 kind: ExprKind::ParReduceExpr {
2386 extract_block,
2387 reduce_block,
2388 list: Box::new(placeholder),
2389 },
2390 line: stage_line,
2391 };
2392 result = self.pipe_forward_apply(result, stage, stage_line)?;
2393 } else if func_name == "pmap_on" || func_name == "pflat_map_on" {
2398 self.suppress_scalar_hash_brace =
2401 self.suppress_scalar_hash_brace.saturating_add(1);
2402 let cluster = self.parse_assign_expr();
2403 self.suppress_scalar_hash_brace =
2404 self.suppress_scalar_hash_brace.saturating_sub(1);
2405 let cluster = cluster?;
2406 self.eat(&Token::Comma);
2409 let block = self.parse_block_or_bareword_block()?;
2410 let placeholder = self.pipe_placeholder_list(stage_line);
2411 let stage = Expr {
2412 kind: ExprKind::PMapExpr {
2413 block,
2414 list: Box::new(placeholder),
2415 progress: None,
2416 flat_outputs: func_name == "pflat_map_on",
2417 on_cluster: Some(Box::new(cluster)),
2418 stream: false,
2419 },
2420 line: stage_line,
2421 };
2422 result = self.pipe_forward_apply(result, stage, stage_line)?;
2423 } else if matches!(self.peek(), Token::LBrace) {
2425 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
2427 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
2428 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
2429 result = self.pipe_forward_apply(result, stage, stage_line)?;
2430 } else if matches!(self.peek(), Token::LParen) {
2431 if func_name == "join" {
2434 self.advance(); let separator = self.parse_assign_expr()?;
2436 self.expect(&Token::RParen)?;
2437 let placeholder = self.pipe_placeholder_list(stage_line);
2438 let stage = Expr {
2439 kind: ExprKind::JoinExpr {
2440 separator: Box::new(separator),
2441 list: Box::new(placeholder),
2442 },
2443 line: stage_line,
2444 };
2445 result = self.pipe_forward_apply(result, stage, stage_line)?;
2446 } else if func_name == "split" {
2447 self.advance(); let pattern = self.parse_assign_expr()?;
2449 let limit = if self.eat(&Token::Comma) {
2450 Some(Box::new(self.parse_assign_expr()?))
2451 } else {
2452 None
2453 };
2454 self.expect(&Token::RParen)?;
2455 let placeholder = Expr {
2456 kind: ExprKind::ScalarVar("_".to_string()),
2457 line: stage_line,
2458 };
2459 let stage = Expr {
2460 kind: ExprKind::SplitExpr {
2461 pattern: Box::new(pattern),
2462 string: Box::new(placeholder),
2463 limit,
2464 },
2465 line: stage_line,
2466 };
2467 result = self.pipe_forward_apply(result, stage, stage_line)?;
2468 } else {
2469 self.advance(); let mut call_args = Vec::new();
2480 while !matches!(self.peek(), Token::RParen | Token::Eof) {
2481 call_args.push(self.parse_assign_expr()?);
2482 if !self.eat(&Token::Comma) {
2483 break;
2484 }
2485 }
2486 self.expect(&Token::RParen)?;
2487 if !call_args.iter().any(Self::expr_contains_topic_var) {
2491 let topic = Expr {
2492 kind: ExprKind::ScalarVar("_".to_string()),
2493 line: stage_line,
2494 };
2495 if self.thread_last_mode {
2496 call_args.push(topic);
2497 } else {
2498 call_args.insert(0, topic);
2499 }
2500 }
2501 let call_expr = Expr {
2502 kind: ExprKind::FuncCall {
2503 name: func_name.clone(),
2504 args: call_args,
2505 },
2506 line: stage_line,
2507 };
2508 let code_ref = Expr {
2509 kind: ExprKind::CodeRef {
2510 params: vec![],
2511 body: vec![Statement {
2512 label: None,
2513 kind: StmtKind::Expression(call_expr),
2514 line: stage_line,
2515 }],
2516 },
2517 line: stage_line,
2518 };
2519 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2520 }
2521 } else {
2522 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
2524 }
2525 }
2526 Token::Regex(ref pattern, ref flags, delim) => {
2528 let pattern = pattern.clone();
2529 let flags = flags.clone();
2530 self.advance();
2531 result =
2532 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
2533 }
2534 Token::Slash => {
2537 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
2543 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
2544 && matches!(self.peek_at(1), Token::Regex(..))
2545 {
2546 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
2553 self.peek().clone()
2554 {
2555 let _ = (misparsed_pattern, misparsed_flags);
2565 }
2566 }
2567 }
2568
2569 let mut pattern = String::new();
2571 loop {
2572 match self.peek().clone() {
2573 Token::Slash => {
2574 self.advance(); break;
2576 }
2577 Token::Eof | Token::Semicolon | Token::Newline => {
2578 return Err(self
2579 .syntax_err("Unterminated regex in thread stage", stage_line));
2580 }
2581 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
2583 if pattern.is_empty()
2591 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
2592 {
2593 let _ = (inner_pattern, inner_flags, delim);
2599 }
2600 return Err(self.syntax_err(
2602 "Complex regex in thread stage - use m/pattern/ syntax instead",
2603 stage_line,
2604 ));
2605 }
2606 Token::Ident(ref s) => {
2607 pattern.push_str(s);
2608 self.advance();
2609 }
2610 Token::Integer(n) => {
2611 pattern.push_str(&n.to_string());
2612 self.advance();
2613 }
2614 Token::ScalarVar(ref v) => {
2615 pattern.push('$');
2616 pattern.push_str(v);
2617 self.advance();
2618 }
2619 Token::Dot => {
2620 pattern.push('.');
2621 self.advance();
2622 }
2623 Token::Star => {
2624 pattern.push('*');
2625 self.advance();
2626 }
2627 Token::Plus => {
2628 pattern.push('+');
2629 self.advance();
2630 }
2631 Token::Question => {
2632 pattern.push('?');
2633 self.advance();
2634 }
2635 Token::LParen => {
2636 pattern.push('(');
2637 self.advance();
2638 }
2639 Token::RParen => {
2640 pattern.push(')');
2641 self.advance();
2642 }
2643 Token::LBracket => {
2644 pattern.push('[');
2645 self.advance();
2646 }
2647 Token::RBracket => {
2648 pattern.push(']');
2649 self.advance();
2650 }
2651 Token::Backslash => {
2652 pattern.push('\\');
2653 self.advance();
2654 }
2655 Token::BitOr => {
2656 pattern.push('|');
2657 self.advance();
2658 }
2659 Token::Power => {
2660 pattern.push_str("**");
2661 self.advance();
2662 }
2663 Token::BitXor => {
2664 pattern.push('^');
2665 self.advance();
2666 }
2667 Token::Minus => {
2668 pattern.push('-');
2669 self.advance();
2670 }
2671 _ => {
2672 return Err(self.syntax_err(
2673 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2674 stage_line,
2675 ));
2676 }
2677 }
2678 }
2679 let mut flags = String::new();
2683 if let Token::Ident(ref s) = self.peek().clone() {
2684 let is_flag_only =
2685 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2686 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2687 if is_flag_only && !followed_by_brace {
2688 flags.push_str(s);
2689 self.advance();
2690 }
2691 }
2692 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2693 }
2694 tok => {
2695 return Err(self.syntax_err(
2696 format!(
2697 "thread: expected stage (ident, fn {{}}, s///, tr///, or /re/), got {:?}",
2698 tok
2699 ),
2700 stage_line,
2701 ));
2702 }
2703 };
2704 last_stage_end_line = self.prev_line();
2705 if let Some(stages) = parallel_collector.as_mut() {
2710 let stage_body = std::mem::replace(
2711 &mut result,
2712 Expr {
2713 kind: ExprKind::ScalarVar("_".into()),
2714 line: stage_line,
2715 },
2716 );
2717 stages.push(stage_body);
2718 }
2719 }
2720
2721 self.thread_last_mode = saved_thread_last;
2723
2724 if let Some(stages) = parallel_collector {
2730 let source_expr = source_for_par.unwrap_or(result);
2731 if stages.is_empty() {
2732 return Err(self.syntax_err(
2733 "~p> / ~p>> require at least one stage after the source",
2734 _line,
2735 ));
2736 }
2737 let stage_closures: Vec<Expr> = stages
2743 .drain(..)
2744 .map(|body| {
2745 let body_line = body.line;
2746 let wrapped = Expr {
2747 kind: ExprKind::ArrayRef(vec![body]),
2748 line: body_line,
2749 };
2750 Expr {
2751 kind: ExprKind::CodeRef {
2752 params: vec![],
2753 body: vec![Statement {
2754 label: None,
2755 kind: StmtKind::Expression(wrapped),
2756 line: body_line,
2757 }],
2758 },
2759 line: body_line,
2760 }
2761 })
2762 .collect();
2763 let stages_arr = Expr {
2764 kind: ExprKind::ArrayRef(stage_closures),
2765 line: _line,
2766 };
2767 let thread_last_flag = Expr {
2768 kind: ExprKind::Integer(if thread_last { 1 } else { 0 }),
2769 line: _line,
2770 };
2771 return Ok(Expr {
2778 kind: ExprKind::FuncCall {
2779 name: "_thread_par_run".into(),
2780 args: vec![stages_arr, thread_last_flag, source_expr],
2781 },
2782 line: _line,
2783 });
2784 }
2785
2786 if pipe_rhs_wrap {
2787 let body_line = result.line;
2790 return Ok(Expr {
2791 kind: ExprKind::CodeRef {
2792 params: vec![],
2793 body: vec![Statement {
2794 label: None,
2795 kind: StmtKind::Expression(result),
2796 line: body_line,
2797 }],
2798 },
2799 line: _line,
2800 });
2801 }
2802 Ok(result)
2803 }
2804
2805 fn thread_regex_grep_stage(
2807 &self,
2808 list: Expr,
2809 pattern: String,
2810 flags: String,
2811 delim: char,
2812 line: usize,
2813 ) -> Expr {
2814 let topic = Expr {
2815 kind: ExprKind::ScalarVar("_".to_string()),
2816 line,
2817 };
2818 let match_expr = Expr {
2819 kind: ExprKind::Match {
2820 expr: Box::new(topic),
2821 pattern,
2822 flags,
2823 scalar_g: false,
2824 delim,
2825 },
2826 line,
2827 };
2828 let block = vec![Statement {
2829 label: None,
2830 kind: StmtKind::Expression(match_expr),
2831 line,
2832 }];
2833 Expr {
2834 kind: ExprKind::GrepExpr {
2835 block,
2836 list: Box::new(list),
2837 keyword: crate::ast::GrepBuiltinKeyword::Grep,
2838 },
2839 line,
2840 }
2841 }
2842
2843 fn expr_contains_topic_var(e: &Expr) -> bool {
2854 format!("{:?}", e).contains("ScalarVar(\"_\")")
2855 }
2856
2857 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> PerlResult<Expr> {
2859 let kind = match name {
2860 "uc" => ExprKind::Uc(Box::new(arg)),
2862 "lc" => ExprKind::Lc(Box::new(arg)),
2863 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
2864 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
2865 "fc" => ExprKind::Fc(Box::new(arg)),
2866 "chomp" => ExprKind::Chomp(Box::new(arg)),
2867 "chop" => ExprKind::Chop(Box::new(arg)),
2868 "length" => ExprKind::Length(Box::new(arg)),
2869 "len" | "cnt" => ExprKind::FuncCall {
2870 name: "count".to_string(),
2871 args: vec![arg],
2872 },
2873 "quotemeta" | "qm" => ExprKind::FuncCall {
2874 name: "quotemeta".to_string(),
2875 args: vec![arg],
2876 },
2877 "abs" => ExprKind::Abs(Box::new(arg)),
2879 "int" => ExprKind::Int(Box::new(arg)),
2880 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
2881 "sin" => ExprKind::Sin(Box::new(arg)),
2882 "cos" => ExprKind::Cos(Box::new(arg)),
2883 "exp" => ExprKind::Exp(Box::new(arg)),
2884 "log" => ExprKind::Log(Box::new(arg)),
2885 "hex" => ExprKind::Hex(Box::new(arg)),
2886 "oct" => ExprKind::Oct(Box::new(arg)),
2887 "chr" => ExprKind::Chr(Box::new(arg)),
2888 "ord" => ExprKind::Ord(Box::new(arg)),
2889 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
2891 "ref" => ExprKind::Ref(Box::new(arg)),
2892 "scalar" => {
2893 if crate::no_interop_mode() {
2894 return Err(self.syntax_err(
2895 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
2896 line,
2897 ));
2898 }
2899 ExprKind::ScalarContext(Box::new(arg))
2900 }
2901 "keys" => ExprKind::Keys(Box::new(arg)),
2903 "values" => ExprKind::Values(Box::new(arg)),
2904 "each" => ExprKind::Each(Box::new(arg)),
2905 "pop" => ExprKind::Pop(Box::new(arg)),
2906 "shift" => ExprKind::Shift(Box::new(arg)),
2907 "reverse" => {
2908 if crate::no_interop_mode() {
2909 return Err(self.syntax_err(
2910 "stryke uses `rev` instead of `reverse` (--no-interop)",
2911 line,
2912 ));
2913 }
2914 ExprKind::ReverseExpr(Box::new(arg))
2915 }
2916 "reversed" | "rv" | "rev" => ExprKind::Rev(Box::new(arg)),
2917 "sort" | "so" => ExprKind::SortExpr {
2918 cmp: None,
2919 list: Box::new(arg),
2920 },
2921 "psort" => ExprKind::PSortExpr {
2922 cmp: None,
2923 list: Box::new(arg),
2924 progress: None,
2925 },
2926 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
2927 name: "uniq".to_string(),
2928 args: vec![arg],
2929 },
2930 "trim" | "tm" => ExprKind::FuncCall {
2931 name: "trim".to_string(),
2932 args: vec![arg],
2933 },
2934 "flatten" | "fl" => ExprKind::FuncCall {
2935 name: "flatten".to_string(),
2936 args: vec![arg],
2937 },
2938 "compact" | "cpt" => ExprKind::FuncCall {
2939 name: "compact".to_string(),
2940 args: vec![arg],
2941 },
2942 "shuffle" | "shuf" => ExprKind::FuncCall {
2943 name: "shuffle".to_string(),
2944 args: vec![arg],
2945 },
2946 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
2947 name: "frequencies".to_string(),
2948 args: vec![arg],
2949 },
2950 "pfrequencies" | "pfreq" | "pfrq" => ExprKind::FuncCall {
2951 name: "pfrequencies".to_string(),
2952 args: vec![arg],
2953 },
2954 "dedup" | "dup" => ExprKind::FuncCall {
2955 name: "dedup".to_string(),
2956 args: vec![arg],
2957 },
2958 "enumerate" | "en" => ExprKind::FuncCall {
2959 name: "enumerate".to_string(),
2960 args: vec![arg],
2961 },
2962 "lines" | "ln" => ExprKind::FuncCall {
2963 name: "lines".to_string(),
2964 args: vec![arg],
2965 },
2966 "words" | "wd" => ExprKind::FuncCall {
2967 name: "words".to_string(),
2968 args: vec![arg],
2969 },
2970 "chars" | "ch" => ExprKind::FuncCall {
2971 name: "chars".to_string(),
2972 args: vec![arg],
2973 },
2974 "digits" | "dg" => ExprKind::FuncCall {
2975 name: "digits".to_string(),
2976 args: vec![arg],
2977 },
2978 "letters" | "lts" => ExprKind::FuncCall {
2979 name: "letters".to_string(),
2980 args: vec![arg],
2981 },
2982 "letters_uc" => ExprKind::FuncCall {
2983 name: "letters_uc".to_string(),
2984 args: vec![arg],
2985 },
2986 "letters_lc" => ExprKind::FuncCall {
2987 name: "letters_lc".to_string(),
2988 args: vec![arg],
2989 },
2990 "punctuation" | "punct" => ExprKind::FuncCall {
2991 name: "punctuation".to_string(),
2992 args: vec![arg],
2993 },
2994 "sentences" | "sents" => ExprKind::FuncCall {
2995 name: "sentences".to_string(),
2996 args: vec![arg],
2997 },
2998 "paragraphs" | "paras" => ExprKind::FuncCall {
2999 name: "paragraphs".to_string(),
3000 args: vec![arg],
3001 },
3002 "sections" | "sects" => ExprKind::FuncCall {
3003 name: "sections".to_string(),
3004 args: vec![arg],
3005 },
3006 "numbers" | "nums" => ExprKind::FuncCall {
3007 name: "numbers".to_string(),
3008 args: vec![arg],
3009 },
3010 "graphemes" | "grs" => ExprKind::FuncCall {
3011 name: "graphemes".to_string(),
3012 args: vec![arg],
3013 },
3014 "columns" | "cols" => ExprKind::FuncCall {
3015 name: "columns".to_string(),
3016 args: vec![arg],
3017 },
3018 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
3020 "chdir" => ExprKind::Chdir(Box::new(arg)),
3021 "stat" => ExprKind::Stat(Box::new(arg)),
3022 "lstat" => ExprKind::Lstat(Box::new(arg)),
3023 "readlink" => ExprKind::Readlink(Box::new(arg)),
3024 "readdir" => ExprKind::Readdir(Box::new(arg)),
3025 "close" => ExprKind::Close(Box::new(arg)),
3026 "basename" | "bn" => ExprKind::FuncCall {
3027 name: "basename".to_string(),
3028 args: vec![arg],
3029 },
3030 "dirname" | "dn" => ExprKind::FuncCall {
3031 name: "dirname".to_string(),
3032 args: vec![arg],
3033 },
3034 "realpath" | "rp" => ExprKind::FuncCall {
3035 name: "realpath".to_string(),
3036 args: vec![arg],
3037 },
3038 "which" | "wh" => ExprKind::FuncCall {
3039 name: "which".to_string(),
3040 args: vec![arg],
3041 },
3042 "eval" => ExprKind::Eval(Box::new(arg)),
3044 "require" => ExprKind::Require(Box::new(arg)),
3045 "study" => ExprKind::Study(Box::new(arg)),
3046 "snake_case" | "sc" => ExprKind::FuncCall {
3048 name: "snake_case".to_string(),
3049 args: vec![arg],
3050 },
3051 "camel_case" | "cc" => ExprKind::FuncCall {
3052 name: "camel_case".to_string(),
3053 args: vec![arg],
3054 },
3055 "kebab_case" | "kc" => ExprKind::FuncCall {
3056 name: "kebab_case".to_string(),
3057 args: vec![arg],
3058 },
3059 "to_json" | "tj" => ExprKind::FuncCall {
3061 name: "to_json".to_string(),
3062 args: vec![arg],
3063 },
3064 "to_yaml" | "ty" => ExprKind::FuncCall {
3065 name: "to_yaml".to_string(),
3066 args: vec![arg],
3067 },
3068 "to_toml" | "tt" => ExprKind::FuncCall {
3069 name: "to_toml".to_string(),
3070 args: vec![arg],
3071 },
3072 "to_csv" | "tc" => ExprKind::FuncCall {
3073 name: "to_csv".to_string(),
3074 args: vec![arg],
3075 },
3076 "to_xml" | "tx" => ExprKind::FuncCall {
3077 name: "to_xml".to_string(),
3078 args: vec![arg],
3079 },
3080 "to_html" | "th" => ExprKind::FuncCall {
3081 name: "to_html".to_string(),
3082 args: vec![arg],
3083 },
3084 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
3085 name: "to_markdown".to_string(),
3086 args: vec![arg],
3087 },
3088 "xopen" | "xo" => ExprKind::FuncCall {
3089 name: "xopen".to_string(),
3090 args: vec![arg],
3091 },
3092 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
3093 name: "clip".to_string(),
3094 args: vec![arg],
3095 },
3096 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
3097 name: "to_table".to_string(),
3098 args: vec![arg],
3099 },
3100 "sparkline" | "spark" => ExprKind::FuncCall {
3101 name: "sparkline".to_string(),
3102 args: vec![arg],
3103 },
3104 "bar_chart" | "bars" => ExprKind::FuncCall {
3105 name: "bar_chart".to_string(),
3106 args: vec![arg],
3107 },
3108 "flame" | "flamechart" => ExprKind::FuncCall {
3109 name: "flame".to_string(),
3110 args: vec![arg],
3111 },
3112 "ddump" | "dd" => ExprKind::FuncCall {
3113 name: "ddump".to_string(),
3114 args: vec![arg],
3115 },
3116 "say" => {
3117 if crate::no_interop_mode() {
3118 return Err(
3119 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
3120 );
3121 }
3122 ExprKind::Say {
3123 handle: None,
3124 args: vec![arg],
3125 }
3126 }
3127 "p" => ExprKind::Say {
3128 handle: None,
3129 args: vec![arg],
3130 },
3131 "print" => ExprKind::Print {
3132 handle: None,
3133 args: vec![arg],
3134 },
3135 "warn" => ExprKind::Warn(vec![arg]),
3136 "die" => ExprKind::Die(vec![arg]),
3137 "stringify" | "str" => ExprKind::FuncCall {
3138 name: "stringify".to_string(),
3139 args: vec![arg],
3140 },
3141 "json_decode" | "jd" => ExprKind::FuncCall {
3142 name: "json_decode".to_string(),
3143 args: vec![arg],
3144 },
3145 "yaml_decode" | "yd" => ExprKind::FuncCall {
3146 name: "yaml_decode".to_string(),
3147 args: vec![arg],
3148 },
3149 "toml_decode" | "td" => ExprKind::FuncCall {
3150 name: "toml_decode".to_string(),
3151 args: vec![arg],
3152 },
3153 "xml_decode" | "xd" => ExprKind::FuncCall {
3154 name: "xml_decode".to_string(),
3155 args: vec![arg],
3156 },
3157 "json_encode" | "je" => ExprKind::FuncCall {
3158 name: "json_encode".to_string(),
3159 args: vec![arg],
3160 },
3161 "yaml_encode" | "ye" => ExprKind::FuncCall {
3162 name: "yaml_encode".to_string(),
3163 args: vec![arg],
3164 },
3165 "toml_encode" | "te" => ExprKind::FuncCall {
3166 name: "toml_encode".to_string(),
3167 args: vec![arg],
3168 },
3169 "xml_encode" | "xe" => ExprKind::FuncCall {
3170 name: "xml_encode".to_string(),
3171 args: vec![arg],
3172 },
3173 "base64_encode" | "b64e" => ExprKind::FuncCall {
3175 name: "base64_encode".to_string(),
3176 args: vec![arg],
3177 },
3178 "base64_decode" | "b64d" => ExprKind::FuncCall {
3179 name: "base64_decode".to_string(),
3180 args: vec![arg],
3181 },
3182 "hex_encode" | "hxe" => ExprKind::FuncCall {
3183 name: "hex_encode".to_string(),
3184 args: vec![arg],
3185 },
3186 "hex_decode" | "hxd" => ExprKind::FuncCall {
3187 name: "hex_decode".to_string(),
3188 args: vec![arg],
3189 },
3190 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
3191 name: "url_encode".to_string(),
3192 args: vec![arg],
3193 },
3194 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
3195 name: "url_decode".to_string(),
3196 args: vec![arg],
3197 },
3198 "gzip" | "gz" => ExprKind::FuncCall {
3199 name: "gzip".to_string(),
3200 args: vec![arg],
3201 },
3202 "gunzip" | "ugz" => ExprKind::FuncCall {
3203 name: "gunzip".to_string(),
3204 args: vec![arg],
3205 },
3206 "zstd" | "zst" => ExprKind::FuncCall {
3207 name: "zstd".to_string(),
3208 args: vec![arg],
3209 },
3210 "zstd_decode" | "uzst" => ExprKind::FuncCall {
3211 name: "zstd_decode".to_string(),
3212 args: vec![arg],
3213 },
3214 "sha256" | "s256" => ExprKind::FuncCall {
3216 name: "sha256".to_string(),
3217 args: vec![arg],
3218 },
3219 "sha1" | "s1" => ExprKind::FuncCall {
3220 name: "sha1".to_string(),
3221 args: vec![arg],
3222 },
3223 "md5" | "m5" => ExprKind::FuncCall {
3224 name: "md5".to_string(),
3225 args: vec![arg],
3226 },
3227 "uuid" | "uid" => ExprKind::FuncCall {
3228 name: "uuid".to_string(),
3229 args: vec![arg],
3230 },
3231 "datetime_utc" | "utc" => ExprKind::FuncCall {
3233 name: "datetime_utc".to_string(),
3234 args: vec![arg],
3235 },
3236 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
3239 block: vec![Statement {
3240 label: None,
3241 kind: StmtKind::Expression(Expr {
3242 kind: ExprKind::Say {
3243 handle: None,
3244 args: vec![Expr {
3245 kind: ExprKind::ScalarVar("_".into()),
3246 line,
3247 }],
3248 },
3249 line,
3250 }),
3251 line,
3252 }],
3253 list: Box::new(arg),
3254 },
3255 _ => ExprKind::FuncCall {
3257 name: name.to_string(),
3258 args: vec![arg],
3259 },
3260 };
3261 Ok(Expr { kind, line })
3262 }
3263
3264 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> PerlResult<Expr> {
3267 let block = self.parse_block()?;
3268 let placeholder = self.pipe_placeholder_list(line);
3270
3271 match name {
3272 "map" | "flat_map" | "maps" | "flat_maps" => {
3273 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
3274 let stream = matches!(name, "maps" | "flat_maps");
3275 Ok(Expr {
3276 kind: ExprKind::MapExpr {
3277 block,
3278 list: Box::new(placeholder),
3279 flatten_array_refs,
3280 stream,
3281 },
3282 line,
3283 })
3284 }
3285 "grep" | "greps" | "filter" | "fi" | "find_all" | "gr" => {
3286 let keyword = match name {
3287 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
3288 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
3289 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
3290 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
3291 _ => unreachable!(),
3292 };
3293 Ok(Expr {
3294 kind: ExprKind::GrepExpr {
3295 block,
3296 list: Box::new(placeholder),
3297 keyword,
3298 },
3299 line,
3300 })
3301 }
3302 "sort" | "so" => Ok(Expr {
3303 kind: ExprKind::SortExpr {
3304 cmp: Some(SortComparator::Block(block)),
3305 list: Box::new(placeholder),
3306 },
3307 line,
3308 }),
3309 "reduce" | "rd" => Ok(Expr {
3310 kind: ExprKind::ReduceExpr {
3311 block,
3312 list: Box::new(placeholder),
3313 },
3314 line,
3315 }),
3316 "fore" | "e" | "ep" => Ok(Expr {
3317 kind: ExprKind::ForEachExpr {
3318 block,
3319 list: Box::new(placeholder),
3320 },
3321 line,
3322 }),
3323 "par" => Ok(Expr {
3324 kind: ExprKind::ParExpr {
3325 block,
3326 list: Box::new(placeholder),
3327 },
3328 line,
3329 }),
3330 "pmap" | "pflat_map" | "pmaps" | "pflat_maps" => Ok(Expr {
3331 kind: ExprKind::PMapExpr {
3332 block,
3333 list: Box::new(placeholder),
3334 progress: None,
3335 flat_outputs: name == "pflat_map" || name == "pflat_maps",
3336 on_cluster: None,
3337 stream: name == "pmaps" || name == "pflat_maps",
3338 },
3339 line,
3340 }),
3341 "pgrep" | "pgreps" => Ok(Expr {
3342 kind: ExprKind::PGrepExpr {
3343 block,
3344 list: Box::new(placeholder),
3345 progress: None,
3346 stream: name == "pgreps",
3347 },
3348 line,
3349 }),
3350 "pfor" => Ok(Expr {
3351 kind: ExprKind::PForExpr {
3352 block,
3353 list: Box::new(placeholder),
3354 progress: None,
3355 },
3356 line,
3357 }),
3358 "preduce" => Ok(Expr {
3359 kind: ExprKind::PReduceExpr {
3360 block,
3361 list: Box::new(placeholder),
3362 progress: None,
3363 },
3364 line,
3365 }),
3366 "pcache" => Ok(Expr {
3367 kind: ExprKind::PcacheExpr {
3368 block,
3369 list: Box::new(placeholder),
3370 progress: None,
3371 },
3372 line,
3373 }),
3374 "psort" => Ok(Expr {
3375 kind: ExprKind::PSortExpr {
3376 cmp: Some(block),
3377 list: Box::new(placeholder),
3378 progress: None,
3379 },
3380 line,
3381 }),
3382 _ => {
3383 let code_ref = Expr {
3390 kind: ExprKind::CodeRef {
3391 params: vec![],
3392 body: block,
3393 },
3394 line,
3395 };
3396 let args = if Self::is_block_then_list_pipe_builtin(name) {
3397 vec![code_ref, placeholder]
3398 } else {
3399 vec![code_ref]
3400 };
3401 Ok(Expr {
3402 kind: ExprKind::FuncCall {
3403 name: name.to_string(),
3404 args,
3405 },
3406 line,
3407 })
3408 }
3409 }
3410 }
3411
3412 fn parse_tie_stmt(&mut self) -> PerlResult<Statement> {
3414 let line = self.peek_line();
3415 self.advance(); let mut implicit_decl: Option<Statement> = None;
3422 if let Token::Ident(kw) = self.peek().clone() {
3423 if matches!(kw.as_str(), "my" | "our") {
3424 let kw_line = self.peek_line();
3425 self.advance(); let (decl_sigil, decl_name) = match self.peek().clone() {
3428 Token::ScalarVar(s) => (Sigil::Scalar, s),
3429 Token::ArrayVar(a) => (Sigil::Array, a),
3430 Token::HashVar(h) => (Sigil::Hash, h),
3431 tok => {
3432 return Err(self.syntax_err(
3433 format!("expected variable after `tie {}`, got {:?}", kw, tok),
3434 self.peek_line(),
3435 ));
3436 }
3437 };
3438 let decls = vec![VarDecl {
3439 sigil: decl_sigil,
3440 name: decl_name.clone(),
3441 initializer: None,
3442 frozen: false,
3443 type_annotation: None,
3444 }];
3445 implicit_decl = Some(Statement {
3446 label: None,
3447 kind: if kw == "my" {
3448 StmtKind::My(decls)
3449 } else {
3450 StmtKind::Our(decls)
3451 },
3452 line: kw_line,
3453 });
3454 }
3459 }
3460 let target = match self.peek().clone() {
3461 Token::HashVar(h) => {
3462 self.advance();
3463 TieTarget::Hash(h)
3464 }
3465 Token::ArrayVar(a) => {
3466 self.advance();
3467 TieTarget::Array(a)
3468 }
3469 Token::ScalarVar(s) => {
3470 self.advance();
3471 TieTarget::Scalar(s)
3472 }
3473 tok => {
3474 return Err(self.syntax_err(
3475 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
3476 self.peek_line(),
3477 ));
3478 }
3479 };
3480 self.expect(&Token::Comma)?;
3481 let class = self.parse_assign_expr()?;
3482 let mut args = Vec::new();
3483 while self.eat(&Token::Comma) {
3484 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
3485 break;
3486 }
3487 args.push(self.parse_assign_expr()?);
3488 }
3489 self.eat(&Token::Semicolon);
3490 let tie_stmt = Statement {
3491 label: None,
3492 kind: StmtKind::Tie {
3493 target,
3494 class,
3495 args,
3496 },
3497 line,
3498 };
3499 if let Some(decl) = implicit_decl {
3500 Ok(Statement {
3505 label: None,
3506 kind: StmtKind::StmtGroup(vec![decl, tie_stmt]),
3507 line,
3508 })
3509 } else {
3510 Ok(tie_stmt)
3511 }
3512 }
3513
3514 fn parse_given(&mut self) -> PerlResult<Statement> {
3516 let line = self.peek_line();
3517 self.advance();
3518 self.expect(&Token::LParen)?;
3519 let topic = self.parse_expression()?;
3520 self.expect(&Token::RParen)?;
3521 let body = self.parse_block()?;
3522 self.eat(&Token::Semicolon);
3523 Ok(Statement {
3524 label: None,
3525 kind: StmtKind::Given { topic, body },
3526 line,
3527 })
3528 }
3529
3530 fn parse_when_stmt(&mut self) -> PerlResult<Statement> {
3532 let line = self.peek_line();
3533 self.advance();
3534 self.expect(&Token::LParen)?;
3535 let cond = self.parse_expression()?;
3536 self.expect(&Token::RParen)?;
3537 let body = self.parse_block()?;
3538 self.eat(&Token::Semicolon);
3539 Ok(Statement {
3540 label: None,
3541 kind: StmtKind::When { cond, body },
3542 line,
3543 })
3544 }
3545
3546 fn parse_default_stmt(&mut self) -> PerlResult<Statement> {
3548 let line = self.peek_line();
3549 self.advance();
3550 let body = self.parse_block()?;
3551 self.eat(&Token::Semicolon);
3552 Ok(Statement {
3553 label: None,
3554 kind: StmtKind::DefaultCase { body },
3555 line,
3556 })
3557 }
3558
3559 fn parse_cond_expr(&mut self, line: usize) -> PerlResult<Expr> {
3565 self.expect(&Token::LBrace)?;
3566
3567 let mut arms: Vec<(Expr, Block)> = Vec::new();
3568 let mut else_block: Option<Block> = None;
3569
3570 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3571 let arm_line = self.peek_line();
3572
3573 let is_default = matches!(self.peek(), Token::Ident(ref s) if s == "default")
3575 && matches!(self.peek_at(1), Token::FatArrow);
3576
3577 if is_default {
3578 self.advance(); self.advance(); let body = if matches!(self.peek(), Token::LBrace) {
3581 self.parse_block()?
3582 } else {
3583 let expr = self.parse_assign_expr()?;
3584 vec![Statement {
3585 label: None,
3586 kind: StmtKind::Expression(expr),
3587 line: arm_line,
3588 }]
3589 };
3590 else_block = Some(body);
3591 self.eat(&Token::Comma);
3592 break; }
3594
3595 let condition = self.parse_assign_expr()?;
3597 self.expect(&Token::FatArrow)?;
3598
3599 let body = if matches!(self.peek(), Token::LBrace) {
3600 self.parse_block()?
3601 } else {
3602 let expr = self.parse_assign_expr()?;
3603 vec![Statement {
3604 label: None,
3605 kind: StmtKind::Expression(expr),
3606 line: arm_line,
3607 }]
3608 };
3609
3610 arms.push((condition, body));
3611 self.eat(&Token::Comma);
3612 }
3613
3614 self.expect(&Token::RBrace)?;
3615
3616 if arms.is_empty() {
3617 return Err(self.syntax_err("cond requires at least one condition arm", line));
3618 }
3619
3620 let (first_cond, first_body) = arms.remove(0);
3622 let elsifs: Vec<(Expr, Block)> = arms;
3623
3624 let if_stmt = Statement {
3626 label: None,
3627 kind: StmtKind::If {
3628 condition: first_cond,
3629 body: first_body,
3630 elsifs,
3631 else_block,
3632 },
3633 line,
3634 };
3635 let inner = Expr {
3636 kind: ExprKind::CodeRef {
3637 params: vec![],
3638 body: vec![if_stmt],
3639 },
3640 line,
3641 };
3642 Ok(Expr {
3643 kind: ExprKind::Do(Box::new(inner)),
3644 line,
3645 })
3646 }
3647
3648 fn parse_algebraic_match_expr(&mut self, line: usize) -> PerlResult<Expr> {
3650 self.expect(&Token::LParen)?;
3651 let subject = self.parse_expression()?;
3652 self.expect(&Token::RParen)?;
3653 self.expect(&Token::LBrace)?;
3654 let mut arms = Vec::new();
3655 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3656 if self.eat(&Token::Semicolon) {
3657 continue;
3658 }
3659 let pattern = self.parse_match_pattern()?;
3660 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
3661 self.advance();
3662 Some(Box::new(self.parse_assign_expr()?))
3665 } else {
3666 None
3667 };
3668 self.expect(&Token::FatArrow)?;
3669 let body = self.parse_assign_expr()?;
3671 arms.push(MatchArm {
3672 pattern,
3673 guard,
3674 body,
3675 });
3676 self.eat(&Token::Comma);
3677 }
3678 self.expect(&Token::RBrace)?;
3679 Ok(Expr {
3680 kind: ExprKind::AlgebraicMatch {
3681 subject: Box::new(subject),
3682 arms,
3683 },
3684 line,
3685 })
3686 }
3687
3688 fn parse_match_pattern(&mut self) -> PerlResult<MatchPattern> {
3689 match self.peek().clone() {
3690 Token::Regex(pattern, flags, _delim) => {
3691 self.advance();
3692 Ok(MatchPattern::Regex { pattern, flags })
3693 }
3694 Token::Ident(ref s) if s == "_" => {
3695 self.advance();
3696 Ok(MatchPattern::Any)
3697 }
3698 Token::Ident(ref s) if s == "Some" => {
3699 self.advance();
3700 self.expect(&Token::LParen)?;
3701 let name = self.parse_scalar_var_name()?;
3702 self.expect(&Token::RParen)?;
3703 Ok(MatchPattern::OptionSome(name))
3704 }
3705 Token::LBracket => self.parse_match_array_pattern(),
3706 Token::LBrace => self.parse_match_hash_pattern(),
3707 Token::LParen => {
3708 self.advance();
3709 let e = self.parse_expression()?;
3710 self.expect(&Token::RParen)?;
3711 Ok(MatchPattern::Value(Box::new(e)))
3712 }
3713 _ => {
3714 let e = self.parse_assign_expr()?;
3715 Ok(MatchPattern::Value(Box::new(e)))
3716 }
3717 }
3718 }
3719
3720 fn parse_match_array_elems_until_rbracket(&mut self) -> PerlResult<Vec<MatchArrayElem>> {
3722 let mut elems = Vec::new();
3723 if self.eat(&Token::RBracket) {
3724 return Ok(vec![]);
3725 }
3726 loop {
3727 if matches!(self.peek(), Token::Star) {
3728 self.advance();
3729 elems.push(MatchArrayElem::Rest);
3730 self.eat(&Token::Comma);
3731 if !matches!(self.peek(), Token::RBracket) {
3732 return Err(self.syntax_err(
3733 "`*` must be the last element in an array match pattern",
3734 self.peek_line(),
3735 ));
3736 }
3737 self.expect(&Token::RBracket)?;
3738 return Ok(elems);
3739 }
3740 if let Token::ArrayVar(name) = self.peek().clone() {
3741 self.advance();
3742 elems.push(MatchArrayElem::RestBind(name));
3743 self.eat(&Token::Comma);
3744 if !matches!(self.peek(), Token::RBracket) {
3745 return Err(self.syntax_err(
3746 "`@name` rest bind must be the last element in an array match pattern",
3747 self.peek_line(),
3748 ));
3749 }
3750 self.expect(&Token::RBracket)?;
3751 return Ok(elems);
3752 }
3753 if let Token::ScalarVar(name) = self.peek().clone() {
3754 self.advance();
3755 elems.push(MatchArrayElem::CaptureScalar(name));
3756 if self.eat(&Token::Comma) {
3757 if matches!(self.peek(), Token::RBracket) {
3758 break;
3759 }
3760 continue;
3761 }
3762 break;
3763 }
3764 let e = self.parse_assign_expr()?;
3765 elems.push(MatchArrayElem::Expr(e));
3766 if self.eat(&Token::Comma) {
3767 if matches!(self.peek(), Token::RBracket) {
3768 break;
3769 }
3770 continue;
3771 }
3772 break;
3773 }
3774 self.expect(&Token::RBracket)?;
3775 Ok(elems)
3776 }
3777
3778 fn parse_match_array_pattern(&mut self) -> PerlResult<MatchPattern> {
3779 self.expect(&Token::LBracket)?;
3780 let elems = self.parse_match_array_elems_until_rbracket()?;
3781 Ok(MatchPattern::Array(elems))
3782 }
3783
3784 fn parse_match_hash_pattern(&mut self) -> PerlResult<MatchPattern> {
3785 self.expect(&Token::LBrace)?;
3786 let mut pairs = Vec::new();
3787 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3788 if self.eat(&Token::Semicolon) {
3789 continue;
3790 }
3791 let key = self.parse_assign_expr()?;
3792 self.expect(&Token::FatArrow)?;
3793 match self.advance().0 {
3794 Token::Ident(ref s) if s == "_" => {
3795 pairs.push(MatchHashPair::KeyOnly { key });
3796 }
3797 Token::ScalarVar(name) => {
3798 pairs.push(MatchHashPair::Capture { key, name });
3799 }
3800 tok => {
3801 return Err(self.syntax_err(
3802 format!(
3803 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
3804 tok
3805 ),
3806 self.peek_line(),
3807 ));
3808 }
3809 }
3810 self.eat(&Token::Comma);
3811 }
3812 self.expect(&Token::RBrace)?;
3813 Ok(MatchPattern::Hash(pairs))
3814 }
3815
3816 fn parse_eval_timeout(&mut self) -> PerlResult<Statement> {
3818 let line = self.peek_line();
3819 self.advance();
3820 let timeout = self.parse_postfix()?;
3821 let body = self.parse_block_or_bareword_block_no_args()?;
3822 self.eat(&Token::Semicolon);
3823 Ok(Statement {
3824 label: None,
3825 kind: StmtKind::EvalTimeout { timeout, body },
3826 line,
3827 })
3828 }
3829
3830 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
3831 match &mut cond.kind {
3832 ExprKind::Match {
3833 flags, scalar_g, ..
3834 } if flags.contains('g') => {
3835 *scalar_g = true;
3836 }
3837 ExprKind::UnaryOp {
3838 op: UnaryOp::LogNot,
3839 expr,
3840 } => {
3841 if let ExprKind::Match {
3842 flags, scalar_g, ..
3843 } = &mut expr.kind
3844 {
3845 if flags.contains('g') {
3846 *scalar_g = true;
3847 }
3848 }
3849 }
3850 _ => {}
3851 }
3852 }
3853
3854 fn parse_if(&mut self) -> PerlResult<Statement> {
3855 let line = self.peek_line();
3856 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
3858 if crate::compat_mode() {
3859 return Err(self.syntax_err(
3860 "`if let` is a stryke extension (disabled by --compat)",
3861 line,
3862 ));
3863 }
3864 return self.parse_if_let(line);
3865 }
3866 self.expect(&Token::LParen)?;
3867 let mut cond = self.parse_expression()?;
3868 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3869 self.expect(&Token::RParen)?;
3870 let body = self.parse_block()?;
3871
3872 let mut elsifs = Vec::new();
3873 let mut else_block = None;
3874
3875 loop {
3876 if let Token::Ident(ref kw) = self.peek().clone() {
3877 if kw == "elsif" {
3878 self.advance();
3879 self.expect(&Token::LParen)?;
3880 let mut c = self.parse_expression()?;
3881 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
3882 self.expect(&Token::RParen)?;
3883 let b = self.parse_block()?;
3884 elsifs.push((c, b));
3885 continue;
3886 }
3887 if kw == "else" {
3888 self.advance();
3889 else_block = Some(self.parse_block()?);
3890 }
3891 }
3892 break;
3893 }
3894
3895 Ok(Statement {
3896 label: None,
3897 kind: StmtKind::If {
3898 condition: cond,
3899 body,
3900 elsifs,
3901 else_block,
3902 },
3903 line,
3904 })
3905 }
3906
3907 fn parse_if_let(&mut self, line: usize) -> PerlResult<Statement> {
3909 self.advance(); let pattern = self.parse_match_pattern()?;
3911 self.expect(&Token::Assign)?;
3912 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
3914 let rhs = self.parse_assign_expr();
3915 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
3916 let rhs = rhs?;
3917 let then_block = self.parse_block()?;
3918 let else_block_opt = match self.peek().clone() {
3919 Token::Ident(ref kw) if kw == "else" => {
3920 self.advance();
3921 Some(self.parse_block()?)
3922 }
3923 Token::Ident(ref kw) if kw == "elsif" => {
3924 return Err(self.syntax_err(
3925 "`if let` does not support `elsif`; use `else { }` or a full `match`",
3926 self.peek_line(),
3927 ));
3928 }
3929 _ => None,
3930 };
3931 let then_expr = Self::expr_do_anon_block(then_block, line);
3932 let else_expr = if let Some(eb) = else_block_opt {
3933 Self::expr_do_anon_block(eb, line)
3934 } else {
3935 Expr {
3936 kind: ExprKind::Undef,
3937 line,
3938 }
3939 };
3940 let arms = vec![
3941 MatchArm {
3942 pattern,
3943 guard: None,
3944 body: then_expr,
3945 },
3946 MatchArm {
3947 pattern: MatchPattern::Any,
3948 guard: None,
3949 body: else_expr,
3950 },
3951 ];
3952 Ok(Statement {
3953 label: None,
3954 kind: StmtKind::Expression(Expr {
3955 kind: ExprKind::AlgebraicMatch {
3956 subject: Box::new(rhs),
3957 arms,
3958 },
3959 line,
3960 }),
3961 line,
3962 })
3963 }
3964
3965 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
3966 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
3967 Expr {
3968 kind: ExprKind::Do(Box::new(Expr {
3969 kind: ExprKind::CodeRef {
3970 params: vec![],
3971 body: block,
3972 },
3973 line: inner_line,
3974 })),
3975 line: outer_line,
3976 }
3977 }
3978
3979 fn parse_unless(&mut self) -> PerlResult<Statement> {
3980 let line = self.peek_line();
3981 self.advance(); self.expect(&Token::LParen)?;
3983 let mut cond = self.parse_expression()?;
3984 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3985 self.expect(&Token::RParen)?;
3986 let body = self.parse_block()?;
3987 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
3988 if kw == "else" {
3989 self.advance();
3990 Some(self.parse_block()?)
3991 } else {
3992 None
3993 }
3994 } else {
3995 None
3996 };
3997 Ok(Statement {
3998 label: None,
3999 kind: StmtKind::Unless {
4000 condition: cond,
4001 body,
4002 else_block,
4003 },
4004 line,
4005 })
4006 }
4007
4008 fn parse_while(&mut self) -> PerlResult<Statement> {
4009 let line = self.peek_line();
4010 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
4012 if crate::compat_mode() {
4013 return Err(self.syntax_err(
4014 "`while let` is a stryke extension (disabled by --compat)",
4015 line,
4016 ));
4017 }
4018 return self.parse_while_let(line);
4019 }
4020 self.expect(&Token::LParen)?;
4021 let mut cond = self.parse_expression()?;
4022 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4023 self.expect(&Token::RParen)?;
4024 let body = self.parse_block()?;
4025 let continue_block = self.parse_optional_continue_block()?;
4026 Ok(Statement {
4027 label: None,
4028 kind: StmtKind::While {
4029 condition: cond,
4030 body,
4031 label: None,
4032 continue_block,
4033 },
4034 line,
4035 })
4036 }
4037
4038 fn parse_while_let(&mut self, line: usize) -> PerlResult<Statement> {
4041 self.advance(); let pattern = self.parse_match_pattern()?;
4043 self.expect(&Token::Assign)?;
4044 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4045 let rhs = self.parse_assign_expr();
4046 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4047 let rhs = rhs?;
4048 let mut user_body = self.parse_block()?;
4049 let continue_block = self.parse_optional_continue_block()?;
4050 user_body.push(Statement::new(
4051 StmtKind::Expression(Expr {
4052 kind: ExprKind::Integer(1),
4053 line,
4054 }),
4055 line,
4056 ));
4057 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
4058 let match_expr = Expr {
4059 kind: ExprKind::AlgebraicMatch {
4060 subject: Box::new(rhs),
4061 arms: vec![
4062 MatchArm {
4063 pattern,
4064 guard: None,
4065 body: Self::expr_do_anon_block(user_body, line),
4066 },
4067 MatchArm {
4068 pattern: MatchPattern::Any,
4069 guard: None,
4070 body: Expr {
4071 kind: ExprKind::Integer(0),
4072 line,
4073 },
4074 },
4075 ],
4076 },
4077 line,
4078 };
4079 let my_stmt = Statement::new(
4080 StmtKind::My(vec![VarDecl {
4081 sigil: Sigil::Scalar,
4082 name: tmp.clone(),
4083 initializer: Some(match_expr),
4084 frozen: false,
4085 type_annotation: None,
4086 }]),
4087 line,
4088 );
4089 let unless_last = Statement::new(
4090 StmtKind::Unless {
4091 condition: Expr {
4092 kind: ExprKind::ScalarVar(tmp),
4093 line,
4094 },
4095 body: vec![Statement::new(StmtKind::Last(None), line)],
4096 else_block: None,
4097 },
4098 line,
4099 );
4100 Ok(Statement::new(
4101 StmtKind::While {
4102 condition: Expr {
4103 kind: ExprKind::Integer(1),
4104 line,
4105 },
4106 body: vec![my_stmt, unless_last],
4107 label: None,
4108 continue_block,
4109 },
4110 line,
4111 ))
4112 }
4113
4114 fn parse_until(&mut self) -> PerlResult<Statement> {
4115 let line = self.peek_line();
4116 self.advance(); self.expect(&Token::LParen)?;
4118 let mut cond = self.parse_expression()?;
4119 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4120 self.expect(&Token::RParen)?;
4121 let body = self.parse_block()?;
4122 let continue_block = self.parse_optional_continue_block()?;
4123 Ok(Statement {
4124 label: None,
4125 kind: StmtKind::Until {
4126 condition: cond,
4127 body,
4128 label: None,
4129 continue_block,
4130 },
4131 line,
4132 })
4133 }
4134
4135 fn parse_optional_continue_block(&mut self) -> PerlResult<Option<Block>> {
4137 if let Token::Ident(ref kw) = self.peek().clone() {
4138 if kw == "continue" {
4139 self.advance();
4140 return Ok(Some(self.parse_block()?));
4141 }
4142 }
4143 Ok(None)
4144 }
4145
4146 fn parse_for_or_foreach(&mut self) -> PerlResult<Statement> {
4147 let line = self.peek_line();
4148 self.advance(); match self.peek() {
4154 Token::LParen => {
4155 let saved = self.pos;
4160 self.advance(); let mut depth = 1;
4163 let mut has_semi = false;
4164 let mut scan = self.pos;
4165 while scan < self.tokens.len() {
4166 match &self.tokens[scan].0 {
4167 Token::LParen => depth += 1,
4168 Token::RParen => {
4169 depth -= 1;
4170 if depth == 0 {
4171 break;
4172 }
4173 }
4174 Token::Semicolon if depth == 1 => {
4175 has_semi = true;
4176 break;
4177 }
4178 _ => {}
4179 }
4180 scan += 1;
4181 }
4182 self.pos = saved;
4183
4184 if has_semi {
4185 self.parse_c_style_for(line)
4186 } else {
4187 self.expect(&Token::LParen)?;
4189 let list = self.parse_expression()?;
4190 self.expect(&Token::RParen)?;
4191 let body = self.parse_block()?;
4192 let continue_block = self.parse_optional_continue_block()?;
4193 Ok(Statement {
4194 label: None,
4195 kind: StmtKind::Foreach {
4196 var: "_".to_string(),
4197 list,
4198 body,
4199 label: None,
4200 continue_block,
4201 },
4202 line,
4203 })
4204 }
4205 }
4206 Token::Ident(ref kw) if kw == "my" => {
4207 self.advance(); let var = self.parse_scalar_var_name()?;
4209 self.expect(&Token::LParen)?;
4210 let list = self.parse_expression()?;
4211 self.expect(&Token::RParen)?;
4212 let body = self.parse_block()?;
4213 let continue_block = self.parse_optional_continue_block()?;
4214 Ok(Statement {
4215 label: None,
4216 kind: StmtKind::Foreach {
4217 var,
4218 list,
4219 body,
4220 label: None,
4221 continue_block,
4222 },
4223 line,
4224 })
4225 }
4226 Token::ScalarVar(_) => {
4227 let var = self.parse_scalar_var_name()?;
4228 self.expect(&Token::LParen)?;
4229 let list = self.parse_expression()?;
4230 self.expect(&Token::RParen)?;
4231 let body = self.parse_block()?;
4232 let continue_block = self.parse_optional_continue_block()?;
4233 Ok(Statement {
4234 label: None,
4235 kind: StmtKind::Foreach {
4236 var,
4237 list,
4238 body,
4239 label: None,
4240 continue_block,
4241 },
4242 line,
4243 })
4244 }
4245 _ => self.parse_c_style_for(line),
4246 }
4247 }
4248
4249 fn parse_c_style_for(&mut self, line: usize) -> PerlResult<Statement> {
4250 self.expect(&Token::LParen)?;
4251 let init = if self.eat(&Token::Semicolon) {
4252 None
4253 } else {
4254 let s = self.parse_statement()?;
4255 self.eat(&Token::Semicolon);
4256 Some(Box::new(s))
4257 };
4258 let mut condition = if matches!(self.peek(), Token::Semicolon) {
4259 None
4260 } else {
4261 Some(self.parse_expression()?)
4262 };
4263 if let Some(ref mut c) = condition {
4264 Self::mark_match_scalar_g_for_boolean_condition(c);
4265 }
4266 self.expect(&Token::Semicolon)?;
4267 let step = if matches!(self.peek(), Token::RParen) {
4268 None
4269 } else {
4270 Some(self.parse_expression()?)
4271 };
4272 self.expect(&Token::RParen)?;
4273 let body = self.parse_block()?;
4274 let continue_block = self.parse_optional_continue_block()?;
4275 Ok(Statement {
4276 label: None,
4277 kind: StmtKind::For {
4278 init,
4279 condition,
4280 step,
4281 body,
4282 label: None,
4283 continue_block,
4284 },
4285 line,
4286 })
4287 }
4288
4289 fn parse_foreach(&mut self) -> PerlResult<Statement> {
4290 let line = self.peek_line();
4291 self.advance(); let var = match self.peek() {
4293 Token::Ident(ref kw) if kw == "my" => {
4294 self.advance();
4295 self.parse_scalar_var_name()?
4296 }
4297 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
4298 _ => "_".to_string(),
4299 };
4300 self.expect(&Token::LParen)?;
4301 let list = self.parse_expression()?;
4302 self.expect(&Token::RParen)?;
4303 let body = self.parse_block()?;
4304 let continue_block = self.parse_optional_continue_block()?;
4305 Ok(Statement {
4306 label: None,
4307 kind: StmtKind::Foreach {
4308 var,
4309 list,
4310 body,
4311 label: None,
4312 continue_block,
4313 },
4314 line,
4315 })
4316 }
4317
4318 fn parse_scalar_var_name(&mut self) -> PerlResult<String> {
4319 match self.advance() {
4320 (Token::ScalarVar(name), _) => Ok(name),
4321 (tok, line) => {
4322 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
4323 }
4324 }
4325 }
4326
4327 fn parse_legacy_sub_prototype_tail(&mut self) -> PerlResult<String> {
4329 let mut s = String::new();
4330 loop {
4331 match self.peek().clone() {
4332 Token::RParen => {
4333 self.advance();
4334 break;
4335 }
4336 Token::Eof => {
4337 return Err(self.syntax_err(
4338 "Unterminated sub prototype (expected ')' before end of input)",
4339 self.peek_line(),
4340 ));
4341 }
4342 Token::ScalarVar(v) if v == ")" => {
4343 self.advance();
4346 s.push('$');
4347 if matches!(self.peek(), Token::LBrace) {
4348 break;
4349 }
4350 }
4351 Token::Ident(i) => {
4352 let i = i.clone();
4353 self.advance();
4354 s.push_str(&i);
4355 }
4356 Token::Semicolon => {
4357 self.advance();
4358 s.push(';');
4359 }
4360 Token::LParen => {
4361 self.advance();
4362 s.push('(');
4363 }
4364 Token::LBracket => {
4365 self.advance();
4366 s.push('[');
4367 }
4368 Token::RBracket => {
4369 self.advance();
4370 s.push(']');
4371 }
4372 Token::Backslash => {
4373 self.advance();
4374 s.push('\\');
4375 }
4376 Token::Comma => {
4377 self.advance();
4378 s.push(',');
4379 }
4380 Token::ScalarVar(v) => {
4381 let v = v.clone();
4382 self.advance();
4383 s.push('$');
4384 s.push_str(&v);
4385 }
4386 Token::ArrayVar(v) => {
4387 let v = v.clone();
4388 self.advance();
4389 s.push('@');
4390 s.push_str(&v);
4391 }
4392 Token::ArrayAt => {
4394 self.advance();
4395 s.push('@');
4396 }
4397 Token::HashVar(v) => {
4398 let v = v.clone();
4399 self.advance();
4400 s.push('%');
4401 s.push_str(&v);
4402 }
4403 Token::HashPercent => {
4404 self.advance();
4405 s.push('%');
4406 }
4407 Token::Plus => {
4408 self.advance();
4409 s.push('+');
4410 }
4411 Token::Minus => {
4412 self.advance();
4413 s.push('-');
4414 }
4415 Token::BitAnd => {
4416 self.advance();
4417 s.push('&');
4418 }
4419 tok => {
4420 return Err(self.syntax_err(
4421 format!("Unexpected token in sub prototype: {:?}", tok),
4422 self.peek_line(),
4423 ));
4424 }
4425 }
4426 }
4427 Ok(s)
4428 }
4429
4430 fn sub_signature_list_starts_here(&self) -> bool {
4431 match self.peek() {
4432 Token::LBrace | Token::LBracket => true,
4433 Token::ScalarVar(name) if name != "$$" && name != ")" => true,
4434 Token::ArrayVar(_) | Token::HashVar(_) => true,
4435 _ => false,
4436 }
4437 }
4438
4439 fn parse_sub_signature_hash_key(&mut self) -> PerlResult<String> {
4440 let (tok, line) = self.advance();
4441 match tok {
4442 Token::Ident(i) => Ok(i),
4443 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
4444 tok => Err(self.syntax_err(
4445 format!(
4446 "sub signature: expected hash key (identifier or string), got {:?}",
4447 tok
4448 ),
4449 line,
4450 )),
4451 }
4452 }
4453
4454 fn parse_sub_signature_param_list(&mut self) -> PerlResult<Vec<SubSigParam>> {
4455 let mut params = Vec::new();
4456 loop {
4457 if matches!(self.peek(), Token::RParen) {
4458 break;
4459 }
4460 match self.peek().clone() {
4461 Token::ScalarVar(name) => {
4462 if name == "$$" || name == ")" {
4463 return Err(self.syntax_err(
4464 format!(
4465 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
4466 ),
4467 self.peek_line(),
4468 ));
4469 }
4470 self.advance();
4471 let ty = if self.eat(&Token::Colon) {
4472 match self.peek() {
4473 Token::Ident(ref tname) => {
4474 let tname = tname.clone();
4475 self.advance();
4476 Some(match tname.as_str() {
4477 "Int" => PerlTypeName::Int,
4478 "Str" => PerlTypeName::Str,
4479 "Float" => PerlTypeName::Float,
4480 "Bool" => PerlTypeName::Bool,
4481 "Array" => PerlTypeName::Array,
4482 "Hash" => PerlTypeName::Hash,
4483 "Ref" => PerlTypeName::Ref,
4484 "Any" => PerlTypeName::Any,
4485 _ => PerlTypeName::Struct(tname),
4486 })
4487 }
4488 _ => {
4489 return Err(self.syntax_err(
4490 "expected type name after `:` in sub signature",
4491 self.peek_line(),
4492 ));
4493 }
4494 }
4495 } else {
4496 None
4497 };
4498 let default = if self.eat(&Token::Assign) {
4500 Some(Box::new(self.parse_ternary()?))
4501 } else {
4502 None
4503 };
4504 params.push(SubSigParam::Scalar(name, ty, default));
4505 }
4506 Token::ArrayVar(name) => {
4507 self.advance();
4508 let default = if self.eat(&Token::Assign) {
4509 Some(Box::new(self.parse_ternary()?))
4510 } else {
4511 None
4512 };
4513 params.push(SubSigParam::Array(name, default));
4514 }
4515 Token::HashVar(name) => {
4516 self.advance();
4517 let default = if self.eat(&Token::Assign) {
4518 Some(Box::new(self.parse_ternary()?))
4519 } else {
4520 None
4521 };
4522 params.push(SubSigParam::Hash(name, default));
4523 }
4524 Token::LBracket => {
4525 self.advance();
4526 let elems = self.parse_match_array_elems_until_rbracket()?;
4527 params.push(SubSigParam::ArrayDestruct(elems));
4528 }
4529 Token::LBrace => {
4530 self.advance();
4531 let mut pairs = Vec::new();
4532 loop {
4533 if matches!(self.peek(), Token::RBrace | Token::Eof) {
4534 break;
4535 }
4536 if self.eat(&Token::Comma) {
4537 continue;
4538 }
4539 let key = self.parse_sub_signature_hash_key()?;
4540 self.expect(&Token::FatArrow)?;
4541 let bind = self.parse_scalar_var_name()?;
4542 pairs.push((key, bind));
4543 self.eat(&Token::Comma);
4544 }
4545 self.expect(&Token::RBrace)?;
4546 params.push(SubSigParam::HashDestruct(pairs));
4547 }
4548 tok => {
4549 return Err(self.syntax_err(
4550 format!(
4551 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
4552 tok
4553 ),
4554 self.peek_line(),
4555 ));
4556 }
4557 }
4558 match self.peek() {
4559 Token::Comma => {
4560 self.advance();
4561 if matches!(self.peek(), Token::RParen) {
4562 return Err(self.syntax_err(
4563 "trailing `,` before `)` in sub signature",
4564 self.peek_line(),
4565 ));
4566 }
4567 }
4568 Token::RParen => break,
4569 _ => {
4570 return Err(self.syntax_err(
4571 format!(
4572 "expected `,` or `)` after sub signature parameter, got {:?}",
4573 self.peek()
4574 ),
4575 self.peek_line(),
4576 ));
4577 }
4578 }
4579 }
4580 Ok(params)
4581 }
4582
4583 fn parse_sub_sig_or_prototype_opt(&mut self) -> PerlResult<(Vec<SubSigParam>, Option<String>)> {
4585 if !matches!(self.peek(), Token::LParen) {
4586 return Ok((vec![], None));
4587 }
4588 self.advance();
4589 if matches!(self.peek(), Token::RParen) {
4590 self.advance();
4591 return Ok((vec![], Some(String::new())));
4592 }
4593 if self.sub_signature_list_starts_here() {
4594 let params = self.parse_sub_signature_param_list()?;
4595 self.expect(&Token::RParen)?;
4596 return Ok((params, None));
4597 }
4598 let proto = self.parse_legacy_sub_prototype_tail()?;
4599 Ok((vec![], Some(proto)))
4600 }
4601
4602 fn parse_sub_attributes(&mut self) -> PerlResult<()> {
4604 while self.eat(&Token::Colon) {
4605 match self.advance() {
4606 (Token::Ident(_), _) => {}
4607 (tok, line) => {
4608 return Err(self.syntax_err(
4609 format!("Expected attribute name after `:`, got {:?}", tok),
4610 line,
4611 ));
4612 }
4613 }
4614 if self.eat(&Token::LParen) {
4615 let mut depth = 1usize;
4616 while depth > 0 {
4617 match self.advance().0 {
4618 Token::LParen => depth += 1,
4619 Token::RParen => {
4620 depth -= 1;
4621 }
4622 Token::Eof => {
4623 return Err(self.syntax_err(
4624 "Unterminated sub attribute argument list",
4625 self.peek_line(),
4626 ));
4627 }
4628 _ => {}
4629 }
4630 }
4631 }
4632 }
4633 Ok(())
4634 }
4635
4636 fn parse_fn_eq_body_or_block(&mut self, is_sub_keyword: bool) -> PerlResult<Block> {
4639 if !is_sub_keyword && self.eat(&Token::Assign) {
4640 let expr = self.parse_assign_expr()?;
4641 if matches!(self.peek(), Token::Comma) {
4642 return Err(self.syntax_err(
4643 "`fn ... =` allows only a single expression; use `fn ... { ... }` for multiple statements",
4644 self.peek_line(),
4645 ));
4646 }
4647 let eline = expr.line;
4648 self.eat(&Token::Semicolon);
4649 let mut body = vec![Statement {
4650 label: None,
4651 kind: StmtKind::Expression(expr),
4652 line: eline,
4653 }];
4654 Self::default_topic_for_sole_bareword(&mut body);
4655 Ok(body)
4656 } else {
4657 self.parse_block()
4658 }
4659 }
4660
4661 fn parse_sub_decl(&mut self, is_sub_keyword: bool) -> PerlResult<Statement> {
4662 let line = self.peek_line();
4663 self.advance(); match self.peek().clone() {
4665 Token::Ident(_) => {
4666 let name = self.parse_package_qualified_identifier()?;
4667 let bare = name.rsplit("::").next().unwrap_or(&name);
4676 if Self::is_underscore_topic_slot(bare) {
4677 return Err(self.syntax_err(
4678 format!(
4679 "`fn {}` would shadow the topic-slot scalar; pick a different name",
4680 name
4681 ),
4682 line,
4683 ));
4684 }
4685 if Self::is_reserved_special_var_name(bare) {
4686 return Err(self.syntax_err(
4687 format!(
4688 "`fn {}` would shadow a Perl special variable / filehandle / compile-time token; pick a different name",
4689 name
4690 ),
4691 line,
4692 ));
4693 }
4694 let allow_shadow =
4701 crate::compat_mode() || (self.parsing_module && !crate::no_interop_mode());
4702 if !allow_shadow {
4703 self.check_udf_shadows_builtin(&name, line)?;
4704 }
4705 self.declared_subs.insert(name.clone());
4706 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
4707 self.parse_sub_attributes()?;
4708 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4709 Ok(Statement {
4710 label: None,
4711 kind: StmtKind::SubDecl {
4712 name,
4713 params,
4714 body,
4715 prototype,
4716 },
4717 line,
4718 })
4719 }
4720 Token::LParen | Token::LBrace | Token::Colon => {
4721 if is_sub_keyword && crate::no_interop_mode() {
4723 return Err(self.syntax_err(
4724 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
4725 line,
4726 ));
4727 }
4728 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
4730 self.parse_sub_attributes()?;
4731 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4732 Ok(Statement {
4733 label: None,
4734 kind: StmtKind::Expression(Expr {
4735 kind: ExprKind::CodeRef { params, body },
4736 line,
4737 }),
4738 line,
4739 })
4740 }
4741 tok => {
4742 let topic_name = match &tok {
4747 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n)
4748 if Self::is_underscore_topic_slot(n) =>
4749 {
4750 Some((
4751 match &tok {
4752 Token::ScalarVar(_) => '$',
4753 Token::ArrayVar(_) => '@',
4754 Token::HashVar(_) => '%',
4755 _ => unreachable!(),
4756 },
4757 n.clone(),
4758 ))
4759 }
4760 _ => None,
4761 };
4762 if let Some((sigil, n)) = topic_name {
4763 return Err(self.syntax_err(
4764 format!(
4765 "`fn {}{}` would shadow the topic-slot scalar; pick a different name",
4766 sigil, n
4767 ),
4768 self.peek_line(),
4769 ));
4770 }
4771 let special_var = match &tok {
4776 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n) => Some((
4777 match &tok {
4778 Token::ScalarVar(_) => '$',
4779 Token::ArrayVar(_) => '@',
4780 Token::HashVar(_) => '%',
4781 _ => unreachable!(),
4782 },
4783 n.clone(),
4784 )),
4785 _ => None,
4786 };
4787 if let Some((sigil, n)) = special_var {
4788 return Err(self.syntax_err(
4789 format!(
4790 "`fn {}{}` would shadow a Perl special variable / global; pick a different name",
4791 sigil, n
4792 ),
4793 self.peek_line(),
4794 ));
4795 }
4796 if matches!(tok, Token::Percent) {
4801 return Err(self.syntax_err(
4802 "`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 { ... }`",
4803 self.peek_line(),
4804 ));
4805 }
4806 Err(self.syntax_err(
4807 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
4808 self.peek_line(),
4809 ))
4810 }
4811 }
4812 }
4813
4814 fn parse_advice_decl(&mut self, kind: crate::ast::AdviceKind) -> PerlResult<Statement> {
4817 let line = self.peek_line();
4818 self.advance(); let pattern = match self.advance() {
4820 (Token::SingleString(s), _) | (Token::DoubleString(s), _) => s,
4821 (tok, err_line) => {
4822 return Err(self.syntax_err(
4823 format!(
4824 "Expected string-literal pattern after `{}`, got {:?}",
4825 match kind {
4826 crate::ast::AdviceKind::Before => "before",
4827 crate::ast::AdviceKind::After => "after",
4828 crate::ast::AdviceKind::Around => "around",
4829 },
4830 tok
4831 ),
4832 err_line,
4833 ));
4834 }
4835 };
4836 let body = self.parse_block()?;
4837 Ok(Statement {
4838 label: None,
4839 kind: StmtKind::AdviceDecl {
4840 kind,
4841 pattern,
4842 body,
4843 },
4844 line,
4845 })
4846 }
4847
4848 fn parse_struct_decl(&mut self) -> PerlResult<Statement> {
4850 let line = self.peek_line();
4851 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4853 self.syntax_err(
4854 format!("Expected struct name, got {:?}", self.peek()),
4855 self.peek_line(),
4856 )
4857 })?;
4858 self.expect(&Token::LBrace)?;
4859 let mut fields = Vec::new();
4860 let mut methods = Vec::new();
4861 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4862 let is_method = match self.peek() {
4864 Token::Ident(s) => s == "fn" || s == "sub",
4865 _ => false,
4866 };
4867 if is_method {
4868 self.advance(); let method_name = match self.advance() {
4870 (Token::Ident(n), _) => n,
4871 (tok, err_line) => {
4872 return Err(self
4873 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
4874 }
4875 };
4876 let params = if self.eat(&Token::LParen) {
4878 let p = self.parse_sub_signature_param_list()?;
4879 self.expect(&Token::RParen)?;
4880 p
4881 } else {
4882 Vec::new()
4883 };
4884 let body = self.parse_block()?;
4886 methods.push(crate::ast::StructMethod {
4887 name: method_name,
4888 params,
4889 body,
4890 });
4891 self.eat(&Token::Comma);
4893 self.eat(&Token::Semicolon);
4894 continue;
4895 }
4896
4897 let field_name = match self.advance() {
4898 (Token::Ident(n), _) => n,
4899 (tok, err_line) => {
4900 return Err(
4901 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
4902 )
4903 }
4904 };
4905 let ty = if self.eat(&Token::FatArrow) {
4907 self.parse_type_name()?
4908 } else {
4909 crate::ast::PerlTypeName::Any
4910 };
4911 let default = if self.eat(&Token::Assign) {
4912 Some(self.parse_ternary()?)
4914 } else {
4915 None
4916 };
4917 fields.push(StructField {
4918 name: field_name,
4919 ty,
4920 default,
4921 });
4922 if !self.eat(&Token::Comma) {
4923 self.eat(&Token::Semicolon);
4925 }
4926 }
4927 self.expect(&Token::RBrace)?;
4928 self.eat(&Token::Semicolon);
4929 Ok(Statement {
4930 label: None,
4931 kind: StmtKind::StructDecl {
4932 def: StructDef {
4933 name,
4934 fields,
4935 methods,
4936 },
4937 },
4938 line,
4939 })
4940 }
4941
4942 fn parse_enum_decl(&mut self) -> PerlResult<Statement> {
4944 let line = self.peek_line();
4945 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4947 self.syntax_err(
4948 format!("Expected enum name, got {:?}", self.peek()),
4949 self.peek_line(),
4950 )
4951 })?;
4952 self.expect(&Token::LBrace)?;
4953 let mut variants = Vec::new();
4954 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
4955 let variant_name = match self.advance() {
4956 (Token::Ident(n), _) => n,
4957 (tok, err_line) => {
4958 return Err(
4959 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
4960 )
4961 }
4962 };
4963 let ty = if self.eat(&Token::FatArrow) {
4964 Some(self.parse_type_name()?)
4965 } else {
4966 None
4967 };
4968 variants.push(EnumVariant {
4969 name: variant_name,
4970 ty,
4971 });
4972 if !self.eat(&Token::Comma) {
4973 self.eat(&Token::Semicolon);
4974 }
4975 }
4976 self.expect(&Token::RBrace)?;
4977 self.eat(&Token::Semicolon);
4978 Ok(Statement {
4979 label: None,
4980 kind: StmtKind::EnumDecl {
4981 def: EnumDef { name, variants },
4982 },
4983 line,
4984 })
4985 }
4986
4987 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> PerlResult<Statement> {
4989 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
4990 let line = self.peek_line();
4991 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
4993 self.syntax_err(
4994 format!("Expected class name, got {:?}", self.peek()),
4995 self.peek_line(),
4996 )
4997 })?;
4998
4999 let mut extends = Vec::new();
5001 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
5002 self.advance(); loop {
5004 let parent = self.parse_package_qualified_identifier().map_err(|_| {
5005 self.syntax_err(
5006 format!(
5007 "Expected parent class name after `extends`, got {:?}",
5008 self.peek()
5009 ),
5010 self.peek_line(),
5011 )
5012 })?;
5013 extends.push(parent);
5014 if !self.eat(&Token::Comma) {
5015 break;
5016 }
5017 }
5018 }
5019
5020 let mut implements = Vec::new();
5022 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
5023 self.advance(); loop {
5025 let trait_name = self.parse_package_qualified_identifier().map_err(|_| {
5026 self.syntax_err(
5027 format!("Expected trait name after `impl`, got {:?}", self.peek()),
5028 self.peek_line(),
5029 )
5030 })?;
5031 implements.push(trait_name);
5032 if !self.eat(&Token::Comma) {
5033 break;
5034 }
5035 }
5036 }
5037
5038 self.expect(&Token::LBrace)?;
5039 let mut fields = Vec::new();
5040 let mut methods = Vec::new();
5041 let mut static_fields = Vec::new();
5042
5043 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5044 let visibility = match self.peek() {
5046 Token::Ident(ref s) if s == "pub" => {
5047 self.advance();
5048 Visibility::Public
5049 }
5050 Token::Ident(ref s) if s == "priv" => {
5051 self.advance();
5052 Visibility::Private
5053 }
5054 Token::Ident(ref s) if s == "prot" => {
5055 self.advance();
5056 Visibility::Protected
5057 }
5058 _ => Visibility::Public, };
5060
5061 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
5063 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5067 return Err(self.syntax_err(
5069 "use `fn Self.name` for static methods, not `static fn`",
5070 self.peek_line(),
5071 ));
5072 }
5073
5074 let field_name = match self.advance() {
5075 (Token::Ident(n), _) => n,
5076 (tok, err_line) => {
5077 return Err(self.syntax_err(
5078 format!("Expected static field name, got {:?}", tok),
5079 err_line,
5080 ))
5081 }
5082 };
5083
5084 let ty = if self.eat(&Token::Colon) {
5085 self.parse_type_name()?
5086 } else {
5087 crate::ast::PerlTypeName::Any
5088 };
5089
5090 let default = if self.eat(&Token::Assign) {
5091 Some(self.parse_ternary()?)
5092 } else {
5093 None
5094 };
5095
5096 static_fields.push(ClassStaticField {
5097 name: field_name,
5098 ty,
5099 visibility,
5100 default,
5101 });
5102
5103 if !self.eat(&Token::Comma) {
5104 self.eat(&Token::Semicolon);
5105 }
5106 continue;
5107 }
5108
5109 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
5111 if method_is_final {
5112 self.advance(); }
5114
5115 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
5117 if is_method {
5118 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
5122 if is_static {
5123 self.advance(); self.expect(&Token::Dot)?;
5125 }
5126
5127 let method_name = match self.advance() {
5128 (Token::Ident(n), _) => n,
5129 (tok, err_line) => {
5130 return Err(self
5131 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5132 }
5133 };
5134
5135 let params = if self.eat(&Token::LParen) {
5137 let p = self.parse_sub_signature_param_list()?;
5138 self.expect(&Token::RParen)?;
5139 p
5140 } else {
5141 Vec::new()
5142 };
5143
5144 let body = if matches!(self.peek(), Token::LBrace) {
5146 Some(self.parse_block()?)
5147 } else {
5148 None
5149 };
5150
5151 methods.push(ClassMethod {
5152 name: method_name,
5153 params,
5154 body,
5155 visibility,
5156 is_static,
5157 is_final: method_is_final,
5158 });
5159 self.eat(&Token::Comma);
5160 self.eat(&Token::Semicolon);
5161 continue;
5162 } else if method_is_final {
5163 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
5164 }
5165
5166 let field_name = match self.advance() {
5168 (Token::Ident(n), _) => n,
5169 (tok, err_line) => {
5170 return Err(
5171 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5172 )
5173 }
5174 };
5175
5176 let ty = if self.eat(&Token::Colon) {
5178 self.parse_type_name()?
5179 } else {
5180 crate::ast::PerlTypeName::Any
5181 };
5182
5183 let default = if self.eat(&Token::Assign) {
5185 Some(self.parse_ternary()?)
5186 } else {
5187 None
5188 };
5189
5190 fields.push(ClassField {
5191 name: field_name,
5192 ty,
5193 visibility,
5194 default,
5195 });
5196
5197 if !self.eat(&Token::Comma) {
5198 self.eat(&Token::Semicolon);
5199 }
5200 }
5201
5202 self.expect(&Token::RBrace)?;
5203 self.eat(&Token::Semicolon);
5204
5205 Ok(Statement {
5206 label: None,
5207 kind: StmtKind::ClassDecl {
5208 def: ClassDef {
5209 name,
5210 is_abstract,
5211 is_final,
5212 extends,
5213 implements,
5214 fields,
5215 methods,
5216 static_fields,
5217 },
5218 },
5219 line,
5220 })
5221 }
5222
5223 fn parse_trait_decl(&mut self) -> PerlResult<Statement> {
5225 use crate::ast::{ClassMethod, TraitDef, Visibility};
5226 let line = self.peek_line();
5227 self.advance(); let name = self.parse_package_qualified_identifier().map_err(|_| {
5229 self.syntax_err(
5230 format!("Expected trait name, got {:?}", self.peek()),
5231 self.peek_line(),
5232 )
5233 })?;
5234
5235 self.expect(&Token::LBrace)?;
5236 let mut methods = Vec::new();
5237
5238 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5239 let visibility = match self.peek() {
5241 Token::Ident(ref s) if s == "pub" => {
5242 self.advance();
5243 Visibility::Public
5244 }
5245 Token::Ident(ref s) if s == "priv" => {
5246 self.advance();
5247 Visibility::Private
5248 }
5249 Token::Ident(ref s) if s == "prot" => {
5250 self.advance();
5251 Visibility::Protected
5252 }
5253 _ => Visibility::Public,
5254 };
5255
5256 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5258 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
5259 }
5260 self.advance(); let method_name = match self.advance() {
5263 (Token::Ident(n), _) => n,
5264 (tok, err_line) => {
5265 return Err(
5266 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
5267 )
5268 }
5269 };
5270
5271 let params = if self.eat(&Token::LParen) {
5273 let p = self.parse_sub_signature_param_list()?;
5274 self.expect(&Token::RParen)?;
5275 p
5276 } else {
5277 Vec::new()
5278 };
5279
5280 let body = if matches!(self.peek(), Token::LBrace) {
5282 Some(self.parse_block()?)
5283 } else {
5284 None
5285 };
5286
5287 methods.push(ClassMethod {
5288 name: method_name,
5289 params,
5290 body,
5291 visibility,
5292 is_static: false,
5293 is_final: false,
5294 });
5295
5296 self.eat(&Token::Comma);
5297 self.eat(&Token::Semicolon);
5298 }
5299
5300 self.expect(&Token::RBrace)?;
5301 self.eat(&Token::Semicolon);
5302
5303 Ok(Statement {
5304 label: None,
5305 kind: StmtKind::TraitDecl {
5306 def: TraitDef { name, methods },
5307 },
5308 line,
5309 })
5310 }
5311
5312 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
5313 match &target.kind {
5314 ExprKind::ScalarVar(name) => Some(VarDecl {
5315 sigil: Sigil::Scalar,
5316 name: name.clone(),
5317 initializer: None,
5318 frozen: false,
5319 type_annotation: None,
5320 }),
5321 ExprKind::ArrayVar(name) => Some(VarDecl {
5322 sigil: Sigil::Array,
5323 name: name.clone(),
5324 initializer: None,
5325 frozen: false,
5326 type_annotation: None,
5327 }),
5328 ExprKind::HashVar(name) => Some(VarDecl {
5329 sigil: Sigil::Hash,
5330 name: name.clone(),
5331 initializer: None,
5332 frozen: false,
5333 type_annotation: None,
5334 }),
5335 ExprKind::Typeglob(name) => Some(VarDecl {
5336 sigil: Sigil::Typeglob,
5337 name: name.clone(),
5338 initializer: None,
5339 frozen: false,
5340 type_annotation: None,
5341 }),
5342 _ => None,
5343 }
5344 }
5345
5346 fn parse_decl_array_destructure(
5347 &mut self,
5348 keyword: &str,
5349 line: usize,
5350 ) -> PerlResult<Statement> {
5351 self.expect(&Token::LBracket)?;
5352 let elems = self.parse_match_array_elems_until_rbracket()?;
5353 self.expect(&Token::Assign)?;
5354 self.suppress_scalar_hash_brace += 1;
5355 let rhs = self.parse_expression()?;
5356 self.suppress_scalar_hash_brace -= 1;
5357 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
5358 self.parse_stmt_postfix_modifier(stmt)
5359 }
5360
5361 fn parse_decl_hash_destructure(&mut self, keyword: &str, line: usize) -> PerlResult<Statement> {
5362 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
5363 unreachable!("parse_match_hash_pattern returns Hash");
5364 };
5365 self.expect(&Token::Assign)?;
5366 self.suppress_scalar_hash_brace += 1;
5367 let rhs = self.parse_expression()?;
5368 self.suppress_scalar_hash_brace -= 1;
5369 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
5370 self.parse_stmt_postfix_modifier(stmt)
5371 }
5372
5373 fn desugar_array_destructure(
5374 &mut self,
5375 keyword: &str,
5376 line: usize,
5377 elems: Vec<MatchArrayElem>,
5378 rhs: Expr,
5379 ) -> PerlResult<Statement> {
5380 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5381 let mut stmts: Vec<Statement> = Vec::new();
5382 stmts.push(destructure_stmt_from_var_decls(
5383 keyword,
5384 vec![VarDecl {
5385 sigil: Sigil::Scalar,
5386 name: tmp.clone(),
5387 initializer: Some(rhs),
5388 frozen: false,
5389 type_annotation: None,
5390 }],
5391 line,
5392 ));
5393
5394 let has_rest = elems
5395 .iter()
5396 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
5397 let fixed_slots = elems
5398 .iter()
5399 .filter(|e| {
5400 matches!(
5401 e,
5402 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
5403 )
5404 })
5405 .count();
5406 if !has_rest {
5407 let cond = Expr {
5408 kind: ExprKind::BinOp {
5409 left: Box::new(destructure_expr_array_len(&tmp, line)),
5410 op: BinOp::NumEq,
5411 right: Box::new(Expr {
5412 kind: ExprKind::Integer(fixed_slots as i64),
5413 line,
5414 }),
5415 },
5416 line,
5417 };
5418 stmts.push(destructure_stmt_unless_die(
5419 line,
5420 cond,
5421 "array destructure: length mismatch",
5422 ));
5423 }
5424
5425 let mut idx: i64 = 0;
5426 for elem in elems {
5427 match elem {
5428 MatchArrayElem::Rest => break,
5429 MatchArrayElem::RestBind(name) => {
5430 let list_source = Expr {
5431 kind: ExprKind::Deref {
5432 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5433 kind: Sigil::Array,
5434 },
5435 line,
5436 };
5437 let last_ix = Expr {
5438 kind: ExprKind::BinOp {
5439 left: Box::new(destructure_expr_array_len(&tmp, line)),
5440 op: BinOp::Sub,
5441 right: Box::new(Expr {
5442 kind: ExprKind::Integer(1),
5443 line,
5444 }),
5445 },
5446 line,
5447 };
5448 let range = Expr {
5449 kind: ExprKind::Range {
5450 from: Box::new(Expr {
5451 kind: ExprKind::Integer(idx),
5452 line,
5453 }),
5454 to: Box::new(last_ix),
5455 exclusive: false,
5456 step: None,
5457 },
5458 line,
5459 };
5460 let slice = Expr {
5461 kind: ExprKind::AnonymousListSlice {
5462 source: Box::new(list_source),
5463 indices: vec![range],
5464 },
5465 line,
5466 };
5467 stmts.push(destructure_stmt_from_var_decls(
5468 keyword,
5469 vec![VarDecl {
5470 sigil: Sigil::Array,
5471 name,
5472 initializer: Some(slice),
5473 frozen: false,
5474 type_annotation: None,
5475 }],
5476 line,
5477 ));
5478 break;
5479 }
5480 MatchArrayElem::CaptureScalar(name) => {
5481 let arrow = Expr {
5482 kind: ExprKind::ArrowDeref {
5483 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5484 index: Box::new(Expr {
5485 kind: ExprKind::Integer(idx),
5486 line,
5487 }),
5488 kind: DerefKind::Array,
5489 },
5490 line,
5491 };
5492 stmts.push(destructure_stmt_from_var_decls(
5493 keyword,
5494 vec![VarDecl {
5495 sigil: Sigil::Scalar,
5496 name,
5497 initializer: Some(arrow),
5498 frozen: false,
5499 type_annotation: None,
5500 }],
5501 line,
5502 ));
5503 idx += 1;
5504 }
5505 MatchArrayElem::Expr(e) => {
5506 let elem_subj = Expr {
5507 kind: ExprKind::ArrowDeref {
5508 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5509 index: Box::new(Expr {
5510 kind: ExprKind::Integer(idx),
5511 line,
5512 }),
5513 kind: DerefKind::Array,
5514 },
5515 line,
5516 };
5517 let match_expr = Expr {
5518 kind: ExprKind::AlgebraicMatch {
5519 subject: Box::new(elem_subj),
5520 arms: vec![
5521 MatchArm {
5522 pattern: MatchPattern::Value(Box::new(e.clone())),
5523 guard: None,
5524 body: Expr {
5525 kind: ExprKind::Integer(0),
5526 line,
5527 },
5528 },
5529 MatchArm {
5530 pattern: MatchPattern::Any,
5531 guard: None,
5532 body: Expr {
5533 kind: ExprKind::Die(vec![Expr {
5534 kind: ExprKind::String(
5535 "array destructure: element pattern mismatch"
5536 .to_string(),
5537 ),
5538 line,
5539 }]),
5540 line,
5541 },
5542 },
5543 ],
5544 },
5545 line,
5546 };
5547 stmts.push(Statement {
5548 label: None,
5549 kind: StmtKind::Expression(match_expr),
5550 line,
5551 });
5552 idx += 1;
5553 }
5554 }
5555 }
5556
5557 Ok(Statement {
5558 label: None,
5559 kind: StmtKind::StmtGroup(stmts),
5560 line,
5561 })
5562 }
5563
5564 fn desugar_hash_destructure(
5565 &mut self,
5566 keyword: &str,
5567 line: usize,
5568 pairs: Vec<MatchHashPair>,
5569 rhs: Expr,
5570 ) -> PerlResult<Statement> {
5571 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5572 let mut stmts: Vec<Statement> = Vec::new();
5573 stmts.push(destructure_stmt_from_var_decls(
5574 keyword,
5575 vec![VarDecl {
5576 sigil: Sigil::Scalar,
5577 name: tmp.clone(),
5578 initializer: Some(rhs),
5579 frozen: false,
5580 type_annotation: None,
5581 }],
5582 line,
5583 ));
5584
5585 for pair in pairs {
5586 match pair {
5587 MatchHashPair::KeyOnly { key } => {
5588 let exists_op = Expr {
5589 kind: ExprKind::Exists(Box::new(Expr {
5590 kind: ExprKind::ArrowDeref {
5591 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5592 index: Box::new(key),
5593 kind: DerefKind::Hash,
5594 },
5595 line,
5596 })),
5597 line,
5598 };
5599 stmts.push(destructure_stmt_unless_die(
5600 line,
5601 exists_op,
5602 "hash destructure: missing required key",
5603 ));
5604 }
5605 MatchHashPair::Capture { key, name } => {
5606 let init = Expr {
5607 kind: ExprKind::ArrowDeref {
5608 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5609 index: Box::new(key),
5610 kind: DerefKind::Hash,
5611 },
5612 line,
5613 };
5614 stmts.push(destructure_stmt_from_var_decls(
5615 keyword,
5616 vec![VarDecl {
5617 sigil: Sigil::Scalar,
5618 name,
5619 initializer: Some(init),
5620 frozen: false,
5621 type_annotation: None,
5622 }],
5623 line,
5624 ));
5625 }
5626 }
5627 }
5628
5629 Ok(Statement {
5630 label: None,
5631 kind: StmtKind::StmtGroup(stmts),
5632 line,
5633 })
5634 }
5635
5636 fn parse_my_our_local(
5637 &mut self,
5638 keyword: &str,
5639 allow_type_annotation: bool,
5640 ) -> PerlResult<Statement> {
5641 let line = self.peek_line();
5642 self.advance(); if keyword == "local"
5645 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
5646 {
5647 let target = self.parse_postfix()?;
5648 let mut initializer: Option<Expr> = None;
5649 if self.eat(&Token::Assign) {
5650 initializer = Some(self.parse_expression()?);
5651 } else if matches!(
5652 self.peek(),
5653 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
5654 ) {
5655 if matches!(&target.kind, ExprKind::Typeglob(_)) {
5656 return Err(self.syntax_err(
5657 "compound assignment on typeglob declaration is not supported",
5658 self.peek_line(),
5659 ));
5660 }
5661 let op = match self.peek().clone() {
5662 Token::OrAssign => BinOp::LogOr,
5663 Token::DefinedOrAssign => BinOp::DefinedOr,
5664 Token::AndAssign => BinOp::LogAnd,
5665 _ => unreachable!(),
5666 };
5667 self.advance();
5668 let rhs = self.parse_assign_expr()?;
5669 let tgt_line = target.line;
5670 initializer = Some(Expr {
5671 kind: ExprKind::CompoundAssign {
5672 target: Box::new(target.clone()),
5673 op,
5674 value: Box::new(rhs),
5675 },
5676 line: tgt_line,
5677 });
5678 }
5679
5680 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
5681 decl.initializer = initializer;
5682 StmtKind::Local(vec![decl])
5683 } else {
5684 StmtKind::LocalExpr {
5685 target,
5686 initializer,
5687 }
5688 };
5689 let stmt = Statement {
5690 label: None,
5691 kind,
5692 line,
5693 };
5694 return self.parse_stmt_postfix_modifier(stmt);
5695 }
5696
5697 if matches!(self.peek(), Token::LBracket) {
5698 return self.parse_decl_array_destructure(keyword, line);
5699 }
5700 if matches!(self.peek(), Token::LBrace) {
5701 return self.parse_decl_hash_destructure(keyword, line);
5702 }
5703
5704 let mut decls = Vec::new();
5705
5706 if self.eat(&Token::LParen) {
5707 while !matches!(self.peek(), Token::RParen | Token::Eof) {
5709 let decl = self.parse_var_decl(allow_type_annotation)?;
5710 decls.push(decl);
5711 if !self.eat(&Token::Comma) {
5712 break;
5713 }
5714 }
5715 self.expect(&Token::RParen)?;
5716 } else {
5717 decls.push(self.parse_var_decl(allow_type_annotation)?);
5718 }
5719
5720 if self.eat(&Token::Assign) {
5722 if keyword == "our" && decls.len() == 1 {
5723 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
5724 self.advance();
5725 decls.push(self.parse_var_decl(allow_type_annotation)?);
5726 if !self.eat(&Token::Assign) {
5727 return Err(self.syntax_err(
5728 "expected `=` after `our` in chained our-declaration",
5729 self.peek_line(),
5730 ));
5731 }
5732 }
5733 }
5734 let rhs_start_pos = self.pos;
5735 let mut val = self.parse_expression()?;
5736 if !crate::compat_mode()
5745 && self.block_depth == 0
5746 && decls.len() == 1
5747 && matches!(decls[0].sigil, Sigil::Scalar)
5748 && !matches!(
5749 val.kind,
5750 ExprKind::CodeRef { .. }
5751 | ExprKind::SubroutineRef(_)
5752 | ExprKind::SubroutineCodeRef(_)
5753 | ExprKind::DynamicSubCodeRef(_)
5754 )
5755 {
5756 let rhs_end_pos = self.pos;
5757 let rhs_has_bare_positional = self.bare_positional_indices.contains(&rhs_start_pos)
5764 && rhs_start_pos < rhs_end_pos;
5765 if rhs_has_bare_positional {
5766 let val_line = val.line;
5767 val = Expr {
5768 kind: ExprKind::CodeRef {
5769 params: Vec::new(),
5770 body: vec![Statement {
5771 label: None,
5772 kind: StmtKind::Expression(val),
5773 line: val_line,
5774 }],
5775 },
5776 line: val_line,
5777 };
5778 }
5779 }
5780 if !crate::compat_mode() && decls.len() == 1 {
5783 let decl = &decls[0];
5784 let target_kind = match decl.sigil {
5785 Sigil::Scalar => ExprKind::ScalarVar(decl.name.clone()),
5786 Sigil::Array => ExprKind::ArrayVar(decl.name.clone()),
5787 Sigil::Hash => ExprKind::HashVar(decl.name.clone()),
5788 Sigil::Typeglob => {
5789 if decls.len() == 1 {
5791 decls[0].initializer = Some(val);
5792 } else {
5793 for d in &mut decls {
5794 d.initializer = Some(val.clone());
5795 }
5796 }
5797 return Ok(Statement {
5798 label: None,
5799 kind: match keyword {
5800 "my" => StmtKind::My(decls),
5801 "mysync" => StmtKind::MySync(decls),
5802 "our" => StmtKind::Our(decls),
5803 "oursync" => StmtKind::OurSync(decls),
5804 "local" => StmtKind::Local(decls),
5805 "state" => StmtKind::State(decls),
5806 _ => unreachable!(),
5807 },
5808 line,
5809 });
5810 }
5811 };
5812 let target = Expr {
5813 kind: target_kind,
5814 line,
5815 };
5816 self.validate_assignment(&target, &val, line)?;
5817 }
5818 if decls.len() == 1 {
5819 decls[0].initializer = Some(val);
5820 } else {
5821 for decl in &mut decls {
5822 decl.initializer = Some(val.clone());
5823 }
5824 }
5825 } else if decls.len() == 1 {
5826 let op = match self.peek().clone() {
5828 Token::OrAssign => Some(BinOp::LogOr),
5829 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
5830 Token::AndAssign => Some(BinOp::LogAnd),
5831 _ => None,
5832 };
5833 if let Some(op) = op {
5834 let d = &decls[0];
5835 if matches!(d.sigil, Sigil::Typeglob) {
5836 return Err(self.syntax_err(
5837 "compound assignment on typeglob declaration is not supported",
5838 self.peek_line(),
5839 ));
5840 }
5841 self.advance();
5842 let rhs = self.parse_assign_expr()?;
5843 let target = Expr {
5844 kind: match d.sigil {
5845 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
5846 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
5847 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
5848 Sigil::Typeglob => unreachable!(),
5849 },
5850 line,
5851 };
5852 decls[0].initializer = Some(Expr {
5853 kind: ExprKind::CompoundAssign {
5854 target: Box::new(target),
5855 op,
5856 value: Box::new(rhs),
5857 },
5858 line,
5859 });
5860 }
5861 }
5862
5863 let kind = match keyword {
5864 "my" => StmtKind::My(decls),
5865 "mysync" => StmtKind::MySync(decls),
5866 "our" => StmtKind::Our(decls),
5867 "oursync" => StmtKind::OurSync(decls),
5868 "local" => StmtKind::Local(decls),
5869 "state" => StmtKind::State(decls),
5870 _ => unreachable!(),
5871 };
5872 let stmt = Statement {
5873 label: None,
5874 kind,
5875 line,
5876 };
5877 self.parse_stmt_postfix_modifier(stmt)
5879 }
5880
5881 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> PerlResult<VarDecl> {
5882 let mut decl = match self.advance() {
5883 (Token::ScalarVar(name), _) => VarDecl {
5884 sigil: Sigil::Scalar,
5885 name,
5886 initializer: None,
5887 frozen: false,
5888 type_annotation: None,
5889 },
5890 (Token::ArrayVar(name), _) => VarDecl {
5891 sigil: Sigil::Array,
5892 name,
5893 initializer: None,
5894 frozen: false,
5895 type_annotation: None,
5896 },
5897 (Token::HashVar(name), line) => {
5898 if !crate::compat_mode() {
5899 self.check_hash_shadows_reserved(&name, line)?;
5900 }
5901 VarDecl {
5902 sigil: Sigil::Hash,
5903 name,
5904 initializer: None,
5905 frozen: false,
5906 type_annotation: None,
5907 }
5908 }
5909 (Token::Star, _line) => {
5910 let name = match self.advance() {
5911 (Token::Ident(n), _) => n,
5912 (tok, l) => {
5913 return Err(self
5914 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
5915 }
5916 };
5917 VarDecl {
5918 sigil: Sigil::Typeglob,
5919 name,
5920 initializer: None,
5921 frozen: false,
5922 type_annotation: None,
5923 }
5924 }
5925 (Token::Ident(ref kw), _) if kw == "undef" => VarDecl {
5930 sigil: Sigil::Scalar,
5931 name: format!("__undef_sink_{}", self.pos),
5935 initializer: None,
5936 frozen: false,
5937 type_annotation: None,
5938 },
5939 (tok, line) => {
5940 return Err(self.syntax_err(
5941 format!("Expected variable in declaration, got {:?}", tok),
5942 line,
5943 ));
5944 }
5945 };
5946 if allow_type_annotation && self.eat(&Token::Colon) {
5947 let ty = self.parse_type_name()?;
5948 if decl.sigil != Sigil::Scalar {
5949 return Err(self.syntax_err(
5950 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
5951 self.peek_line(),
5952 ));
5953 }
5954 decl.type_annotation = Some(ty);
5955 }
5956 Ok(decl)
5957 }
5958
5959 fn parse_type_name(&mut self) -> PerlResult<PerlTypeName> {
5960 match self.advance() {
5961 (Token::Ident(name), _) => match name.as_str() {
5962 "Int" => Ok(PerlTypeName::Int),
5963 "Str" => Ok(PerlTypeName::Str),
5964 "Float" => Ok(PerlTypeName::Float),
5965 "Bool" => Ok(PerlTypeName::Bool),
5966 "Array" => Ok(PerlTypeName::Array),
5967 "Hash" => Ok(PerlTypeName::Hash),
5968 "Ref" => Ok(PerlTypeName::Ref),
5969 "Any" => Ok(PerlTypeName::Any),
5970 _ => Ok(PerlTypeName::Struct(name)),
5971 },
5972 (tok, err_line) => Err(self.syntax_err(
5973 format!("Expected type name after `:`, got {:?}", tok),
5974 err_line,
5975 )),
5976 }
5977 }
5978
5979 fn parse_package(&mut self) -> PerlResult<Statement> {
5980 let line = self.peek_line();
5981 self.advance(); let name = match self.advance() {
5983 (Token::Ident(n), _) => n,
5984 (tok, line) => {
5985 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
5986 }
5987 };
5988 let mut full_name = name;
5990 while self.eat(&Token::PackageSep) {
5991 if let (Token::Ident(part), _) = self.advance() {
5992 full_name = format!("{}::{}", full_name, part);
5993 }
5994 }
5995 self.eat(&Token::Semicolon);
5996 Ok(Statement {
5997 label: None,
5998 kind: StmtKind::Package { name: full_name },
5999 line,
6000 })
6001 }
6002
6003 fn parse_use(&mut self) -> PerlResult<Statement> {
6004 let line = self.peek_line();
6005 self.advance(); let (tok, tok_line) = self.advance();
6007 match tok {
6008 Token::Float(v) => {
6009 self.eat(&Token::Semicolon);
6010 Ok(Statement {
6011 label: None,
6012 kind: StmtKind::UsePerlVersion { version: v },
6013 line,
6014 })
6015 }
6016 Token::Integer(n) => {
6017 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6018 self.eat(&Token::Semicolon);
6019 Ok(Statement {
6020 label: None,
6021 kind: StmtKind::UsePerlVersion { version: n as f64 },
6022 line,
6023 })
6024 } else {
6025 Err(self.syntax_err(
6026 format!("Expected ';' after use VERSION (got {:?})", self.peek()),
6027 line,
6028 ))
6029 }
6030 }
6031 Token::Ident(n) => {
6032 let mut full_name = n;
6033 while self.eat(&Token::PackageSep) {
6034 if let (Token::Ident(part), _) = self.advance() {
6035 full_name = format!("{}::{}", full_name, part);
6036 }
6037 }
6038 if full_name == "overload" {
6039 let mut pairs = Vec::new();
6040 let mut parse_overload_pairs = |this: &mut Self| -> PerlResult<()> {
6041 loop {
6042 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
6043 {
6044 break;
6045 }
6046 let key_e = this.parse_assign_expr()?;
6047 this.expect(&Token::FatArrow)?;
6048 let val_e = this.parse_assign_expr()?;
6049 let key = this.expr_to_overload_key(&key_e)?;
6050 let val = this.expr_to_overload_sub(&val_e)?;
6051 pairs.push((key, val));
6052 if !this.eat(&Token::Comma) {
6053 break;
6054 }
6055 }
6056 Ok(())
6057 };
6058 if self.eat(&Token::LParen) {
6059 parse_overload_pairs(self)?;
6061 self.expect(&Token::RParen)?;
6062 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
6063 parse_overload_pairs(self)?;
6064 }
6065 self.eat(&Token::Semicolon);
6066 return Ok(Statement {
6067 label: None,
6068 kind: StmtKind::UseOverload { pairs },
6069 line,
6070 });
6071 }
6072 let mut imports = Vec::new();
6073 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
6074 && !self.next_is_new_statement_start(tok_line)
6075 {
6076 loop {
6077 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6078 break;
6079 }
6080 imports.push(self.parse_expression()?);
6081 if !self.eat(&Token::Comma) {
6082 break;
6083 }
6084 }
6085 }
6086 self.eat(&Token::Semicolon);
6087 Ok(Statement {
6088 label: None,
6089 kind: StmtKind::Use {
6090 module: full_name,
6091 imports,
6092 },
6093 line,
6094 })
6095 }
6096 other => Err(self.syntax_err(
6097 format!("Expected module name or version after use, got {:?}", other),
6098 tok_line,
6099 )),
6100 }
6101 }
6102
6103 fn parse_no(&mut self) -> PerlResult<Statement> {
6104 let line = self.peek_line();
6105 self.advance(); let module = match self.advance() {
6107 (Token::Ident(n), tok_line) => (n, tok_line),
6108 (tok, line) => {
6109 return Err(self.syntax_err(
6110 format!("Expected module name after no, got {:?}", tok),
6111 line,
6112 ))
6113 }
6114 };
6115 let (module_name, tok_line) = module;
6116 let mut full_name = module_name;
6117 while self.eat(&Token::PackageSep) {
6118 if let (Token::Ident(part), _) = self.advance() {
6119 full_name = format!("{}::{}", full_name, part);
6120 }
6121 }
6122 let mut imports = Vec::new();
6123 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
6124 && !self.next_is_new_statement_start(tok_line)
6125 {
6126 loop {
6127 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6128 break;
6129 }
6130 imports.push(self.parse_expression()?);
6131 if !self.eat(&Token::Comma) {
6132 break;
6133 }
6134 }
6135 }
6136 self.eat(&Token::Semicolon);
6137 Ok(Statement {
6138 label: None,
6139 kind: StmtKind::No {
6140 module: full_name,
6141 imports,
6142 },
6143 line,
6144 })
6145 }
6146
6147 fn parse_return(&mut self) -> PerlResult<Statement> {
6148 let line = self.peek_line();
6149 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6156 || self.peek_is_postfix_stmt_modifier_keyword()
6157 {
6158 None
6159 } else {
6160 let first = self.parse_assign_expr()?;
6165 if matches!(self.peek(), Token::Comma | Token::FatArrow) {
6166 let mut items = vec![first];
6167 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6168 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6169 || self.peek_is_postfix_stmt_modifier_keyword()
6170 {
6171 break;
6172 }
6173 items.push(self.parse_assign_expr()?);
6174 }
6175 let line = items.first().map(|e| e.line).unwrap_or(line);
6176 Some(Expr {
6177 kind: ExprKind::List(items),
6178 line,
6179 })
6180 } else {
6181 Some(first)
6182 }
6183 };
6184 let stmt = Statement {
6186 label: None,
6187 kind: StmtKind::Return(val),
6188 line,
6189 };
6190 if let Token::Ident(ref kw) = self.peek().clone() {
6191 match kw.as_str() {
6192 "if" => {
6193 self.advance();
6194 let cond = self.parse_expression()?;
6195 self.eat(&Token::Semicolon);
6196 return Ok(Statement {
6197 label: None,
6198 kind: StmtKind::If {
6199 condition: cond,
6200 body: vec![stmt],
6201 elsifs: vec![],
6202 else_block: None,
6203 },
6204 line,
6205 });
6206 }
6207 "unless" => {
6208 self.advance();
6209 let cond = self.parse_expression()?;
6210 self.eat(&Token::Semicolon);
6211 return Ok(Statement {
6212 label: None,
6213 kind: StmtKind::Unless {
6214 condition: cond,
6215 body: vec![stmt],
6216 else_block: None,
6217 },
6218 line,
6219 });
6220 }
6221 _ => {}
6222 }
6223 }
6224 self.eat(&Token::Semicolon);
6225 Ok(stmt)
6226 }
6227
6228 fn parse_expression(&mut self) -> PerlResult<Expr> {
6231 self.parse_comma_expr()
6232 }
6233
6234 fn parse_comma_expr(&mut self) -> PerlResult<Expr> {
6235 let expr = self.parse_or_word()?;
6243 let mut exprs = vec![expr];
6244 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6245 if matches!(
6246 self.peek(),
6247 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
6248 ) {
6249 break; }
6251 exprs.push(self.parse_or_word()?);
6252 }
6253 if exprs.len() == 1 {
6254 return Ok(exprs.pop().unwrap());
6255 }
6256 let line = exprs[0].line;
6257 Ok(Expr {
6258 kind: ExprKind::List(exprs),
6259 line,
6260 })
6261 }
6262
6263 fn parse_assign_expr(&mut self) -> PerlResult<Expr> {
6264 let expr = self.parse_ternary()?;
6265 let line = expr.line;
6266
6267 match self.peek().clone() {
6268 Token::Assign => {
6269 self.advance();
6270 let right = self.parse_assign_expr()?;
6271 if let ExprKind::MethodCall { ref args, .. } = expr.kind {
6273 if args.is_empty() {
6274 let ExprKind::MethodCall {
6276 object,
6277 method,
6278 super_call,
6279 ..
6280 } = expr.kind
6281 else {
6282 unreachable!()
6283 };
6284 return Ok(Expr {
6285 kind: ExprKind::MethodCall {
6286 object,
6287 method,
6288 args: vec![right],
6289 super_call,
6290 },
6291 line,
6292 });
6293 }
6294 }
6295 self.validate_assignment(&expr, &right, line)?;
6296 Ok(Expr {
6297 kind: ExprKind::Assign {
6298 target: Box::new(expr),
6299 value: Box::new(right),
6300 },
6301 line,
6302 })
6303 }
6304 Token::PlusAssign => {
6305 self.advance();
6306 let r = self.parse_assign_expr()?;
6307 Ok(Expr {
6308 kind: ExprKind::CompoundAssign {
6309 target: Box::new(expr),
6310 op: BinOp::Add,
6311 value: Box::new(r),
6312 },
6313 line,
6314 })
6315 }
6316 Token::MinusAssign => {
6317 self.advance();
6318 let r = self.parse_assign_expr()?;
6319 Ok(Expr {
6320 kind: ExprKind::CompoundAssign {
6321 target: Box::new(expr),
6322 op: BinOp::Sub,
6323 value: Box::new(r),
6324 },
6325 line,
6326 })
6327 }
6328 Token::MulAssign => {
6329 self.advance();
6330 let r = self.parse_assign_expr()?;
6331 Ok(Expr {
6332 kind: ExprKind::CompoundAssign {
6333 target: Box::new(expr),
6334 op: BinOp::Mul,
6335 value: Box::new(r),
6336 },
6337 line,
6338 })
6339 }
6340 Token::DivAssign => {
6341 self.advance();
6342 let r = self.parse_assign_expr()?;
6343 Ok(Expr {
6344 kind: ExprKind::CompoundAssign {
6345 target: Box::new(expr),
6346 op: BinOp::Div,
6347 value: Box::new(r),
6348 },
6349 line,
6350 })
6351 }
6352 Token::ModAssign => {
6353 self.advance();
6354 let r = self.parse_assign_expr()?;
6355 Ok(Expr {
6356 kind: ExprKind::CompoundAssign {
6357 target: Box::new(expr),
6358 op: BinOp::Mod,
6359 value: Box::new(r),
6360 },
6361 line,
6362 })
6363 }
6364 Token::PowAssign => {
6365 self.advance();
6366 let r = self.parse_assign_expr()?;
6367 Ok(Expr {
6368 kind: ExprKind::CompoundAssign {
6369 target: Box::new(expr),
6370 op: BinOp::Pow,
6371 value: Box::new(r),
6372 },
6373 line,
6374 })
6375 }
6376 Token::DotAssign => {
6377 self.advance();
6378 let r = self.parse_assign_expr()?;
6379 Ok(Expr {
6380 kind: ExprKind::CompoundAssign {
6381 target: Box::new(expr),
6382 op: BinOp::Concat,
6383 value: Box::new(r),
6384 },
6385 line,
6386 })
6387 }
6388 Token::BitAndAssign => {
6389 self.advance();
6390 let r = self.parse_assign_expr()?;
6391 Ok(Expr {
6392 kind: ExprKind::CompoundAssign {
6393 target: Box::new(expr),
6394 op: BinOp::BitAnd,
6395 value: Box::new(r),
6396 },
6397 line,
6398 })
6399 }
6400 Token::BitOrAssign => {
6401 self.advance();
6402 let r = self.parse_assign_expr()?;
6403 Ok(Expr {
6404 kind: ExprKind::CompoundAssign {
6405 target: Box::new(expr),
6406 op: BinOp::BitOr,
6407 value: Box::new(r),
6408 },
6409 line,
6410 })
6411 }
6412 Token::XorAssign => {
6413 self.advance();
6414 let r = self.parse_assign_expr()?;
6415 Ok(Expr {
6416 kind: ExprKind::CompoundAssign {
6417 target: Box::new(expr),
6418 op: BinOp::BitXor,
6419 value: Box::new(r),
6420 },
6421 line,
6422 })
6423 }
6424 Token::ShiftLeftAssign => {
6425 self.advance();
6426 let r = self.parse_assign_expr()?;
6427 Ok(Expr {
6428 kind: ExprKind::CompoundAssign {
6429 target: Box::new(expr),
6430 op: BinOp::ShiftLeft,
6431 value: Box::new(r),
6432 },
6433 line,
6434 })
6435 }
6436 Token::ShiftRightAssign => {
6437 self.advance();
6438 let r = self.parse_assign_expr()?;
6439 Ok(Expr {
6440 kind: ExprKind::CompoundAssign {
6441 target: Box::new(expr),
6442 op: BinOp::ShiftRight,
6443 value: Box::new(r),
6444 },
6445 line,
6446 })
6447 }
6448 Token::OrAssign => {
6449 self.advance();
6450 let r = self.parse_assign_expr()?;
6451 Ok(Expr {
6452 kind: ExprKind::CompoundAssign {
6453 target: Box::new(expr),
6454 op: BinOp::LogOr,
6455 value: Box::new(r),
6456 },
6457 line,
6458 })
6459 }
6460 Token::DefinedOrAssign => {
6461 self.advance();
6462 let r = self.parse_assign_expr()?;
6463 Ok(Expr {
6464 kind: ExprKind::CompoundAssign {
6465 target: Box::new(expr),
6466 op: BinOp::DefinedOr,
6467 value: Box::new(r),
6468 },
6469 line,
6470 })
6471 }
6472 Token::AndAssign => {
6473 self.advance();
6474 let r = self.parse_assign_expr()?;
6475 Ok(Expr {
6476 kind: ExprKind::CompoundAssign {
6477 target: Box::new(expr),
6478 op: BinOp::LogAnd,
6479 value: Box::new(r),
6480 },
6481 line,
6482 })
6483 }
6484 _ => Ok(expr),
6485 }
6486 }
6487
6488 fn parse_ternary(&mut self) -> PerlResult<Expr> {
6489 let expr = self.parse_pipe_forward()?;
6490 if self.eat(&Token::Question) {
6491 let line = expr.line;
6492 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
6493 let then_expr = self.parse_assign_expr();
6494 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
6495 let then_expr = then_expr?;
6496 self.expect(&Token::Colon)?;
6497 let else_expr = self.parse_assign_expr()?;
6498 return Ok(Expr {
6499 kind: ExprKind::Ternary {
6500 condition: Box::new(expr),
6501 then_expr: Box::new(then_expr),
6502 else_expr: Box::new(else_expr),
6503 },
6504 line,
6505 });
6506 }
6507 Ok(expr)
6508 }
6509
6510 fn parse_pipe_forward(&mut self) -> PerlResult<Expr> {
6516 let mut left = self.parse_range()?;
6523 if self.no_pipe_forward_depth > 0 {
6529 return Ok(left);
6530 }
6531 while matches!(self.peek(), Token::PipeForward) {
6532 if crate::compat_mode() {
6533 return Err(self.syntax_err(
6534 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
6535 left.line,
6536 ));
6537 }
6538 let line = left.line;
6539 self.advance();
6540 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
6543 let right_result = self.parse_range();
6547 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
6548 let right = right_result?;
6549 left = self.pipe_forward_apply(left, right, line)?;
6550 }
6551 Ok(left)
6552 }
6553
6554 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> PerlResult<Expr> {
6576 let Expr { kind, line: rline } = rhs;
6577 let new_kind = match kind {
6578 ExprKind::FuncCall { name, mut args } => {
6580 let dispatch_name: &str = name.strip_prefix("CORE::").unwrap_or(name.as_str());
6583 match dispatch_name {
6584 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
6585 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
6586 | "shuffled" | "frequencies" | "freq" | "pfrequencies" | "pfreq"
6587 | "interleave" | "ddump" | "stringify" | "str" | "lines" | "words"
6588 | "chars" | "digits" | "letters" | "letters_uc" | "letters_lc"
6589 | "punctuation" | "numbers" | "graphemes" | "columns" | "sentences"
6590 | "paragraphs" | "sections" | "trim" | "avg" | "to_json" | "to_csv"
6591 | "to_toml" | "to_yaml" | "to_xml" | "to_html" | "from_json" | "from_csv"
6592 | "from_toml" | "from_yaml" | "from_xml" | "to_markdown" | "to_table"
6593 | "xopen" | "clip" | "sparkline" | "bar_chart" | "flame" | "stddev"
6594 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "normalize"
6595 | "snake_case" | "camel_case" | "kebab_case" => {
6596 if args.is_empty() {
6597 args.push(lhs);
6598 } else {
6599 args[0] = lhs;
6600 }
6601 }
6602 "chunked" | "windowed" => {
6603 if args.is_empty() {
6604 return Err(self.syntax_err(
6605 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
6606 line,
6607 ));
6608 }
6609 args.insert(0, lhs);
6610 }
6611 "reduce" | "fold" => {
6612 args.push(lhs);
6613 }
6614 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
6615 args.push(lhs);
6621 }
6622 "enumerate" | "dedup" => {
6623 args.insert(0, lhs);
6626 }
6627 "clamp" => {
6628 args.push(lhs);
6630 }
6631 n if Self::is_block_then_list_pipe_builtin(n) => {
6632 if args.len() < 2 {
6633 return Err(self.syntax_err(
6634 format!(
6635 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
6636 ),
6637 line,
6638 ));
6639 }
6640 args[1] = lhs;
6641 }
6642 "take" | "head" | "tail" | "drop" => {
6643 if args.is_empty() {
6644 return Err(self.syntax_err(
6645 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
6646 line,
6647 ));
6648 }
6649 args.insert(0, lhs);
6651 }
6652 _ => {
6653 if self.thread_last_mode {
6654 args.push(lhs);
6655 } else {
6656 args.insert(0, lhs);
6657 }
6658 }
6659 }
6660 ExprKind::FuncCall { name, args }
6661 }
6662 ExprKind::MethodCall {
6663 object,
6664 method,
6665 mut args,
6666 super_call,
6667 } => {
6668 if self.thread_last_mode {
6669 args.push(lhs);
6670 } else {
6671 args.insert(0, lhs);
6672 }
6673 ExprKind::MethodCall {
6674 object,
6675 method,
6676 args,
6677 super_call,
6678 }
6679 }
6680 ExprKind::IndirectCall {
6681 target,
6682 mut args,
6683 ampersand,
6684 pass_caller_arglist: _,
6685 } => {
6686 if self.thread_last_mode {
6687 args.push(lhs);
6688 } else {
6689 args.insert(0, lhs);
6690 }
6691 ExprKind::IndirectCall {
6692 target,
6693 args,
6694 ampersand,
6695 pass_caller_arglist: false,
6698 }
6699 }
6700
6701 ExprKind::Print { handle, mut args } => {
6703 if self.thread_last_mode {
6704 args.push(lhs);
6705 } else {
6706 args.insert(0, lhs);
6707 }
6708 ExprKind::Print { handle, args }
6709 }
6710 ExprKind::Say { handle, mut args } => {
6711 if self.thread_last_mode {
6712 args.push(lhs);
6713 } else {
6714 args.insert(0, lhs);
6715 }
6716 ExprKind::Say { handle, args }
6717 }
6718 ExprKind::Printf { handle, mut args } => {
6719 if self.thread_last_mode {
6720 args.push(lhs);
6721 } else {
6722 args.insert(0, lhs);
6723 }
6724 ExprKind::Printf { handle, args }
6725 }
6726 ExprKind::Die(mut args) => {
6727 if self.thread_last_mode {
6728 args.push(lhs);
6729 } else {
6730 args.insert(0, lhs);
6731 }
6732 ExprKind::Die(args)
6733 }
6734 ExprKind::Warn(mut args) => {
6735 if self.thread_last_mode {
6736 args.push(lhs);
6737 } else {
6738 args.insert(0, lhs);
6739 }
6740 ExprKind::Warn(args)
6741 }
6742
6743 ExprKind::Sprintf { format, mut args } => {
6749 if self.thread_last_mode {
6750 args.push(lhs);
6751 } else {
6752 args.insert(0, lhs);
6753 }
6754 ExprKind::Sprintf { format, args }
6755 }
6756
6757 ExprKind::System(mut args) => {
6759 if self.thread_last_mode {
6760 args.push(lhs);
6761 } else {
6762 args.insert(0, lhs);
6763 }
6764 ExprKind::System(args)
6765 }
6766 ExprKind::Exec(mut args) => {
6767 if self.thread_last_mode {
6768 args.push(lhs);
6769 } else {
6770 args.insert(0, lhs);
6771 }
6772 ExprKind::Exec(args)
6773 }
6774 ExprKind::Unlink(mut args) => {
6775 if self.thread_last_mode {
6776 args.push(lhs);
6777 } else {
6778 args.insert(0, lhs);
6779 }
6780 ExprKind::Unlink(args)
6781 }
6782 ExprKind::Chmod(mut args) => {
6783 if self.thread_last_mode {
6784 args.push(lhs);
6785 } else {
6786 args.insert(0, lhs);
6787 }
6788 ExprKind::Chmod(args)
6789 }
6790 ExprKind::Chown(mut args) => {
6791 if self.thread_last_mode {
6792 args.push(lhs);
6793 } else {
6794 args.insert(0, lhs);
6795 }
6796 ExprKind::Chown(args)
6797 }
6798 ExprKind::Glob(mut args) => {
6799 if self.thread_last_mode {
6800 args.push(lhs);
6801 } else {
6802 args.insert(0, lhs);
6803 }
6804 ExprKind::Glob(args)
6805 }
6806 ExprKind::Files(mut args) => {
6807 if self.thread_last_mode {
6808 args.push(lhs);
6809 } else {
6810 args.insert(0, lhs);
6811 }
6812 ExprKind::Files(args)
6813 }
6814 ExprKind::Filesf(mut args) => {
6815 if self.thread_last_mode {
6816 args.push(lhs);
6817 } else {
6818 args.insert(0, lhs);
6819 }
6820 ExprKind::Filesf(args)
6821 }
6822 ExprKind::FilesfRecursive(mut args) => {
6823 if self.thread_last_mode {
6824 args.push(lhs);
6825 } else {
6826 args.insert(0, lhs);
6827 }
6828 ExprKind::FilesfRecursive(args)
6829 }
6830 ExprKind::Dirs(mut args) => {
6831 if self.thread_last_mode {
6832 args.push(lhs);
6833 } else {
6834 args.insert(0, lhs);
6835 }
6836 ExprKind::Dirs(args)
6837 }
6838 ExprKind::DirsRecursive(mut args) => {
6839 if self.thread_last_mode {
6840 args.push(lhs);
6841 } else {
6842 args.insert(0, lhs);
6843 }
6844 ExprKind::DirsRecursive(args)
6845 }
6846 ExprKind::SymLinks(mut args) => {
6847 if self.thread_last_mode {
6848 args.push(lhs);
6849 } else {
6850 args.insert(0, lhs);
6851 }
6852 ExprKind::SymLinks(args)
6853 }
6854 ExprKind::Sockets(mut args) => {
6855 if self.thread_last_mode {
6856 args.push(lhs);
6857 } else {
6858 args.insert(0, lhs);
6859 }
6860 ExprKind::Sockets(args)
6861 }
6862 ExprKind::Pipes(mut args) => {
6863 if self.thread_last_mode {
6864 args.push(lhs);
6865 } else {
6866 args.insert(0, lhs);
6867 }
6868 ExprKind::Pipes(args)
6869 }
6870 ExprKind::BlockDevices(mut args) => {
6871 if self.thread_last_mode {
6872 args.push(lhs);
6873 } else {
6874 args.insert(0, lhs);
6875 }
6876 ExprKind::BlockDevices(args)
6877 }
6878 ExprKind::CharDevices(mut args) => {
6879 if self.thread_last_mode {
6880 args.push(lhs);
6881 } else {
6882 args.insert(0, lhs);
6883 }
6884 ExprKind::CharDevices(args)
6885 }
6886 ExprKind::GlobPar { mut args, progress } => {
6887 if self.thread_last_mode {
6888 args.push(lhs);
6889 } else {
6890 args.insert(0, lhs);
6891 }
6892 ExprKind::GlobPar { args, progress }
6893 }
6894 ExprKind::ParSed { mut args, progress } => {
6895 if self.thread_last_mode {
6896 args.push(lhs);
6897 } else {
6898 args.insert(0, lhs);
6899 }
6900 ExprKind::ParSed { args, progress }
6901 }
6902
6903 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
6905 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
6906 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
6907 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
6908 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
6909 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
6910 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
6911 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
6912 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
6913 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
6914 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
6915 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
6916 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
6917 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
6918 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
6919 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
6920 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
6921 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
6922 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
6923 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
6924 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
6925 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
6926 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
6927 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
6928 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
6929 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
6930 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
6931 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
6932 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
6933 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
6934 ExprKind::Rev(_) => ExprKind::Rev(Box::new(lhs)),
6935 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
6936 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
6937 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
6938 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
6939 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
6940 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
6941 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
6942 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
6943 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
6944 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
6945 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
6946 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
6947 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
6948 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
6949 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
6950 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
6951 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
6952 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
6953 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
6954 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
6955
6956 ExprKind::MapExpr {
6958 block,
6959 list: _,
6960 flatten_array_refs,
6961 stream,
6962 } => ExprKind::MapExpr {
6963 block,
6964 list: Box::new(lhs),
6965 flatten_array_refs,
6966 stream,
6967 },
6968 ExprKind::MapExprComma {
6969 expr,
6970 list: _,
6971 flatten_array_refs,
6972 stream,
6973 } => ExprKind::MapExprComma {
6974 expr,
6975 list: Box::new(lhs),
6976 flatten_array_refs,
6977 stream,
6978 },
6979 ExprKind::GrepExpr {
6980 block,
6981 list: _,
6982 keyword,
6983 } => ExprKind::GrepExpr {
6984 block,
6985 list: Box::new(lhs),
6986 keyword,
6987 },
6988 ExprKind::GrepExprComma {
6989 expr,
6990 list: _,
6991 keyword,
6992 } => ExprKind::GrepExprComma {
6993 expr,
6994 list: Box::new(lhs),
6995 keyword,
6996 },
6997 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
6998 block,
6999 list: Box::new(lhs),
7000 },
7001 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
7002 cmp,
7003 list: Box::new(lhs),
7004 },
7005 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
7006 separator,
7007 list: Box::new(lhs),
7008 },
7009 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
7010 block,
7011 list: Box::new(lhs),
7012 },
7013 ExprKind::PMapExpr {
7014 block,
7015 list: _,
7016 progress,
7017 flat_outputs,
7018 on_cluster,
7019 stream,
7020 } => ExprKind::PMapExpr {
7021 block,
7022 list: Box::new(lhs),
7023 progress,
7024 flat_outputs,
7025 on_cluster,
7026 stream,
7027 },
7028 ExprKind::ParExpr { block, list: _ } => ExprKind::ParExpr {
7029 block,
7030 list: Box::new(lhs),
7031 },
7032 ExprKind::ParReduceExpr {
7033 extract_block,
7034 reduce_block,
7035 list: _,
7036 } => ExprKind::ParReduceExpr {
7037 extract_block,
7038 reduce_block,
7039 list: Box::new(lhs),
7040 },
7041 ExprKind::PMapChunkedExpr {
7042 chunk_size,
7043 block,
7044 list: _,
7045 progress,
7046 } => ExprKind::PMapChunkedExpr {
7047 chunk_size,
7048 block,
7049 list: Box::new(lhs),
7050 progress,
7051 },
7052 ExprKind::PGrepExpr {
7053 block,
7054 list: _,
7055 progress,
7056 stream,
7057 } => ExprKind::PGrepExpr {
7058 block,
7059 list: Box::new(lhs),
7060 progress,
7061 stream,
7062 },
7063 ExprKind::PForExpr {
7064 block,
7065 list: _,
7066 progress,
7067 } => ExprKind::PForExpr {
7068 block,
7069 list: Box::new(lhs),
7070 progress,
7071 },
7072 ExprKind::PSortExpr {
7073 cmp,
7074 list: _,
7075 progress,
7076 } => ExprKind::PSortExpr {
7077 cmp,
7078 list: Box::new(lhs),
7079 progress,
7080 },
7081 ExprKind::PReduceExpr {
7082 block,
7083 list: _,
7084 progress,
7085 } => ExprKind::PReduceExpr {
7086 block,
7087 list: Box::new(lhs),
7088 progress,
7089 },
7090 ExprKind::PcacheExpr {
7091 block,
7092 list: _,
7093 progress,
7094 } => ExprKind::PcacheExpr {
7095 block,
7096 list: Box::new(lhs),
7097 progress,
7098 },
7099 ExprKind::PReduceInitExpr {
7100 init,
7101 block,
7102 list: _,
7103 progress,
7104 } => ExprKind::PReduceInitExpr {
7105 init,
7106 block,
7107 list: Box::new(lhs),
7108 progress,
7109 },
7110 ExprKind::PMapReduceExpr {
7111 map_block,
7112 reduce_block,
7113 list: _,
7114 progress,
7115 } => ExprKind::PMapReduceExpr {
7116 map_block,
7117 reduce_block,
7118 list: Box::new(lhs),
7119 progress,
7120 },
7121
7122 ExprKind::Push { array, mut values } => {
7127 values.insert(0, lhs);
7128 ExprKind::Push { array, values }
7129 }
7130 ExprKind::Unshift { array, mut values } => {
7131 values.insert(0, lhs);
7132 ExprKind::Unshift { array, values }
7133 }
7134
7135 ExprKind::SplitExpr {
7137 pattern,
7138 string: _,
7139 limit,
7140 } => ExprKind::SplitExpr {
7141 pattern,
7142 string: Box::new(lhs),
7143 limit,
7144 },
7145
7146 ExprKind::Substitution {
7150 pattern,
7151 replacement,
7152 mut flags,
7153 expr: _,
7154 delim,
7155 } => {
7156 if !flags.contains('r') {
7157 flags.push('r');
7158 }
7159 ExprKind::Substitution {
7160 expr: Box::new(lhs),
7161 pattern,
7162 replacement,
7163 flags,
7164 delim,
7165 }
7166 }
7167 ExprKind::Transliterate {
7168 from,
7169 to,
7170 mut flags,
7171 expr: _,
7172 delim,
7173 } => {
7174 if !flags.contains('r') {
7175 flags.push('r');
7176 }
7177 ExprKind::Transliterate {
7178 expr: Box::new(lhs),
7179 from,
7180 to,
7181 flags,
7182 delim,
7183 }
7184 }
7185 ExprKind::Match {
7186 pattern,
7187 flags,
7188 scalar_g,
7189 expr: _,
7190 delim,
7191 } => ExprKind::Match {
7192 expr: Box::new(lhs),
7193 pattern,
7194 flags,
7195 scalar_g,
7196 delim,
7197 },
7198 ExprKind::Regex(pattern, flags) => ExprKind::Match {
7200 expr: Box::new(lhs),
7201 pattern,
7202 flags,
7203 scalar_g: false,
7204 delim: '/',
7205 },
7206
7207 ExprKind::Bareword(name) => match name.as_str() {
7209 "reverse" => {
7210 if crate::no_interop_mode() {
7211 return Err(self.syntax_err(
7212 "stryke uses `rev` instead of `reverse` (--no-interop)",
7213 line,
7214 ));
7215 }
7216 ExprKind::ReverseExpr(Box::new(lhs))
7217 }
7218 "rv" | "reversed" | "rev" => ExprKind::Rev(Box::new(lhs)),
7219 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
7220 name: "uniq".to_string(),
7221 args: vec![lhs],
7222 },
7223 "fl" | "flatten" => ExprKind::FuncCall {
7224 name: "flatten".to_string(),
7225 args: vec![lhs],
7226 },
7227 _ => ExprKind::FuncCall {
7228 name,
7229 args: vec![lhs],
7230 },
7231 },
7232
7233 kind @ (ExprKind::ScalarVar(_)
7235 | ExprKind::ArrayElement { .. }
7236 | ExprKind::HashElement { .. }
7237 | ExprKind::Deref { .. }
7238 | ExprKind::ArrowDeref { .. }
7239 | ExprKind::CodeRef { .. }
7240 | ExprKind::SubroutineRef(_)
7241 | ExprKind::SubroutineCodeRef(_)
7242 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
7243 target: Box::new(Expr { kind, line: rline }),
7244 args: vec![lhs],
7245 ampersand: false,
7246 pass_caller_arglist: false,
7247 },
7248
7249 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
7253 ExprKind::IndirectCall {
7254 target: inner,
7255 args: vec![lhs],
7256 ampersand: false,
7257 pass_caller_arglist: false,
7258 }
7259 }
7260
7261 other => {
7262 return Err(self.syntax_err(
7263 format!(
7264 "right-hand side of `|>` must be a call, builtin, or coderef \
7265 expression (got {})",
7266 Self::expr_kind_name(&other)
7267 ),
7268 line,
7269 ));
7270 }
7271 };
7272 Ok(Expr {
7273 kind: new_kind,
7274 line,
7275 })
7276 }
7277
7278 fn expr_kind_name(kind: &ExprKind) -> &'static str {
7280 match kind {
7281 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
7282 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
7283 ExprKind::BinOp { .. } => "binary expression",
7284 ExprKind::UnaryOp { .. } => "unary expression",
7285 ExprKind::Ternary { .. } => "ternary expression",
7286 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
7287 ExprKind::List(_) => "list expression",
7288 ExprKind::Range { .. } => "range expression",
7289 _ => "expression",
7290 }
7291 }
7292
7293 fn parse_or_word(&mut self) -> PerlResult<Expr> {
7295 let mut left = self.parse_and_word()?;
7296 while matches!(self.peek(), Token::LogOrWord) {
7297 let line = left.line;
7298 self.advance();
7299 let right = self.parse_and_word()?;
7300 left = Expr {
7301 kind: ExprKind::BinOp {
7302 left: Box::new(left),
7303 op: BinOp::LogOrWord,
7304 right: Box::new(right),
7305 },
7306 line,
7307 };
7308 }
7309 Ok(left)
7310 }
7311
7312 fn parse_and_word(&mut self) -> PerlResult<Expr> {
7313 let mut left = self.parse_not_word()?;
7314 while matches!(self.peek(), Token::LogAndWord) {
7315 let line = left.line;
7316 self.advance();
7317 let right = self.parse_not_word()?;
7318 left = Expr {
7319 kind: ExprKind::BinOp {
7320 left: Box::new(left),
7321 op: BinOp::LogAndWord,
7322 right: Box::new(right),
7323 },
7324 line,
7325 };
7326 }
7327 Ok(left)
7328 }
7329
7330 fn parse_not_word(&mut self) -> PerlResult<Expr> {
7331 if matches!(self.peek(), Token::LogNotWord) {
7332 let line = self.peek_line();
7333 self.advance();
7334 let expr = self.parse_not_word()?;
7335 return Ok(Expr {
7336 kind: ExprKind::UnaryOp {
7337 op: UnaryOp::LogNotWord,
7338 expr: Box::new(expr),
7339 },
7340 line,
7341 });
7342 }
7343 self.parse_assign_expr()
7346 }
7347
7348 fn parse_log_or(&mut self) -> PerlResult<Expr> {
7349 let mut left = self.parse_log_and()?;
7350 loop {
7351 let op = match self.peek() {
7352 Token::LogOr => BinOp::LogOr,
7353 Token::DefinedOr => BinOp::DefinedOr,
7354 _ => break,
7355 };
7356 let line = left.line;
7357 self.advance();
7358 let right = self.parse_log_and()?;
7359 left = Expr {
7360 kind: ExprKind::BinOp {
7361 left: Box::new(left),
7362 op,
7363 right: Box::new(right),
7364 },
7365 line,
7366 };
7367 }
7368 Ok(left)
7369 }
7370
7371 fn parse_log_and(&mut self) -> PerlResult<Expr> {
7372 let mut left = self.parse_bit_or()?;
7373 while matches!(self.peek(), Token::LogAnd) {
7374 let line = left.line;
7375 self.advance();
7376 let right = self.parse_bit_or()?;
7377 left = Expr {
7378 kind: ExprKind::BinOp {
7379 left: Box::new(left),
7380 op: BinOp::LogAnd,
7381 right: Box::new(right),
7382 },
7383 line,
7384 };
7385 }
7386 Ok(left)
7387 }
7388
7389 fn parse_bit_or(&mut self) -> PerlResult<Expr> {
7390 let mut left = self.parse_bit_xor()?;
7391 while matches!(self.peek(), Token::BitOr) {
7392 let line = left.line;
7393 self.advance();
7394 let right = self.parse_bit_xor()?;
7395 left = Expr {
7396 kind: ExprKind::BinOp {
7397 left: Box::new(left),
7398 op: BinOp::BitOr,
7399 right: Box::new(right),
7400 },
7401 line,
7402 };
7403 }
7404 Ok(left)
7405 }
7406
7407 fn parse_bit_xor(&mut self) -> PerlResult<Expr> {
7408 let mut left = self.parse_bit_and()?;
7409 while matches!(self.peek(), Token::BitXor) {
7410 let line = left.line;
7411 self.advance();
7412 let right = self.parse_bit_and()?;
7413 left = Expr {
7414 kind: ExprKind::BinOp {
7415 left: Box::new(left),
7416 op: BinOp::BitXor,
7417 right: Box::new(right),
7418 },
7419 line,
7420 };
7421 }
7422 Ok(left)
7423 }
7424
7425 fn parse_bit_and(&mut self) -> PerlResult<Expr> {
7426 let mut left = self.parse_equality()?;
7427 while matches!(self.peek(), Token::BitAnd) {
7428 let line = left.line;
7429 self.advance();
7430 let right = self.parse_equality()?;
7431 left = Expr {
7432 kind: ExprKind::BinOp {
7433 left: Box::new(left),
7434 op: BinOp::BitAnd,
7435 right: Box::new(right),
7436 },
7437 line,
7438 };
7439 }
7440 Ok(left)
7441 }
7442
7443 fn parse_equality(&mut self) -> PerlResult<Expr> {
7444 let mut left = self.parse_comparison()?;
7445 loop {
7446 let op = match self.peek() {
7447 Token::NumEq => BinOp::NumEq,
7448 Token::NumNe => BinOp::NumNe,
7449 Token::StrEq => BinOp::StrEq,
7450 Token::StrNe => BinOp::StrNe,
7451 Token::Spaceship => BinOp::Spaceship,
7452 Token::StrCmp => BinOp::StrCmp,
7453 _ => break,
7454 };
7455 let line = left.line;
7456 self.advance();
7457 let right = self.parse_comparison()?;
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_comparison(&mut self) -> PerlResult<Expr> {
7471 let left = self.parse_shift()?;
7472 let first_op = match self.peek() {
7473 Token::NumLt => BinOp::NumLt,
7474 Token::NumGt => BinOp::NumGt,
7475 Token::NumLe => BinOp::NumLe,
7476 Token::NumGe => BinOp::NumGe,
7477 Token::StrLt => BinOp::StrLt,
7478 Token::StrGt => BinOp::StrGt,
7479 Token::StrLe => BinOp::StrLe,
7480 Token::StrGe => BinOp::StrGe,
7481 _ => return Ok(left),
7482 };
7483 let line = left.line;
7484 self.advance();
7485 let middle = self.parse_shift()?;
7486
7487 let second_op = match self.peek() {
7488 Token::NumLt => Some(BinOp::NumLt),
7489 Token::NumGt => Some(BinOp::NumGt),
7490 Token::NumLe => Some(BinOp::NumLe),
7491 Token::NumGe => Some(BinOp::NumGe),
7492 Token::StrLt => Some(BinOp::StrLt),
7493 Token::StrGt => Some(BinOp::StrGt),
7494 Token::StrLe => Some(BinOp::StrLe),
7495 Token::StrGe => Some(BinOp::StrGe),
7496 _ => None,
7497 };
7498
7499 if second_op.is_none() {
7500 return Ok(Expr {
7501 kind: ExprKind::BinOp {
7502 left: Box::new(left),
7503 op: first_op,
7504 right: Box::new(middle),
7505 },
7506 line,
7507 });
7508 }
7509
7510 let mut operands = vec![left, middle];
7513 let mut ops = vec![first_op];
7514
7515 loop {
7516 let op = match self.peek() {
7517 Token::NumLt => BinOp::NumLt,
7518 Token::NumGt => BinOp::NumGt,
7519 Token::NumLe => BinOp::NumLe,
7520 Token::NumGe => BinOp::NumGe,
7521 Token::StrLt => BinOp::StrLt,
7522 Token::StrGt => BinOp::StrGt,
7523 Token::StrLe => BinOp::StrLe,
7524 Token::StrGe => BinOp::StrGe,
7525 _ => break,
7526 };
7527 self.advance();
7528 ops.push(op);
7529 operands.push(self.parse_shift()?);
7530 }
7531
7532 let mut result = Expr {
7534 kind: ExprKind::BinOp {
7535 left: Box::new(operands[0].clone()),
7536 op: ops[0],
7537 right: Box::new(operands[1].clone()),
7538 },
7539 line,
7540 };
7541
7542 for i in 1..ops.len() {
7543 let cmp = Expr {
7544 kind: ExprKind::BinOp {
7545 left: Box::new(operands[i].clone()),
7546 op: ops[i],
7547 right: Box::new(operands[i + 1].clone()),
7548 },
7549 line,
7550 };
7551 result = Expr {
7552 kind: ExprKind::BinOp {
7553 left: Box::new(result),
7554 op: BinOp::LogAnd,
7555 right: Box::new(cmp),
7556 },
7557 line,
7558 };
7559 }
7560
7561 Ok(result)
7562 }
7563
7564 fn parse_shift(&mut self) -> PerlResult<Expr> {
7565 let mut left = self.parse_addition()?;
7566 loop {
7567 let op = match self.peek() {
7568 Token::ShiftLeft => BinOp::ShiftLeft,
7569 Token::ShiftRight => BinOp::ShiftRight,
7570 _ => break,
7571 };
7572 let line = left.line;
7573 self.advance();
7574 let right = self.parse_addition()?;
7575 left = Expr {
7576 kind: ExprKind::BinOp {
7577 left: Box::new(left),
7578 op,
7579 right: Box::new(right),
7580 },
7581 line,
7582 };
7583 }
7584 Ok(left)
7585 }
7586
7587 fn parse_addition(&mut self) -> PerlResult<Expr> {
7588 let mut left = self.parse_multiplication()?;
7589 loop {
7590 let op = match self.peek() {
7593 Token::Plus if self.peek_line() == self.prev_line() => BinOp::Add,
7594 Token::Minus if self.peek_line() == self.prev_line() => BinOp::Sub,
7595 Token::Dot => BinOp::Concat,
7596 _ => break,
7597 };
7598 let line = left.line;
7599 self.advance();
7600 let right = self.parse_multiplication()?;
7601 left = Expr {
7602 kind: ExprKind::BinOp {
7603 left: Box::new(left),
7604 op,
7605 right: Box::new(right),
7606 },
7607 line,
7608 };
7609 }
7610 Ok(left)
7611 }
7612
7613 fn parse_multiplication(&mut self) -> PerlResult<Expr> {
7614 let mut left = self.parse_regex_bind()?;
7615 loop {
7616 let op = match self.peek() {
7617 Token::Star => BinOp::Mul,
7618 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
7619 Token::Percent if self.peek_line() == self.prev_line() => BinOp::Mod,
7622 Token::X => {
7623 let line = left.line;
7624 let list_repeat = self.list_construct_close_pos == Some(self.pos);
7631 self.advance();
7632 let right = self.parse_regex_bind()?;
7633 left = Expr {
7634 kind: ExprKind::Repeat {
7635 expr: Box::new(left),
7636 count: Box::new(right),
7637 list_repeat,
7638 },
7639 line,
7640 };
7641 continue;
7642 }
7643 _ => break,
7644 };
7645 let line = left.line;
7646 self.advance();
7647 let right = self.parse_regex_bind()?;
7648 left = Expr {
7649 kind: ExprKind::BinOp {
7650 left: Box::new(left),
7651 op,
7652 right: Box::new(right),
7653 },
7654 line,
7655 };
7656 }
7657 Ok(left)
7658 }
7659
7660 fn parse_regex_bind(&mut self) -> PerlResult<Expr> {
7661 let left = self.parse_unary()?;
7662 match self.peek() {
7663 Token::BindMatch => {
7664 let line = left.line;
7665 self.advance();
7666 match self.peek().clone() {
7667 Token::Regex(pattern, flags, delim) => {
7668 self.advance();
7669 Ok(Expr {
7670 kind: ExprKind::Match {
7671 expr: Box::new(left),
7672 pattern,
7673 flags,
7674 scalar_g: false,
7675 delim,
7676 },
7677 line,
7678 })
7679 }
7680 Token::Ident(ref s) if s.starts_with('\x00') => {
7681 let (Token::Ident(encoded), _) = self.advance() else {
7682 unreachable!()
7683 };
7684 let parts: Vec<&str> = encoded.split('\x00').collect();
7685 if parts.len() >= 4 && parts[1] == "s" {
7686 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7687 Ok(Expr {
7688 kind: ExprKind::Substitution {
7689 expr: Box::new(left),
7690 pattern: parts[2].to_string(),
7691 replacement: parts[3].to_string(),
7692 flags: parts.get(4).unwrap_or(&"").to_string(),
7693 delim,
7694 },
7695 line,
7696 })
7697 } else if parts.len() >= 4 && parts[1] == "tr" {
7698 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7699 Ok(Expr {
7700 kind: ExprKind::Transliterate {
7701 expr: Box::new(left),
7702 from: parts[2].to_string(),
7703 to: parts[3].to_string(),
7704 flags: parts.get(4).unwrap_or(&"").to_string(),
7705 delim,
7706 },
7707 line,
7708 })
7709 } else {
7710 Err(self.syntax_err("Invalid regex binding", line))
7711 }
7712 }
7713 _ => {
7714 let rhs = self.parse_unary()?;
7715 Ok(Expr {
7716 kind: ExprKind::BinOp {
7717 left: Box::new(left),
7718 op: BinOp::BindMatch,
7719 right: Box::new(rhs),
7720 },
7721 line,
7722 })
7723 }
7724 }
7725 }
7726 Token::BindNotMatch => {
7727 let line = left.line;
7728 self.advance();
7729 match self.peek().clone() {
7730 Token::Regex(pattern, flags, delim) => {
7731 self.advance();
7732 Ok(Expr {
7733 kind: ExprKind::UnaryOp {
7734 op: UnaryOp::LogNot,
7735 expr: Box::new(Expr {
7736 kind: ExprKind::Match {
7737 expr: Box::new(left),
7738 pattern,
7739 flags,
7740 scalar_g: false,
7741 delim,
7742 },
7743 line,
7744 }),
7745 },
7746 line,
7747 })
7748 }
7749 Token::Ident(ref s) if s.starts_with('\x00') => {
7750 let (Token::Ident(encoded), _) = self.advance() else {
7751 unreachable!()
7752 };
7753 let parts: Vec<&str> = encoded.split('\x00').collect();
7754 if parts.len() >= 4 && parts[1] == "s" {
7755 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7756 Ok(Expr {
7757 kind: ExprKind::UnaryOp {
7758 op: UnaryOp::LogNot,
7759 expr: Box::new(Expr {
7760 kind: ExprKind::Substitution {
7761 expr: Box::new(left),
7762 pattern: parts[2].to_string(),
7763 replacement: parts[3].to_string(),
7764 flags: parts.get(4).unwrap_or(&"").to_string(),
7765 delim,
7766 },
7767 line,
7768 }),
7769 },
7770 line,
7771 })
7772 } else if parts.len() >= 4 && parts[1] == "tr" {
7773 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7774 Ok(Expr {
7775 kind: ExprKind::UnaryOp {
7776 op: UnaryOp::LogNot,
7777 expr: Box::new(Expr {
7778 kind: ExprKind::Transliterate {
7779 expr: Box::new(left),
7780 from: parts[2].to_string(),
7781 to: parts[3].to_string(),
7782 flags: parts.get(4).unwrap_or(&"").to_string(),
7783 delim,
7784 },
7785 line,
7786 }),
7787 },
7788 line,
7789 })
7790 } else {
7791 Err(self.syntax_err("Invalid regex binding after !~", line))
7792 }
7793 }
7794 _ => {
7795 let rhs = self.parse_unary()?;
7796 Ok(Expr {
7797 kind: ExprKind::BinOp {
7798 left: Box::new(left),
7799 op: BinOp::BindNotMatch,
7800 right: Box::new(rhs),
7801 },
7802 line,
7803 })
7804 }
7805 }
7806 }
7807 _ => Ok(left),
7808 }
7809 }
7810
7811 fn parse_thread_input(&mut self) -> PerlResult<Expr> {
7814 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
7815 let result = self.parse_range();
7816 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
7817 result
7818 }
7819
7820 fn parse_thread_macro_chunk_par(&mut self, line: usize, thread_last: bool) -> PerlResult<Expr> {
7825 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
7827 let source_expr = self.parse_thread_input();
7828 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
7829 let source_expr = source_expr?;
7830
7831 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
7837 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
7838 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
7839 let chunk_chain = chunk_chain?;
7840
7841 let extract_block: Block = match chunk_chain.kind {
7846 ExprKind::CodeRef { params: _, body } => body,
7847 _ => vec![Statement {
7848 label: None,
7849 kind: StmtKind::Expression(chunk_chain),
7850 line,
7851 }],
7852 };
7853
7854 let par_reduce = Expr {
7855 kind: ExprKind::ParReduceExpr {
7856 extract_block,
7857 reduce_block: None,
7858 list: Box::new(source_expr),
7859 },
7860 line,
7861 };
7862
7863 if self.eat_chunk_par_split_boundary() {
7867 return self.parse_thread_macro_continuation(par_reduce, line, thread_last);
7868 }
7869 Ok(par_reduce)
7870 }
7871
7872 fn parse_thread_macro_continuation(
7878 &mut self,
7879 prior: Expr,
7880 line: usize,
7881 thread_last: bool,
7882 ) -> PerlResult<Expr> {
7883 self.pending_thread_input = Some(prior);
7884 let res = self.parse_thread_macro_inner(line, thread_last, None);
7885 self.pending_thread_input = None;
7886 res
7887 }
7888
7889 fn eat_chunk_par_split_boundary(&mut self) -> bool {
7893 if matches!(self.peek(), Token::LogOr) && matches!(self.peek_at(1), Token::NumGt) {
7895 self.advance(); self.advance(); return true;
7898 }
7899 if matches!(self.peek(), Token::BitOr) {
7901 if let Token::Ident(name) = self.peek_at(1).clone() {
7902 if name == "then" && matches!(self.peek_at(2), Token::BitOr) {
7903 self.advance(); self.advance(); self.advance(); return true;
7907 }
7908 }
7909 }
7910 false
7911 }
7912
7913 fn parse_range(&mut self) -> PerlResult<Expr> {
7920 let left = self.parse_log_or()?;
7921 let line = left.line;
7922 let (exclusive, _colon_style) = if self.eat(&Token::RangeExclusive) {
7929 (true, false)
7930 } else if self.eat(&Token::Range) {
7931 (false, false)
7932 } else if self.suppress_colon_range == 0 && self.eat(&Token::Colon) {
7933 (false, true)
7936 } else if self.suppress_tilde_range == 0 && self.eat(&Token::BitNot) {
7937 (false, true)
7938 } else {
7939 return Ok(left);
7940 };
7941 let right = self.parse_log_or()?;
7942 let step = if self.eat(&Token::Colon)
7946 || (self.suppress_tilde_range == 0 && self.eat(&Token::BitNot))
7947 {
7948 Some(Box::new(self.parse_unary()?))
7949 } else {
7950 None
7951 };
7952 Ok(Expr {
7953 kind: ExprKind::Range {
7954 from: Box::new(left),
7955 to: Box::new(right),
7956 exclusive,
7957 step,
7958 },
7959 line,
7960 })
7961 }
7962
7963 fn parse_package_qualified_identifier(&mut self) -> PerlResult<String> {
7965 let mut name = match self.advance() {
7966 (Token::Ident(n), _) => n,
7967 (tok, l) => {
7968 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
7969 }
7970 };
7971 while self.eat(&Token::PackageSep) {
7972 match self.advance() {
7973 (Token::Ident(part), _) => {
7974 name.push_str("::");
7975 name.push_str(&part);
7976 }
7977 (Token::ScalarVar(part), _) if Self::is_underscore_topic_slot(&part) => {
7985 name.push_str("::");
7986 name.push_str(&part);
7987 }
7988 (tok, l) => {
7989 return Err(self
7990 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
7991 }
7992 }
7993 }
7994 Ok(name)
7995 }
7996
7997 fn parse_qualified_subroutine_name(&mut self) -> PerlResult<String> {
7999 self.parse_package_qualified_identifier()
8000 }
8001
8002 fn parse_unary(&mut self) -> PerlResult<Expr> {
8003 let line = self.peek_line();
8004 match self.peek().clone() {
8005 Token::Minus => {
8006 self.advance();
8007 let expr = self.parse_power()?;
8008 Ok(Expr {
8009 kind: ExprKind::UnaryOp {
8010 op: UnaryOp::Negate,
8011 expr: Box::new(expr),
8012 },
8013 line,
8014 })
8015 }
8016 Token::Plus => {
8023 self.advance();
8024 if matches!(self.peek(), Token::LBrace) {
8025 let line = self.peek_line();
8026 self.advance(); return self.parse_forced_hashref_body(line);
8028 }
8029 self.parse_unary()
8030 }
8031 Token::LogNot => {
8032 self.advance();
8033 let expr = self.parse_unary()?;
8034 Ok(Expr {
8035 kind: ExprKind::UnaryOp {
8036 op: UnaryOp::LogNot,
8037 expr: Box::new(expr),
8038 },
8039 line,
8040 })
8041 }
8042 Token::BitNot => {
8043 self.advance();
8044 let expr = self.parse_unary()?;
8045 Ok(Expr {
8046 kind: ExprKind::UnaryOp {
8047 op: UnaryOp::BitNot,
8048 expr: Box::new(expr),
8049 },
8050 line,
8051 })
8052 }
8053 Token::Increment => {
8054 self.advance();
8055 let expr = self.parse_postfix()?;
8056 Ok(Expr {
8057 kind: ExprKind::UnaryOp {
8058 op: UnaryOp::PreIncrement,
8059 expr: Box::new(expr),
8060 },
8061 line,
8062 })
8063 }
8064 Token::Decrement => {
8065 self.advance();
8066 let expr = self.parse_postfix()?;
8067 Ok(Expr {
8068 kind: ExprKind::UnaryOp {
8069 op: UnaryOp::PreDecrement,
8070 expr: Box::new(expr),
8071 },
8072 line,
8073 })
8074 }
8075 Token::BitAnd => {
8076 self.advance();
8079 if matches!(self.peek(), Token::LBrace) {
8080 self.advance();
8081 let inner = self.parse_expression()?;
8082 self.expect(&Token::RBrace)?;
8083 return Ok(Expr {
8084 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
8085 line,
8086 });
8087 }
8088 if matches!(self.peek(), Token::Ident(_)) {
8089 let name = self.parse_qualified_subroutine_name()?;
8090 return Ok(Expr {
8091 kind: ExprKind::SubroutineRef(name),
8092 line,
8093 });
8094 }
8095 let target = self.parse_primary()?;
8096 if matches!(self.peek(), Token::LParen) {
8097 self.advance();
8098 let args = self.parse_arg_list()?;
8099 self.expect(&Token::RParen)?;
8100 return Ok(Expr {
8101 kind: ExprKind::IndirectCall {
8102 target: Box::new(target),
8103 args,
8104 ampersand: true,
8105 pass_caller_arglist: false,
8106 },
8107 line,
8108 });
8109 }
8110 Ok(Expr {
8112 kind: ExprKind::IndirectCall {
8113 target: Box::new(target),
8114 args: vec![],
8115 ampersand: true,
8116 pass_caller_arglist: true,
8117 },
8118 line,
8119 })
8120 }
8121 Token::Backslash => {
8122 self.advance();
8123 let expr = self.parse_unary()?;
8124 if let ExprKind::SubroutineRef(name) = expr.kind {
8125 return Ok(Expr {
8126 kind: ExprKind::SubroutineCodeRef(name),
8127 line,
8128 });
8129 }
8130 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
8131 return Ok(expr);
8132 }
8133 Ok(Expr {
8135 kind: ExprKind::ScalarRef(Box::new(expr)),
8136 line,
8137 })
8138 }
8139 Token::FileTest(op) => {
8140 self.advance();
8141 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
8143 Expr {
8144 kind: ExprKind::ScalarVar("_".into()),
8145 line: self.peek_line(),
8146 }
8147 } else {
8148 self.parse_unary()?
8149 };
8150 Ok(Expr {
8151 kind: ExprKind::FileTest {
8152 op,
8153 expr: Box::new(expr),
8154 },
8155 line,
8156 })
8157 }
8158 _ => self.parse_power(),
8159 }
8160 }
8161
8162 fn parse_power(&mut self) -> PerlResult<Expr> {
8163 let left = self.parse_postfix()?;
8164 if matches!(self.peek(), Token::Power) {
8165 let line = left.line;
8166 self.advance();
8167 let right = self.parse_unary()?; return Ok(Expr {
8169 kind: ExprKind::BinOp {
8170 left: Box::new(left),
8171 op: BinOp::Pow,
8172 right: Box::new(right),
8173 },
8174 line,
8175 });
8176 }
8177 Ok(left)
8178 }
8179
8180 fn parse_postfix(&mut self) -> PerlResult<Expr> {
8181 let mut expr = self.parse_primary()?;
8182 loop {
8183 match self.peek().clone() {
8184 Token::Increment => {
8185 if self.peek_line() > self.prev_line() {
8188 break;
8189 }
8190 let line = expr.line;
8191 self.advance();
8192 expr = Expr {
8193 kind: ExprKind::PostfixOp {
8194 expr: Box::new(expr),
8195 op: PostfixOp::Increment,
8196 },
8197 line,
8198 };
8199 }
8200 Token::Decrement => {
8201 if self.peek_line() > self.prev_line() {
8204 break;
8205 }
8206 let line = expr.line;
8207 self.advance();
8208 expr = Expr {
8209 kind: ExprKind::PostfixOp {
8210 expr: Box::new(expr),
8211 op: PostfixOp::Decrement,
8212 },
8213 line,
8214 };
8215 }
8216 Token::LParen => {
8217 if self.suppress_indirect_paren_call > 0 {
8218 break;
8219 }
8220 if self.peek_line() > self.prev_line() {
8224 break;
8225 }
8226 let line = expr.line;
8227 self.advance();
8228 let args = self.parse_arg_list()?;
8229 self.expect(&Token::RParen)?;
8230 expr = Expr {
8231 kind: ExprKind::IndirectCall {
8232 target: Box::new(expr),
8233 args,
8234 ampersand: false,
8235 pass_caller_arglist: false,
8236 },
8237 line,
8238 };
8239 }
8240 Token::Arrow => {
8241 let line = expr.line;
8242 self.advance();
8243 match self.peek().clone() {
8244 Token::LBracket => {
8245 self.advance();
8246 let index = self.parse_expression()?;
8247 self.expect(&Token::RBracket)?;
8248 expr = Expr {
8249 kind: ExprKind::ArrowDeref {
8250 expr: Box::new(expr),
8251 index: Box::new(index),
8252 kind: DerefKind::Array,
8253 },
8254 line,
8255 };
8256 }
8257 Token::LBrace => {
8258 self.advance();
8259 let key = self.parse_hash_subscript_key()?;
8260 self.expect(&Token::RBrace)?;
8261 expr = Expr {
8262 kind: ExprKind::ArrowDeref {
8263 expr: Box::new(expr),
8264 index: Box::new(key),
8265 kind: DerefKind::Hash,
8266 },
8267 line,
8268 };
8269 }
8270 Token::LParen => {
8271 self.advance();
8272 let args = self.parse_arg_list()?;
8273 self.expect(&Token::RParen)?;
8274 expr = Expr {
8275 kind: ExprKind::ArrowDeref {
8276 expr: Box::new(expr),
8277 index: Box::new(Expr {
8278 kind: ExprKind::List(args),
8279 line,
8280 }),
8281 kind: DerefKind::Call,
8282 },
8283 line,
8284 };
8285 }
8286 Token::Ident(method) => {
8287 self.advance();
8288 if method == "SUPER" {
8289 self.expect(&Token::PackageSep)?;
8290 let real_method = match self.advance() {
8291 (Token::Ident(n), _) => n,
8292 (tok, l) => {
8293 return Err(self.syntax_err(
8294 format!(
8295 "Expected method name after SUPER::, got {:?}",
8296 tok
8297 ),
8298 l,
8299 ));
8300 }
8301 };
8302 let args = if self.eat(&Token::LParen) {
8303 let a = self.parse_arg_list()?;
8304 self.expect(&Token::RParen)?;
8305 a
8306 } else {
8307 self.parse_method_arg_list_no_paren()?
8308 };
8309 expr = Expr {
8310 kind: ExprKind::MethodCall {
8311 object: Box::new(expr),
8312 method: real_method,
8313 args,
8314 super_call: true,
8315 },
8316 line,
8317 };
8318 } else {
8319 let mut method_name = method;
8320 while self.eat(&Token::PackageSep) {
8321 match self.advance() {
8322 (Token::Ident(part), _) => {
8323 method_name.push_str("::");
8324 method_name.push_str(&part);
8325 }
8326 (tok, l) => {
8327 return Err(self.syntax_err(
8328 format!(
8329 "Expected identifier after :: in method name, got {:?}",
8330 tok
8331 ),
8332 l,
8333 ));
8334 }
8335 }
8336 }
8337 let args = if self.eat(&Token::LParen) {
8338 let a = self.parse_arg_list()?;
8339 self.expect(&Token::RParen)?;
8340 a
8341 } else {
8342 self.parse_method_arg_list_no_paren()?
8343 };
8344 expr = Expr {
8345 kind: ExprKind::MethodCall {
8346 object: Box::new(expr),
8347 method: method_name,
8348 args,
8349 super_call: false,
8350 },
8351 line,
8352 };
8353 }
8354 }
8355 Token::ArrayAt => {
8361 self.advance(); match self.peek().clone() {
8363 Token::Star => {
8364 self.advance();
8365 expr = Expr {
8366 kind: ExprKind::Deref {
8367 expr: Box::new(expr),
8368 kind: Sigil::Array,
8369 },
8370 line,
8371 };
8372 }
8373 Token::LBracket => {
8374 self.advance();
8375 let indices = self.parse_slice_arg_list(false)?;
8376 self.expect(&Token::RBracket)?;
8377 let source = Expr {
8378 kind: ExprKind::Deref {
8379 expr: Box::new(expr),
8380 kind: Sigil::Array,
8381 },
8382 line,
8383 };
8384 expr = Expr {
8385 kind: ExprKind::AnonymousListSlice {
8386 source: Box::new(source),
8387 indices,
8388 },
8389 line,
8390 };
8391 }
8392 Token::LBrace => {
8393 self.advance();
8394 let keys = self.parse_slice_arg_list(true)?;
8395 self.expect(&Token::RBrace)?;
8396 expr = Expr {
8397 kind: ExprKind::HashSliceDeref {
8398 container: Box::new(expr),
8399 keys,
8400 },
8401 line,
8402 };
8403 }
8404 tok => {
8405 return Err(self.syntax_err(
8406 format!(
8407 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
8408 tok
8409 ),
8410 line,
8411 ));
8412 }
8413 }
8414 }
8415 Token::HashPercent => {
8416 self.advance(); match self.peek().clone() {
8418 Token::Star => {
8419 self.advance();
8420 expr = Expr {
8421 kind: ExprKind::Deref {
8422 expr: Box::new(expr),
8423 kind: Sigil::Hash,
8424 },
8425 line,
8426 };
8427 }
8428 tok => {
8429 return Err(self.syntax_err(
8430 format!("Expected `*` after `->%`, got {:?}", tok),
8431 line,
8432 ));
8433 }
8434 }
8435 }
8436 Token::X => {
8438 self.advance();
8439 let args = if self.eat(&Token::LParen) {
8440 let a = self.parse_arg_list()?;
8441 self.expect(&Token::RParen)?;
8442 a
8443 } else {
8444 self.parse_method_arg_list_no_paren()?
8445 };
8446 expr = Expr {
8447 kind: ExprKind::MethodCall {
8448 object: Box::new(expr),
8449 method: "x".to_string(),
8450 args,
8451 super_call: false,
8452 },
8453 line,
8454 };
8455 }
8456 _ => break,
8457 }
8458 }
8459 Token::LBracket => {
8460 if self.peek_line() > self.prev_line() {
8463 break;
8464 }
8465 let line = expr.line;
8467 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
8468 if let ExprKind::ScalarVar(ref name) = expr.kind {
8469 let name = name.clone();
8470 self.advance();
8471 let index = self.parse_expression()?;
8472 self.expect(&Token::RBracket)?;
8473 expr = Expr {
8474 kind: ExprKind::ArrayElement {
8475 array: name,
8476 index: Box::new(index),
8477 },
8478 line,
8479 };
8480 }
8481 } else if postfix_lbracket_is_arrow_container(&expr) {
8482 self.advance();
8483 let indices = self.parse_arg_list()?;
8484 self.expect(&Token::RBracket)?;
8485 expr = Expr {
8486 kind: ExprKind::ArrowDeref {
8487 expr: Box::new(expr),
8488 index: Box::new(Expr {
8489 kind: ExprKind::List(indices),
8490 line,
8491 }),
8492 kind: DerefKind::Array,
8493 },
8494 line,
8495 };
8496 } else {
8497 self.advance();
8498 let indices = self.parse_arg_list()?;
8499 self.expect(&Token::RBracket)?;
8500 expr = Expr {
8501 kind: ExprKind::AnonymousListSlice {
8502 source: Box::new(expr),
8503 indices,
8504 },
8505 line,
8506 };
8507 }
8508 }
8509 Token::LBrace => {
8510 if self.suppress_scalar_hash_brace > 0 {
8511 break;
8512 }
8513 if self.peek_line() > self.prev_line() {
8516 break;
8517 }
8518 let line = expr.line;
8521 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
8522 let is_chainable_hash_subscript = is_scalar_named_hash
8523 || matches!(
8524 expr.kind,
8525 ExprKind::HashElement { .. }
8526 | ExprKind::ArrayElement { .. }
8527 | ExprKind::ArrowDeref { .. }
8528 | ExprKind::Deref {
8529 kind: Sigil::Scalar,
8530 ..
8531 }
8532 );
8533 if !is_chainable_hash_subscript {
8534 break;
8535 }
8536 self.advance();
8537 let key = self.parse_hash_subscript_key()?;
8538 self.expect(&Token::RBrace)?;
8539 expr = if is_scalar_named_hash {
8540 if let ExprKind::ScalarVar(ref name) = expr.kind {
8541 let name = name.clone();
8542 if name == "_" {
8544 Expr {
8545 kind: ExprKind::ArrowDeref {
8546 expr: Box::new(Expr {
8547 kind: ExprKind::ScalarVar("_".into()),
8548 line,
8549 }),
8550 index: Box::new(key),
8551 kind: DerefKind::Hash,
8552 },
8553 line,
8554 }
8555 } else {
8556 Expr {
8557 kind: ExprKind::HashElement {
8558 hash: name,
8559 key: Box::new(key),
8560 },
8561 line,
8562 }
8563 }
8564 } else {
8565 unreachable!("is_scalar_named_hash implies ScalarVar");
8566 }
8567 } else {
8568 Expr {
8569 kind: ExprKind::ArrowDeref {
8570 expr: Box::new(expr),
8571 index: Box::new(key),
8572 kind: DerefKind::Hash,
8573 },
8574 line,
8575 }
8576 };
8577 }
8578 Token::LogNot | Token::BitNot => {
8579 if !matches!(expr.kind, ExprKind::ScalarVar(_)) {
8594 break;
8595 }
8596 if self.peek_line() > self.prev_line() {
8597 break;
8598 }
8599 let opener = self.peek().clone();
8600 let line = expr.line;
8601 let name = if let ExprKind::ScalarVar(ref n) = expr.kind {
8602 n.clone()
8603 } else {
8604 unreachable!()
8605 };
8606 self.advance(); self.suppress_tilde_range = self.suppress_tilde_range.saturating_add(1);
8612 let index_result = self.parse_expression();
8613 self.suppress_tilde_range = self.suppress_tilde_range.saturating_sub(1);
8614 let index = index_result?;
8615 let close_match = matches!(
8616 (&opener, self.peek()),
8617 (Token::LogNot, Token::LogNot) | (Token::BitNot, Token::BitNot)
8618 );
8619 if !close_match {
8620 let want = if matches!(opener, Token::LogNot) {
8621 "!"
8622 } else {
8623 "~"
8624 };
8625 return Err(self.syntax_err(
8626 format!("expected closing `{}` for string subscript", want),
8627 self.peek_line(),
8628 ));
8629 }
8630 self.advance(); expr = Expr {
8632 kind: ExprKind::ArrayElement {
8633 array: format!("__topicstr__{}", name),
8634 index: Box::new(index),
8635 },
8636 line,
8637 };
8638 }
8639 _ => break,
8640 }
8641 }
8642 Ok(expr)
8643 }
8644
8645 fn parse_primary(&mut self) -> PerlResult<Expr> {
8646 let line = self.peek_line();
8647 if let Token::Ident(ref kw) = self.peek().clone() {
8652 if matches!(kw.as_str(), "my" | "our" | "state" | "local") {
8653 let kw_owned = kw.clone();
8654 let saved_pos = self.pos;
8659 let stmt = self.parse_my_our_local(&kw_owned, false)?;
8660 let decls = match stmt.kind {
8661 StmtKind::My(d)
8662 | StmtKind::Our(d)
8663 | StmtKind::State(d)
8664 | StmtKind::Local(d) => d,
8665 _ => {
8666 self.pos = saved_pos;
8671 return Err(self.syntax_err(
8672 "`my`/`our`/`local` in expression must declare variables",
8673 line,
8674 ));
8675 }
8676 };
8677 return Ok(Expr {
8678 kind: ExprKind::MyExpr {
8679 keyword: kw_owned,
8680 decls,
8681 },
8682 line,
8683 });
8684 }
8685 }
8686 match self.peek().clone() {
8687 Token::Integer(n) => {
8688 self.advance();
8689 Ok(Expr {
8690 kind: ExprKind::Integer(n),
8691 line,
8692 })
8693 }
8694 Token::Float(f) => {
8695 self.advance();
8696 Ok(Expr {
8697 kind: ExprKind::Float(f),
8698 line,
8699 })
8700 }
8701 Token::ArrowBrace => {
8707 self.advance();
8708 let mut stmts = Vec::new();
8709 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
8710 if self.eat(&Token::Semicolon) {
8711 continue;
8712 }
8713 stmts.push(self.parse_statement()?);
8714 }
8715 self.expect(&Token::RBrace)?;
8716 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
8717 let inner = Expr {
8718 kind: ExprKind::CodeRef {
8719 params: vec![],
8720 body: stmts,
8721 },
8722 line: inner_line,
8723 };
8724 Ok(Expr {
8725 kind: ExprKind::Do(Box::new(inner)),
8726 line,
8727 })
8728 }
8729 Token::Star => {
8730 self.advance();
8731 if matches!(self.peek(), Token::LBrace) {
8732 self.advance();
8733 let inner = self.parse_expression()?;
8734 self.expect(&Token::RBrace)?;
8735 return Ok(Expr {
8736 kind: ExprKind::Deref {
8737 expr: Box::new(inner),
8738 kind: Sigil::Typeglob,
8739 },
8740 line,
8741 });
8742 }
8743 if matches!(
8745 self.peek(),
8746 Token::ScalarVar(_)
8747 | Token::ArrayVar(_)
8748 | Token::HashVar(_)
8749 | Token::DerefScalarVar(_)
8750 | Token::HashPercent
8751 ) {
8752 let inner = self.parse_postfix()?;
8753 return Ok(Expr {
8754 kind: ExprKind::TypeglobExpr(Box::new(inner)),
8755 line,
8756 });
8757 }
8758 let mut full_name = match self.advance() {
8760 (Token::Ident(n), _) => n,
8761 (Token::X, _) => "x".to_string(),
8762 (tok, l) => {
8763 return Err(self
8764 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
8765 }
8766 };
8767 while self.eat(&Token::PackageSep) {
8768 match self.advance() {
8769 (Token::Ident(part), _) => {
8770 full_name = format!("{}::{}", full_name, part);
8771 }
8772 (Token::X, _) => {
8773 full_name = format!("{}::x", full_name);
8774 }
8775 (tok, l) => {
8776 return Err(self.syntax_err(
8777 format!("Expected identifier after :: in typeglob, got {:?}", tok),
8778 l,
8779 ));
8780 }
8781 }
8782 }
8783 Ok(Expr {
8784 kind: ExprKind::Typeglob(full_name),
8785 line,
8786 })
8787 }
8788 Token::SingleString(s) => {
8789 self.advance();
8790 Ok(Expr {
8791 kind: ExprKind::String(s),
8792 line,
8793 })
8794 }
8795 Token::DoubleString(s) => {
8796 self.advance();
8797 self.parse_interpolated_string(&s, line)
8798 }
8799 Token::BacktickString(s) => {
8800 self.advance();
8801 let inner = self.parse_interpolated_string(&s, line)?;
8802 Ok(Expr {
8803 kind: ExprKind::Qx(Box::new(inner)),
8804 line,
8805 })
8806 }
8807 Token::HereDoc(_, body, interpolate) => {
8808 self.advance();
8809 if interpolate {
8810 self.parse_interpolated_string(&body, line)
8811 } else {
8812 Ok(Expr {
8813 kind: ExprKind::String(body),
8814 line,
8815 })
8816 }
8817 }
8818 Token::Regex(pattern, flags, _delim) => {
8819 self.advance();
8820 Ok(Expr {
8821 kind: ExprKind::Regex(pattern, flags),
8822 line,
8823 })
8824 }
8825 Token::QW(words) => {
8826 self.advance();
8827 self.list_construct_close_pos = Some(self.pos);
8830 Ok(Expr {
8831 kind: ExprKind::QW(words),
8832 line,
8833 })
8834 }
8835 Token::DerefScalarVar(name) => {
8836 self.advance();
8837 Ok(Expr {
8838 kind: ExprKind::Deref {
8839 expr: Box::new(Expr {
8840 kind: ExprKind::ScalarVar(name),
8841 line,
8842 }),
8843 kind: Sigil::Scalar,
8844 },
8845 line,
8846 })
8847 }
8848 Token::ScalarVar(name) => {
8849 self.advance();
8850 Ok(Expr {
8851 kind: ExprKind::ScalarVar(name),
8852 line,
8853 })
8854 }
8855 Token::ArrayVar(name) => {
8856 self.advance();
8857 match self.peek() {
8859 Token::LBracket => {
8860 self.advance();
8861 let indices = self.parse_slice_arg_list(false)?;
8862 self.expect(&Token::RBracket)?;
8863 Ok(Expr {
8864 kind: ExprKind::ArraySlice {
8865 array: name,
8866 indices,
8867 },
8868 line,
8869 })
8870 }
8871 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
8872 self.advance();
8873 let keys = self.parse_slice_arg_list(true)?;
8874 self.expect(&Token::RBrace)?;
8875 Ok(Expr {
8876 kind: ExprKind::HashSlice { hash: name, keys },
8877 line,
8878 })
8879 }
8880 _ => Ok(Expr {
8881 kind: ExprKind::ArrayVar(name),
8882 line,
8883 }),
8884 }
8885 }
8886 Token::HashVar(name) => {
8887 self.advance();
8888 if matches!(self.peek(), Token::LBrace) && self.suppress_scalar_hash_brace == 0 {
8893 self.advance(); let keys = self.parse_slice_arg_list(true)?;
8895 self.expect(&Token::RBrace)?;
8896 return Ok(Expr {
8897 kind: ExprKind::HashKvSlice { hash: name, keys },
8898 line,
8899 });
8900 }
8901 Ok(Expr {
8902 kind: ExprKind::HashVar(name),
8903 line,
8904 })
8905 }
8906 Token::HashPercent => {
8907 self.advance();
8909 if matches!(self.peek(), Token::ScalarVar(_)) {
8910 let n = match self.advance() {
8911 (Token::ScalarVar(n), _) => n,
8912 (tok, l) => {
8913 return Err(self.syntax_err(
8914 format!("Expected scalar variable after %%, got {:?}", tok),
8915 l,
8916 ));
8917 }
8918 };
8919 return Ok(Expr {
8920 kind: ExprKind::Deref {
8921 expr: Box::new(Expr {
8922 kind: ExprKind::ScalarVar(n),
8923 line,
8924 }),
8925 kind: Sigil::Hash,
8926 },
8927 line,
8928 });
8929 }
8930 if matches!(self.peek(), Token::LBracket) {
8935 self.advance();
8936 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
8937 self.expect(&Token::RBracket)?;
8938 let href = Expr {
8939 kind: ExprKind::HashRef(pairs),
8940 line,
8941 };
8942 return Ok(Expr {
8943 kind: ExprKind::Deref {
8944 expr: Box::new(href),
8945 kind: Sigil::Hash,
8946 },
8947 line,
8948 });
8949 }
8950 self.expect(&Token::LBrace)?;
8951 let looks_like_pair = matches!(
8957 self.peek(),
8958 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
8959 ) && matches!(self.peek_at(1), Token::FatArrow);
8960 let inner = if looks_like_pair {
8961 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
8962 Expr {
8963 kind: ExprKind::HashRef(pairs),
8964 line,
8965 }
8966 } else {
8967 self.parse_expression()?
8968 };
8969 self.expect(&Token::RBrace)?;
8970 Ok(Expr {
8971 kind: ExprKind::Deref {
8972 expr: Box::new(inner),
8973 kind: Sigil::Hash,
8974 },
8975 line,
8976 })
8977 }
8978 Token::ArrayAt => {
8979 self.advance();
8980 if matches!(self.peek(), Token::LBrace) {
8982 self.advance();
8983 let inner = self.parse_expression()?;
8984 self.expect(&Token::RBrace)?;
8985 return Ok(Expr {
8986 kind: ExprKind::Deref {
8987 expr: Box::new(inner),
8988 kind: Sigil::Array,
8989 },
8990 line,
8991 });
8992 }
8993 if matches!(self.peek(), Token::LBracket) {
8997 self.advance();
8998 let mut elems = Vec::new();
8999 if !matches!(self.peek(), Token::RBracket) {
9000 elems.push(self.parse_assign_expr()?);
9001 while self.eat(&Token::Comma) {
9002 if matches!(self.peek(), Token::RBracket) {
9003 break;
9004 }
9005 elems.push(self.parse_assign_expr()?);
9006 }
9007 }
9008 self.expect(&Token::RBracket)?;
9009 let aref = Expr {
9010 kind: ExprKind::ArrayRef(elems),
9011 line,
9012 };
9013 return Ok(Expr {
9014 kind: ExprKind::Deref {
9015 expr: Box::new(aref),
9016 kind: Sigil::Array,
9017 },
9018 line,
9019 });
9020 }
9021 let container = match self.peek().clone() {
9023 Token::ScalarVar(n) => {
9024 self.advance();
9025 Expr {
9026 kind: ExprKind::ScalarVar(n),
9027 line,
9028 }
9029 }
9030 _ => {
9031 return Err(self.syntax_err(
9032 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
9033 line,
9034 ));
9035 }
9036 };
9037 if matches!(self.peek(), Token::LBrace) {
9038 self.advance();
9039 let keys = self.parse_slice_arg_list(true)?;
9040 self.expect(&Token::RBrace)?;
9041 return Ok(Expr {
9042 kind: ExprKind::HashSliceDeref {
9043 container: Box::new(container),
9044 keys,
9045 },
9046 line,
9047 });
9048 }
9049 Ok(Expr {
9050 kind: ExprKind::Deref {
9051 expr: Box::new(container),
9052 kind: Sigil::Array,
9053 },
9054 line,
9055 })
9056 }
9057 Token::LParen => {
9058 self.advance();
9059 if matches!(self.peek(), Token::RParen) {
9060 self.advance();
9061 self.list_construct_close_pos = Some(self.pos);
9064 return Ok(Expr {
9065 kind: ExprKind::List(vec![]),
9066 line,
9067 });
9068 }
9069 let saved_no_pipe = self.no_pipe_forward_depth;
9072 self.no_pipe_forward_depth = 0;
9073 let expr = self.parse_expression();
9074 self.no_pipe_forward_depth = saved_no_pipe;
9075 let expr = expr?;
9076 self.expect(&Token::RParen)?;
9077 self.list_construct_close_pos = Some(self.pos);
9082 Ok(expr)
9083 }
9084 Token::LBracket => {
9085 self.advance();
9086 let elems = self.parse_arg_list()?;
9087 self.expect(&Token::RBracket)?;
9088 Ok(Expr {
9089 kind: ExprKind::ArrayRef(elems),
9090 line,
9091 })
9092 }
9093 Token::LBrace => {
9094 self.advance();
9096 let saved = self.pos;
9098 match self.try_parse_hash_ref() {
9099 Ok(pairs) => Ok(Expr {
9100 kind: ExprKind::HashRef(pairs),
9101 line,
9102 }),
9103 Err(_) => {
9104 self.pos = saved;
9105 let mut stmts = Vec::new();
9107 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9108 if self.eat(&Token::Semicolon) {
9109 continue;
9110 }
9111 stmts.push(self.parse_statement()?);
9112 }
9113 self.expect(&Token::RBrace)?;
9114 Ok(Expr {
9115 kind: ExprKind::CodeRef {
9116 params: vec![],
9117 body: stmts,
9118 },
9119 line,
9120 })
9121 }
9122 }
9123 }
9124 Token::Diamond => {
9125 self.advance();
9126 Ok(Expr {
9127 kind: ExprKind::ReadLine(None),
9128 line,
9129 })
9130 }
9131 Token::ReadLine(handle) => {
9132 self.advance();
9133 Ok(Expr {
9134 kind: ExprKind::ReadLine(Some(handle)),
9135 line,
9136 })
9137 }
9138
9139 Token::ThreadArrow => {
9141 self.advance();
9142 self.parse_thread_macro(line, false)
9143 }
9144 Token::ThreadArrowLast => {
9145 self.advance();
9146 self.parse_thread_macro(line, true)
9147 }
9148 Token::ThreadArrowStream => {
9149 self.advance();
9150 let mut stages = Vec::new();
9151 self.parse_thread_macro_inner(line, false, Some(&mut stages))
9152 }
9153 Token::ThreadArrowStreamLast => {
9154 self.advance();
9155 let mut stages = Vec::new();
9156 self.parse_thread_macro_inner(line, true, Some(&mut stages))
9157 }
9158 Token::ThreadArrowPar => {
9159 self.advance();
9160 self.parse_thread_macro_chunk_par(line, false)
9161 }
9162 Token::ThreadArrowParLast => {
9163 self.advance();
9164 self.parse_thread_macro_chunk_par(line, true)
9165 }
9166 Token::Ident(ref name) => {
9167 let name = name.clone();
9168 if name.starts_with('\x00') {
9170 self.advance();
9171 let parts: Vec<&str> = name.split('\x00').collect();
9172 if parts.len() >= 4 && parts[1] == "s" {
9173 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9174 return Ok(Expr {
9175 kind: ExprKind::Substitution {
9176 expr: Box::new(Expr {
9177 kind: ExprKind::ScalarVar("_".into()),
9178 line,
9179 }),
9180 pattern: parts[2].to_string(),
9181 replacement: parts[3].to_string(),
9182 flags: parts.get(4).unwrap_or(&"").to_string(),
9183 delim,
9184 },
9185 line,
9186 });
9187 }
9188 if parts.len() >= 4 && parts[1] == "tr" {
9189 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9190 return Ok(Expr {
9191 kind: ExprKind::Transliterate {
9192 expr: Box::new(Expr {
9193 kind: ExprKind::ScalarVar("_".into()),
9194 line,
9195 }),
9196 from: parts[2].to_string(),
9197 to: parts[3].to_string(),
9198 flags: parts.get(4).unwrap_or(&"").to_string(),
9199 delim,
9200 },
9201 line,
9202 });
9203 }
9204 return Err(self.syntax_err("Unexpected encoded token", line));
9205 }
9206 self.parse_named_expr(name)
9207 }
9208
9209 Token::Percent => {
9212 self.advance();
9213 match self.peek().clone() {
9214 Token::Ident(name) => {
9215 self.advance();
9216 Ok(Expr {
9217 kind: ExprKind::HashVar(name),
9218 line,
9219 })
9220 }
9221 Token::ScalarVar(n) => {
9222 self.advance();
9223 Ok(Expr {
9224 kind: ExprKind::Deref {
9225 expr: Box::new(Expr {
9226 kind: ExprKind::ScalarVar(n),
9227 line,
9228 }),
9229 kind: Sigil::Hash,
9230 },
9231 line,
9232 })
9233 }
9234 Token::LBrace => {
9235 self.advance();
9236 let looks_like_pair = matches!(
9237 self.peek(),
9238 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9239 ) && matches!(self.peek_at(1), Token::FatArrow);
9240 let inner = if looks_like_pair {
9241 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9242 Expr {
9243 kind: ExprKind::HashRef(pairs),
9244 line,
9245 }
9246 } else {
9247 self.parse_expression()?
9248 };
9249 self.expect(&Token::RBrace)?;
9250 Ok(Expr {
9251 kind: ExprKind::Deref {
9252 expr: Box::new(inner),
9253 kind: Sigil::Hash,
9254 },
9255 line,
9256 })
9257 }
9258 Token::LBracket => {
9259 self.advance();
9260 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9261 self.expect(&Token::RBracket)?;
9262 let href = Expr {
9263 kind: ExprKind::HashRef(pairs),
9264 line,
9265 };
9266 Ok(Expr {
9267 kind: ExprKind::Deref {
9268 expr: Box::new(href),
9269 kind: Sigil::Hash,
9270 },
9271 line,
9272 })
9273 }
9274 tok => Err(self.syntax_err(
9275 format!(
9276 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
9277 tok
9278 ),
9279 line,
9280 )),
9281 }
9282 }
9283
9284 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
9285 }
9286 }
9287
9288 fn parse_named_expr(&mut self, mut name: String) -> PerlResult<Expr> {
9289 let line = self.peek_line();
9290 self.advance(); while self.eat(&Token::PackageSep) {
9292 match self.advance() {
9293 (Token::Ident(part), _) => {
9294 name = format!("{}::{}", name, part);
9295 }
9296 (tok, err_line) => {
9297 return Err(self.syntax_err(
9298 format!("Expected identifier after `::`, got {:?}", tok),
9299 err_line,
9300 ));
9301 }
9302 }
9303 }
9304
9305 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name) {
9312 return Ok(Expr {
9313 kind: ExprKind::String(name),
9314 line,
9315 });
9316 }
9317
9318 if crate::compat_mode() {
9319 if let Some(ext) = Self::stryke_extension_name(&name) {
9320 if !self.declared_subs.contains(&name) {
9321 return Err(self.syntax_err(
9322 format!("`{ext}` is a stryke extension (disabled by --compat)"),
9323 line,
9324 ));
9325 }
9326 }
9327 }
9328
9329 if let Some(rest) = name.strip_prefix("CORE::") {
9336 name = rest.to_string();
9337 }
9338
9339 match name.as_str() {
9340 "__FILE__" => Ok(Expr {
9341 kind: ExprKind::MagicConst(MagicConstKind::File),
9342 line,
9343 }),
9344 "__LINE__" => Ok(Expr {
9345 kind: ExprKind::MagicConst(MagicConstKind::Line),
9346 line,
9347 }),
9348 "__SUB__" => Ok(Expr {
9349 kind: ExprKind::MagicConst(MagicConstKind::Sub),
9350 line,
9351 }),
9352 "stdin" => Ok(Expr {
9353 kind: ExprKind::FuncCall {
9354 name: "stdin".into(),
9355 args: vec![],
9356 },
9357 line,
9358 }),
9359 "range" => {
9360 let args = self.parse_builtin_args()?;
9361 Ok(Expr {
9362 kind: ExprKind::FuncCall {
9363 name: "range".into(),
9364 args,
9365 },
9366 line,
9367 })
9368 }
9369 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
9370 "say" => {
9371 if crate::no_interop_mode() {
9372 return Err(
9373 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
9374 );
9375 }
9376 self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a })
9377 }
9378 "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
9379 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
9380 "die" => {
9381 let args = self.parse_list_until_terminator()?;
9382 Ok(Expr {
9383 kind: ExprKind::Die(args),
9384 line,
9385 })
9386 }
9387 "warn" => {
9388 let args = self.parse_list_until_terminator()?;
9389 Ok(Expr {
9390 kind: ExprKind::Warn(args),
9391 line,
9392 })
9393 }
9394 "croak" | "confess" => {
9399 let args = self.parse_list_until_terminator()?;
9400 Ok(Expr {
9401 kind: ExprKind::Die(args),
9402 line,
9403 })
9404 }
9405 "carp" | "cluck" => {
9407 let args = self.parse_list_until_terminator()?;
9408 Ok(Expr {
9409 kind: ExprKind::Warn(args),
9410 line,
9411 })
9412 }
9413 "chomp" => {
9414 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9415 return Ok(e);
9416 }
9417 let a = self.parse_one_arg_or_default()?;
9418 Ok(Expr {
9419 kind: ExprKind::Chomp(Box::new(a)),
9420 line,
9421 })
9422 }
9423 "chop" => {
9424 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9425 return Ok(e);
9426 }
9427 let a = self.parse_one_arg_or_default()?;
9428 Ok(Expr {
9429 kind: ExprKind::Chop(Box::new(a)),
9430 line,
9431 })
9432 }
9433 "length" => {
9434 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9435 return Ok(e);
9436 }
9437 let a = self.parse_one_arg_or_default()?;
9438 Ok(Expr {
9439 kind: ExprKind::Length(Box::new(a)),
9440 line,
9441 })
9442 }
9443 "defined" => {
9444 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9445 return Ok(e);
9446 }
9447 let a = if matches!(
9455 self.peek(),
9456 Token::Semicolon
9457 | Token::RBrace
9458 | Token::RParen
9459 | Token::RBracket
9460 | Token::Eof
9461 | Token::Comma
9462 | Token::FatArrow
9463 | Token::PipeForward
9464 | Token::Question
9465 | Token::Colon
9466 | Token::NumEq
9467 | Token::NumNe
9468 | Token::NumLt
9469 | Token::NumGt
9470 | Token::NumLe
9471 | Token::NumGe
9472 | Token::Spaceship
9473 | Token::StrEq
9474 | Token::StrNe
9475 | Token::StrLt
9476 | Token::StrGt
9477 | Token::StrLe
9478 | Token::StrGe
9479 | Token::StrCmp
9480 | Token::LogAnd
9481 | Token::LogOr
9482 | Token::LogNot
9483 | Token::LogAndWord
9484 | Token::LogOrWord
9485 | Token::LogNotWord
9486 | Token::DefinedOr
9487 | Token::Range
9488 | Token::RangeExclusive
9489 | Token::Assign
9490 | Token::PlusAssign
9491 | Token::MinusAssign
9492 | Token::MulAssign
9493 | Token::DivAssign
9494 | Token::ModAssign
9495 | Token::PowAssign
9496 | Token::DotAssign
9497 | Token::AndAssign
9498 | Token::OrAssign
9499 | Token::XorAssign
9500 | Token::DefinedOrAssign
9501 | Token::ShiftLeftAssign
9502 | Token::ShiftRightAssign
9503 | Token::BitAndAssign
9504 | Token::BitOrAssign
9505 ) {
9506 Expr {
9507 kind: ExprKind::ScalarVar("_".into()),
9508 line: self.peek_line(),
9509 }
9510 } else if matches!(self.peek(), Token::LParen)
9511 && matches!(self.peek_at(1), Token::RParen)
9512 {
9513 let pl = self.peek_line();
9514 self.advance();
9515 self.advance();
9516 Expr {
9517 kind: ExprKind::ScalarVar("_".into()),
9518 line: pl,
9519 }
9520 } else {
9521 self.parse_named_unary_arg()?
9522 };
9523 Ok(Expr {
9524 kind: ExprKind::Defined(Box::new(a)),
9525 line,
9526 })
9527 }
9528 "ref" => {
9529 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9530 return Ok(e);
9531 }
9532 let a = self.parse_one_arg_or_default()?;
9533 Ok(Expr {
9534 kind: ExprKind::Ref(Box::new(a)),
9535 line,
9536 })
9537 }
9538 "undef" => {
9539 if self.peek_line() == self.prev_line()
9542 && matches!(
9543 self.peek(),
9544 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
9545 )
9546 {
9547 let target = self.parse_primary()?;
9548 return Ok(Expr {
9549 kind: ExprKind::Assign {
9550 target: Box::new(target),
9551 value: Box::new(Expr {
9552 kind: ExprKind::Undef,
9553 line,
9554 }),
9555 },
9556 line,
9557 });
9558 }
9559 Ok(Expr {
9560 kind: ExprKind::Undef,
9561 line,
9562 })
9563 }
9564 "scalar" => {
9565 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9566 return Ok(e);
9567 }
9568 if crate::no_interop_mode() {
9569 return Err(self.syntax_err(
9570 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
9571 line,
9572 ));
9573 }
9574 let a = self.parse_one_arg_or_default()?;
9575 Ok(Expr {
9576 kind: ExprKind::ScalarContext(Box::new(a)),
9577 line,
9578 })
9579 }
9580 "abs" => {
9581 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9582 return Ok(e);
9583 }
9584 let a = self.parse_one_arg_or_default()?;
9585 Ok(Expr {
9586 kind: ExprKind::Abs(Box::new(a)),
9587 line,
9588 })
9589 }
9590 "inc" | "dec" => {
9595 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9596 return Ok(e);
9597 }
9598 let a = self.parse_one_arg_or_default()?;
9599 Ok(Expr {
9600 kind: ExprKind::FuncCall {
9601 name,
9602 args: vec![a],
9603 },
9604 line,
9605 })
9606 }
9607 "int" => {
9608 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9609 return Ok(e);
9610 }
9611 let a = self.parse_one_arg_or_default()?;
9612 Ok(Expr {
9613 kind: ExprKind::Int(Box::new(a)),
9614 line,
9615 })
9616 }
9617 "sqrt" => {
9618 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9619 return Ok(e);
9620 }
9621 let a = self.parse_one_arg_or_default()?;
9622 Ok(Expr {
9623 kind: ExprKind::Sqrt(Box::new(a)),
9624 line,
9625 })
9626 }
9627 "sin" => {
9628 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9629 return Ok(e);
9630 }
9631 let a = self.parse_one_arg_or_default()?;
9632 Ok(Expr {
9633 kind: ExprKind::Sin(Box::new(a)),
9634 line,
9635 })
9636 }
9637 "cos" => {
9638 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9639 return Ok(e);
9640 }
9641 let a = self.parse_one_arg_or_default()?;
9642 Ok(Expr {
9643 kind: ExprKind::Cos(Box::new(a)),
9644 line,
9645 })
9646 }
9647 "atan2" => {
9648 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9649 return Ok(e);
9650 }
9651 let args = self.parse_builtin_args()?;
9652 if args.len() != 2 {
9653 return Err(self.syntax_err("atan2 requires two arguments", line));
9654 }
9655 Ok(Expr {
9656 kind: ExprKind::Atan2 {
9657 y: Box::new(args[0].clone()),
9658 x: Box::new(args[1].clone()),
9659 },
9660 line,
9661 })
9662 }
9663 "exp" => {
9664 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9665 return Ok(e);
9666 }
9667 let a = self.parse_one_arg_or_default()?;
9668 Ok(Expr {
9669 kind: ExprKind::Exp(Box::new(a)),
9670 line,
9671 })
9672 }
9673 "log" => {
9674 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9675 return Ok(e);
9676 }
9677 let a = self.parse_one_arg_or_default()?;
9678 Ok(Expr {
9679 kind: ExprKind::Log(Box::new(a)),
9680 line,
9681 })
9682 }
9683 "input" => {
9684 let args = if matches!(
9685 self.peek(),
9686 Token::Semicolon
9687 | Token::RBrace
9688 | Token::RParen
9689 | Token::Eof
9690 | Token::Comma
9691 | Token::PipeForward
9692 ) {
9693 vec![]
9694 } else if matches!(self.peek(), Token::LParen) {
9695 self.advance();
9696 if matches!(self.peek(), Token::RParen) {
9697 self.advance();
9698 vec![]
9699 } else {
9700 let a = self.parse_expression()?;
9701 self.expect(&Token::RParen)?;
9702 vec![a]
9703 }
9704 } else {
9705 let a = self.parse_one_arg()?;
9706 vec![a]
9707 };
9708 Ok(Expr {
9709 kind: ExprKind::FuncCall {
9710 name: "input".to_string(),
9711 args,
9712 },
9713 line,
9714 })
9715 }
9716 "rand" => {
9717 if matches!(
9718 self.peek(),
9719 Token::Semicolon
9720 | Token::RBrace
9721 | Token::RParen
9722 | Token::Eof
9723 | Token::Comma
9724 | Token::PipeForward
9725 ) {
9726 Ok(Expr {
9727 kind: ExprKind::Rand(None),
9728 line,
9729 })
9730 } else if matches!(self.peek(), Token::LParen) {
9731 self.advance();
9732 if matches!(self.peek(), Token::RParen) {
9733 self.advance();
9734 Ok(Expr {
9735 kind: ExprKind::Rand(None),
9736 line,
9737 })
9738 } else {
9739 let a = self.parse_expression()?;
9740 self.expect(&Token::RParen)?;
9741 Ok(Expr {
9742 kind: ExprKind::Rand(Some(Box::new(a))),
9743 line,
9744 })
9745 }
9746 } else {
9747 let a = self.parse_one_arg()?;
9748 Ok(Expr {
9749 kind: ExprKind::Rand(Some(Box::new(a))),
9750 line,
9751 })
9752 }
9753 }
9754 "srand" => {
9755 if matches!(
9756 self.peek(),
9757 Token::Semicolon
9758 | Token::RBrace
9759 | Token::RParen
9760 | Token::Eof
9761 | Token::Comma
9762 | Token::PipeForward
9763 ) {
9764 Ok(Expr {
9765 kind: ExprKind::Srand(None),
9766 line,
9767 })
9768 } else if matches!(self.peek(), Token::LParen) {
9769 self.advance();
9770 if matches!(self.peek(), Token::RParen) {
9771 self.advance();
9772 Ok(Expr {
9773 kind: ExprKind::Srand(None),
9774 line,
9775 })
9776 } else {
9777 let a = self.parse_expression()?;
9778 self.expect(&Token::RParen)?;
9779 Ok(Expr {
9780 kind: ExprKind::Srand(Some(Box::new(a))),
9781 line,
9782 })
9783 }
9784 } else {
9785 let a = self.parse_one_arg()?;
9786 Ok(Expr {
9787 kind: ExprKind::Srand(Some(Box::new(a))),
9788 line,
9789 })
9790 }
9791 }
9792 "hex" => {
9793 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9794 return Ok(e);
9795 }
9796 let a = self.parse_one_arg_or_default()?;
9797 Ok(Expr {
9798 kind: ExprKind::Hex(Box::new(a)),
9799 line,
9800 })
9801 }
9802 "oct" => {
9803 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9804 return Ok(e);
9805 }
9806 let a = self.parse_one_arg_or_default()?;
9807 Ok(Expr {
9808 kind: ExprKind::Oct(Box::new(a)),
9809 line,
9810 })
9811 }
9812 "chr" => {
9813 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9814 return Ok(e);
9815 }
9816 let a = self.parse_one_arg_or_default()?;
9817 Ok(Expr {
9818 kind: ExprKind::Chr(Box::new(a)),
9819 line,
9820 })
9821 }
9822 "ord" => {
9823 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9824 return Ok(e);
9825 }
9826 let a = self.parse_one_arg_or_default()?;
9827 Ok(Expr {
9828 kind: ExprKind::Ord(Box::new(a)),
9829 line,
9830 })
9831 }
9832 "lc" => {
9833 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9834 return Ok(e);
9835 }
9836 let a = self.parse_one_arg_or_default()?;
9837 Ok(Expr {
9838 kind: ExprKind::Lc(Box::new(a)),
9839 line,
9840 })
9841 }
9842 "uc" => {
9843 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9844 return Ok(e);
9845 }
9846 let a = self.parse_one_arg_or_default()?;
9847 Ok(Expr {
9848 kind: ExprKind::Uc(Box::new(a)),
9849 line,
9850 })
9851 }
9852 "lcfirst" => {
9853 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9854 return Ok(e);
9855 }
9856 let a = self.parse_one_arg_or_default()?;
9857 Ok(Expr {
9858 kind: ExprKind::Lcfirst(Box::new(a)),
9859 line,
9860 })
9861 }
9862 "ucfirst" => {
9863 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9864 return Ok(e);
9865 }
9866 let a = self.parse_one_arg_or_default()?;
9867 Ok(Expr {
9868 kind: ExprKind::Ucfirst(Box::new(a)),
9869 line,
9870 })
9871 }
9872 "fc" => {
9873 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9874 return Ok(e);
9875 }
9876 let a = self.parse_one_arg_or_default()?;
9877 Ok(Expr {
9878 kind: ExprKind::Fc(Box::new(a)),
9879 line,
9880 })
9881 }
9882 "crypt" => {
9883 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9884 return Ok(e);
9885 }
9886 let args = self.parse_builtin_args()?;
9887 if args.len() != 2 {
9888 return Err(self.syntax_err("crypt requires two arguments", line));
9889 }
9890 Ok(Expr {
9891 kind: ExprKind::Crypt {
9892 plaintext: Box::new(args[0].clone()),
9893 salt: Box::new(args[1].clone()),
9894 },
9895 line,
9896 })
9897 }
9898 "pos" => {
9899 if matches!(
9900 self.peek(),
9901 Token::Semicolon
9902 | Token::RBrace
9903 | Token::RParen
9904 | Token::Eof
9905 | Token::Comma
9906 | Token::PipeForward
9907 ) {
9908 Ok(Expr {
9909 kind: ExprKind::Pos(None),
9910 line,
9911 })
9912 } else if matches!(self.peek(), Token::Assign) {
9913 self.advance();
9915 let rhs = self.parse_assign_expr()?;
9916 Ok(Expr {
9917 kind: ExprKind::Assign {
9918 target: Box::new(Expr {
9919 kind: ExprKind::Pos(Some(Box::new(Expr {
9920 kind: ExprKind::ScalarVar("_".into()),
9921 line,
9922 }))),
9923 line,
9924 }),
9925 value: Box::new(rhs),
9926 },
9927 line,
9928 })
9929 } else if matches!(self.peek(), Token::LParen) {
9930 self.advance();
9931 if matches!(self.peek(), Token::RParen) {
9932 self.advance();
9933 Ok(Expr {
9934 kind: ExprKind::Pos(None),
9935 line,
9936 })
9937 } else {
9938 let a = self.parse_expression()?;
9939 self.expect(&Token::RParen)?;
9940 Ok(Expr {
9941 kind: ExprKind::Pos(Some(Box::new(a))),
9942 line,
9943 })
9944 }
9945 } else {
9946 let saved = self.pos;
9947 let subj = self.parse_unary()?;
9948 if matches!(self.peek(), Token::Assign) {
9949 self.advance();
9950 let rhs = self.parse_assign_expr()?;
9951 Ok(Expr {
9952 kind: ExprKind::Assign {
9953 target: Box::new(Expr {
9954 kind: ExprKind::Pos(Some(Box::new(subj))),
9955 line,
9956 }),
9957 value: Box::new(rhs),
9958 },
9959 line,
9960 })
9961 } else {
9962 self.pos = saved;
9963 let a = self.parse_one_arg()?;
9964 Ok(Expr {
9965 kind: ExprKind::Pos(Some(Box::new(a))),
9966 line,
9967 })
9968 }
9969 }
9970 }
9971 "study" => {
9972 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9973 return Ok(e);
9974 }
9975 let a = self.parse_one_arg_or_default()?;
9976 Ok(Expr {
9977 kind: ExprKind::Study(Box::new(a)),
9978 line,
9979 })
9980 }
9981 "push" => {
9982 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9983 return Ok(e);
9984 }
9985 let args = self.parse_builtin_args()?;
9986 let (first, rest) = args
9987 .split_first()
9988 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
9989 if matches!(
9994 first.kind,
9995 ExprKind::ScalarVar(_)
9996 | ExprKind::Integer(_)
9997 | ExprKind::Float(_)
9998 | ExprKind::String(_)
9999 ) {
10000 return Err(self
10001 .syntax_err("Experimental push on scalar is now forbidden", line)
10002 .with_near("at EOF"));
10003 }
10004 Ok(Expr {
10005 kind: ExprKind::Push {
10006 array: Box::new(first.clone()),
10007 values: rest.to_vec(),
10008 },
10009 line,
10010 })
10011 }
10012 "pop" => {
10013 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10014 return Ok(e);
10015 }
10016 let a = self.parse_one_arg_or_argv()?;
10017 Ok(Expr {
10018 kind: ExprKind::Pop(Box::new(a)),
10019 line,
10020 })
10021 }
10022 "shift" => {
10023 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10024 return Ok(e);
10025 }
10026 let a = self.parse_one_arg_or_argv()?;
10027 Ok(Expr {
10028 kind: ExprKind::Shift(Box::new(a)),
10029 line,
10030 })
10031 }
10032 "unshift" => {
10033 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10034 return Ok(e);
10035 }
10036 let args = self.parse_builtin_args()?;
10037 let (first, rest) = args
10038 .split_first()
10039 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
10040 Ok(Expr {
10041 kind: ExprKind::Unshift {
10042 array: Box::new(first.clone()),
10043 values: rest.to_vec(),
10044 },
10045 line,
10046 })
10047 }
10048 "splice" => {
10049 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10050 return Ok(e);
10051 }
10052 let args = self.parse_builtin_args()?;
10053 let mut iter = args.into_iter();
10054 let array = Box::new(
10055 iter.next()
10056 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
10057 );
10058 let offset = iter.next().map(Box::new);
10059 let length = iter.next().map(Box::new);
10060 let replacement: Vec<Expr> = iter.collect();
10061 Ok(Expr {
10062 kind: ExprKind::Splice {
10063 array,
10064 offset,
10065 length,
10066 replacement,
10067 },
10068 line,
10069 })
10070 }
10071 "splice_last" | "splice1" | "spl_last" => {
10076 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10077 return Ok(e);
10078 }
10079 let args = self.parse_builtin_args()?;
10080 let mut iter = args.into_iter();
10081 let array = Box::new(
10082 iter.next()
10083 .ok_or_else(|| self.syntax_err("splice_last requires arguments", line))?,
10084 );
10085 let offset = iter.next().map(Box::new);
10086 let length = iter.next().map(Box::new);
10087 let replacement: Vec<Expr> = iter.collect();
10088 let splice_expr = Expr {
10089 kind: ExprKind::Splice {
10090 array,
10091 offset,
10092 length,
10093 replacement,
10094 },
10095 line,
10096 };
10097 Ok(Expr {
10098 kind: ExprKind::FuncCall {
10099 name: "tail".to_string(),
10100 args: vec![splice_expr],
10101 },
10102 line,
10103 })
10104 }
10105 "delete" => {
10106 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10107 return Ok(e);
10108 }
10109 let a = self.parse_postfix()?;
10110 Ok(Expr {
10111 kind: ExprKind::Delete(Box::new(a)),
10112 line,
10113 })
10114 }
10115 "exists" => {
10116 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10117 return Ok(e);
10118 }
10119 let a = self.parse_postfix()?;
10120 Ok(Expr {
10121 kind: ExprKind::Exists(Box::new(a)),
10122 line,
10123 })
10124 }
10125 "keys" => {
10126 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10127 return Ok(e);
10128 }
10129 let a = self.parse_one_arg_or_default()?;
10130 Ok(Expr {
10131 kind: ExprKind::Keys(Box::new(a)),
10132 line,
10133 })
10134 }
10135 "values" => {
10136 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10137 return Ok(e);
10138 }
10139 let a = self.parse_one_arg_or_default()?;
10140 Ok(Expr {
10141 kind: ExprKind::Values(Box::new(a)),
10142 line,
10143 })
10144 }
10145 "each" => {
10146 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10147 return Ok(e);
10148 }
10149 let a = self.parse_one_arg_or_default()?;
10150 Ok(Expr {
10151 kind: ExprKind::Each(Box::new(a)),
10152 line,
10153 })
10154 }
10155 "fore" | "e" | "ep" => {
10156 if matches!(self.peek(), Token::LBrace) {
10158 let (block, list) = self.parse_block_list()?;
10159 Ok(Expr {
10160 kind: ExprKind::ForEachExpr {
10161 block,
10162 list: Box::new(list),
10163 },
10164 line,
10165 })
10166 } else if self.in_pipe_rhs() {
10167 let is_terminal = matches!(
10170 self.peek(),
10171 Token::Semicolon
10172 | Token::RParen
10173 | Token::Eof
10174 | Token::PipeForward
10175 | Token::RBrace
10176 );
10177 let block = if name == "ep" && is_terminal {
10178 vec![Statement {
10179 label: None,
10180 kind: StmtKind::Expression(Expr {
10181 kind: ExprKind::Say {
10182 handle: None,
10183 args: vec![Expr {
10184 kind: ExprKind::ScalarVar("_".into()),
10185 line,
10186 }],
10187 },
10188 line,
10189 }),
10190 line,
10191 }]
10192 } else {
10193 let expr = self.parse_assign_expr_stop_at_pipe()?;
10194 let expr = Self::lift_bareword_to_topic_call(expr);
10195 vec![Statement {
10196 label: None,
10197 kind: StmtKind::Expression(expr),
10198 line,
10199 }]
10200 };
10201 let list = self.pipe_placeholder_list(line);
10202 Ok(Expr {
10203 kind: ExprKind::ForEachExpr {
10204 block,
10205 list: Box::new(list),
10206 },
10207 line,
10208 })
10209 } else {
10210 let expr = self.parse_assign_expr()?;
10219 let expr = Self::lift_bareword_to_topic_call(expr);
10220 if !matches!(self.peek(), Token::Comma) && name == "ep" {
10221 let block = vec![Statement {
10222 label: None,
10223 kind: StmtKind::Expression(Expr {
10224 kind: ExprKind::Say {
10225 handle: None,
10226 args: vec![Expr {
10227 kind: ExprKind::ScalarVar("_".into()),
10228 line,
10229 }],
10230 },
10231 line,
10232 }),
10233 line,
10234 }];
10235 return Ok(Expr {
10236 kind: ExprKind::ForEachExpr {
10237 block,
10238 list: Box::new(expr),
10239 },
10240 line,
10241 });
10242 }
10243 self.expect(&Token::Comma)?;
10244 let list_parts = self.parse_list_until_terminator()?;
10245 let list_expr = if list_parts.len() == 1 {
10246 list_parts.into_iter().next().unwrap()
10247 } else {
10248 Expr {
10249 kind: ExprKind::List(list_parts),
10250 line,
10251 }
10252 };
10253 let block = vec![Statement {
10254 label: None,
10255 kind: StmtKind::Expression(expr),
10256 line,
10257 }];
10258 Ok(Expr {
10259 kind: ExprKind::ForEachExpr {
10260 block,
10261 list: Box::new(list_expr),
10262 },
10263 line,
10264 })
10265 }
10266 }
10267 "rev" => {
10268 let prev = self.prev_line();
10274 let a = if self.in_pipe_rhs()
10275 && (matches!(
10276 self.peek(),
10277 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
10278 ) || self.peek_line() > prev)
10279 {
10280 self.pipe_placeholder_list(line)
10281 } else if self.peek_line() > prev {
10282 Expr {
10288 kind: ExprKind::ScalarVar("_".into()),
10289 line: prev,
10290 }
10291 } else if matches!(
10292 self.peek(),
10293 Token::Semicolon
10294 | Token::RBrace
10295 | Token::RParen
10296 | Token::RBracket
10297 | Token::Eof
10298 | Token::Comma
10299 | Token::FatArrow
10300 | Token::PipeForward
10301 ) {
10302 Expr {
10303 kind: ExprKind::ScalarVar("_".into()),
10304 line: self.peek_line(),
10305 }
10306 } else if matches!(self.peek(), Token::LParen)
10307 && matches!(self.peek_at(1), Token::RParen)
10308 {
10309 let pl = self.peek_line();
10312 self.advance(); self.advance(); Expr {
10315 kind: ExprKind::ScalarVar("_".into()),
10316 line: pl,
10317 }
10318 } else {
10319 self.parse_one_arg()?
10320 };
10321 Ok(Expr {
10322 kind: ExprKind::Rev(Box::new(a)),
10323 line,
10324 })
10325 }
10326 "reverse" => {
10327 if crate::no_interop_mode() {
10328 return Err(self.syntax_err(
10329 "stryke uses `rev` instead of `reverse` (--no-interop)",
10330 line,
10331 ));
10332 }
10333 let a = if self.in_pipe_rhs()
10335 && matches!(
10336 self.peek(),
10337 Token::Semicolon
10338 | Token::RBrace
10339 | Token::RParen
10340 | Token::Eof
10341 | Token::PipeForward
10342 ) {
10343 self.pipe_placeholder_list(line)
10344 } else {
10345 self.parse_one_arg()?
10346 };
10347 Ok(Expr {
10348 kind: ExprKind::ReverseExpr(Box::new(a)),
10349 line,
10350 })
10351 }
10352 "reversed" | "rv" => {
10353 let a = if self.in_pipe_rhs()
10355 && matches!(
10356 self.peek(),
10357 Token::Semicolon
10358 | Token::RBrace
10359 | Token::RParen
10360 | Token::Eof
10361 | Token::PipeForward
10362 ) {
10363 self.pipe_placeholder_list(line)
10364 } else {
10365 self.parse_one_arg()?
10366 };
10367 Ok(Expr {
10368 kind: ExprKind::Rev(Box::new(a)),
10369 line,
10370 })
10371 }
10372 "join" => {
10373 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10374 return Ok(e);
10375 }
10376 let args = self.parse_builtin_args()?;
10377 if args.is_empty() {
10378 return Err(self.syntax_err("join requires separator and list", line));
10379 }
10380 if args.len() < 2 && !self.in_pipe_rhs() {
10382 return Err(self.syntax_err("join requires separator and list", line));
10383 }
10384 Ok(Expr {
10385 kind: ExprKind::JoinExpr {
10386 separator: Box::new(args[0].clone()),
10387 list: Box::new(Expr {
10388 kind: ExprKind::List(args[1..].to_vec()),
10389 line,
10390 }),
10391 },
10392 line,
10393 })
10394 }
10395 "split" => {
10396 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10397 return Ok(e);
10398 }
10399 let args = self.parse_builtin_args()?;
10400 let pattern = args.first().cloned().unwrap_or(Expr {
10401 kind: ExprKind::String(" ".into()),
10402 line,
10403 });
10404 let string = args.get(1).cloned().unwrap_or(Expr {
10405 kind: ExprKind::ScalarVar("_".into()),
10406 line,
10407 });
10408 let limit = args.get(2).cloned().map(Box::new);
10409 Ok(Expr {
10410 kind: ExprKind::SplitExpr {
10411 pattern: Box::new(pattern),
10412 string: Box::new(string),
10413 limit,
10414 },
10415 line,
10416 })
10417 }
10418 "substr" => {
10419 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10420 return Ok(e);
10421 }
10422 let args = self.parse_builtin_args()?;
10423 Ok(Expr {
10424 kind: ExprKind::Substr {
10425 string: Box::new(args[0].clone()),
10426 offset: Box::new(args[1].clone()),
10427 length: args.get(2).cloned().map(Box::new),
10428 replacement: args.get(3).cloned().map(Box::new),
10429 },
10430 line,
10431 })
10432 }
10433 "index" => {
10434 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10435 return Ok(e);
10436 }
10437 let args = self.parse_builtin_args()?;
10438 Ok(Expr {
10439 kind: ExprKind::Index {
10440 string: Box::new(args[0].clone()),
10441 substr: Box::new(args[1].clone()),
10442 position: args.get(2).cloned().map(Box::new),
10443 },
10444 line,
10445 })
10446 }
10447 "rindex" => {
10448 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10449 return Ok(e);
10450 }
10451 let args = self.parse_builtin_args()?;
10452 Ok(Expr {
10453 kind: ExprKind::Rindex {
10454 string: Box::new(args[0].clone()),
10455 substr: Box::new(args[1].clone()),
10456 position: args.get(2).cloned().map(Box::new),
10457 },
10458 line,
10459 })
10460 }
10461 "sprintf" => {
10462 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10463 return Ok(e);
10464 }
10465 let args = self.parse_builtin_args()?;
10466 let (first, rest) = args
10467 .split_first()
10468 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
10469 Ok(Expr {
10470 kind: ExprKind::Sprintf {
10471 format: Box::new(first.clone()),
10472 args: rest.to_vec(),
10473 },
10474 line,
10475 })
10476 }
10477 "map" | "flat_map" | "maps" | "flat_maps" => {
10478 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
10479 let stream = matches!(name.as_str(), "maps" | "flat_maps");
10480 if matches!(self.peek(), Token::LBrace) {
10481 let (block, list) = self.parse_block_list()?;
10482 Ok(Expr {
10483 kind: ExprKind::MapExpr {
10484 block,
10485 list: Box::new(list),
10486 flatten_array_refs,
10487 stream,
10488 },
10489 line,
10490 })
10491 } else {
10492 let expr = self.parse_assign_expr_stop_at_pipe()?;
10493 let expr = Self::lift_bareword_to_topic_call(expr);
10496 let list_expr = if self.pipe_supplies_slurped_list_operand() {
10497 self.pipe_placeholder_list(line)
10498 } else {
10499 self.expect(&Token::Comma)?;
10500 let list_parts = self.parse_list_until_terminator()?;
10501 if list_parts.len() == 1 {
10502 list_parts.into_iter().next().unwrap()
10503 } else {
10504 Expr {
10505 kind: ExprKind::List(list_parts),
10506 line,
10507 }
10508 }
10509 };
10510 Ok(Expr {
10511 kind: ExprKind::MapExprComma {
10512 expr: Box::new(expr),
10513 list: Box::new(list_expr),
10514 flatten_array_refs,
10515 stream,
10516 },
10517 line,
10518 })
10519 }
10520 }
10521 "cond" => {
10522 if crate::compat_mode() {
10523 return Err(self
10524 .syntax_err("`cond` is a stryke extension (disabled by --compat)", line));
10525 }
10526 self.parse_cond_expr(line)
10527 }
10528 "match" => {
10529 if crate::compat_mode() {
10530 return Err(self.syntax_err(
10531 "algebraic `match` is a stryke extension (disabled by --compat)",
10532 line,
10533 ));
10534 }
10535 self.parse_algebraic_match_expr(line)
10536 }
10537 "grep" | "greps" | "filter" | "fi" | "find_all" => {
10538 let keyword = match name.as_str() {
10539 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
10540 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
10541 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
10542 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
10543 _ => unreachable!(),
10544 };
10545 if matches!(self.peek(), Token::LBrace) {
10546 let (block, list) = self.parse_block_list()?;
10547 Ok(Expr {
10548 kind: ExprKind::GrepExpr {
10549 block,
10550 list: Box::new(list),
10551 keyword,
10552 },
10553 line,
10554 })
10555 } else {
10556 let expr = self.parse_assign_expr_stop_at_pipe()?;
10557 if self.pipe_supplies_slurped_list_operand() {
10558 let list = self.pipe_placeholder_list(line);
10563 let topic = Expr {
10564 kind: ExprKind::ScalarVar("_".into()),
10565 line,
10566 };
10567 let test = match &expr.kind {
10568 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
10569 kind: ExprKind::BinOp {
10570 op: BinOp::NumEq,
10571 left: Box::new(topic),
10572 right: Box::new(expr),
10573 },
10574 line,
10575 },
10576 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
10577 kind: ExprKind::BinOp {
10578 op: BinOp::StrEq,
10579 left: Box::new(topic),
10580 right: Box::new(expr),
10581 },
10582 line,
10583 },
10584 ExprKind::Regex { .. } => Expr {
10585 kind: ExprKind::BinOp {
10586 op: BinOp::BindMatch,
10587 left: Box::new(topic),
10588 right: Box::new(expr),
10589 },
10590 line,
10591 },
10592 _ => {
10593 let expr = Self::lift_bareword_to_topic_call(expr);
10599 return Ok(Expr {
10600 kind: ExprKind::GrepExprComma {
10601 expr: Box::new(expr),
10602 list: Box::new(list),
10603 keyword,
10604 },
10605 line,
10606 });
10607 }
10608 };
10609 let block = vec![Statement {
10610 label: None,
10611 kind: StmtKind::Expression(test),
10612 line,
10613 }];
10614 Ok(Expr {
10615 kind: ExprKind::GrepExpr {
10616 block,
10617 list: Box::new(list),
10618 keyword,
10619 },
10620 line,
10621 })
10622 } else {
10623 let expr = Self::lift_bareword_to_topic_call(expr);
10624 self.expect(&Token::Comma)?;
10625 let list_parts = self.parse_list_until_terminator()?;
10626 let list_expr = if list_parts.len() == 1 {
10627 list_parts.into_iter().next().unwrap()
10628 } else {
10629 Expr {
10630 kind: ExprKind::List(list_parts),
10631 line,
10632 }
10633 };
10634 Ok(Expr {
10635 kind: ExprKind::GrepExprComma {
10636 expr: Box::new(expr),
10637 list: Box::new(list_expr),
10638 keyword,
10639 },
10640 line,
10641 })
10642 }
10643 }
10644 }
10645 "sort" => {
10646 use crate::ast::SortComparator;
10647 if matches!(self.peek(), Token::LBrace) {
10648 let block = self.parse_block()?;
10649 let block_end_line = self.prev_line();
10650 let _ = self.eat(&Token::Comma);
10651 let list = if self.in_pipe_rhs()
10652 && (matches!(
10653 self.peek(),
10654 Token::Semicolon
10655 | Token::RBrace
10656 | Token::RParen
10657 | Token::Eof
10658 | Token::PipeForward
10659 ) || self.peek_line() > block_end_line)
10660 {
10661 self.pipe_placeholder_list(line)
10662 } else {
10663 self.parse_expression()?
10664 };
10665 Ok(Expr {
10666 kind: ExprKind::SortExpr {
10667 cmp: Some(SortComparator::Block(block)),
10668 list: Box::new(list),
10669 },
10670 line,
10671 })
10672 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
10673 let block = self.parse_block_or_bareword_cmp_block()?;
10675 let _ = self.eat(&Token::Comma);
10676 let list = if self.in_pipe_rhs()
10677 && matches!(
10678 self.peek(),
10679 Token::Semicolon
10680 | Token::RBrace
10681 | Token::RParen
10682 | Token::Eof
10683 | Token::PipeForward
10684 ) {
10685 self.pipe_placeholder_list(line)
10686 } else {
10687 self.parse_expression()?
10688 };
10689 Ok(Expr {
10690 kind: ExprKind::SortExpr {
10691 cmp: Some(SortComparator::Block(block)),
10692 list: Box::new(list),
10693 },
10694 line,
10695 })
10696 } else if matches!(self.peek(), Token::ScalarVar(_)) {
10697 self.suppress_indirect_paren_call =
10700 self.suppress_indirect_paren_call.saturating_add(1);
10701 let code = self.parse_assign_expr()?;
10702 self.suppress_indirect_paren_call =
10703 self.suppress_indirect_paren_call.saturating_sub(1);
10704 let _ = self.eat(&Token::Comma);
10705 let list = if self.in_pipe_rhs()
10706 && matches!(
10707 self.peek(),
10708 Token::Semicolon
10709 | Token::RBrace
10710 | Token::RParen
10711 | Token::Eof
10712 | Token::PipeForward
10713 ) {
10714 self.pipe_placeholder_list(line)
10715 } else if matches!(self.peek(), Token::LParen) {
10716 self.advance();
10717 let e = self.parse_expression()?;
10718 self.expect(&Token::RParen)?;
10719 e
10720 } else {
10721 self.parse_expression()?
10722 };
10723 Ok(Expr {
10724 kind: ExprKind::SortExpr {
10725 cmp: Some(SortComparator::Code(Box::new(code))),
10726 list: Box::new(list),
10727 },
10728 line,
10729 })
10730 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
10731 {
10732 let block = self.parse_block_or_bareword_cmp_block()?;
10734 let _ = self.eat(&Token::Comma);
10735 let list = if self.in_pipe_rhs()
10736 && matches!(
10737 self.peek(),
10738 Token::Semicolon
10739 | Token::RBrace
10740 | Token::RParen
10741 | Token::Eof
10742 | Token::PipeForward
10743 ) {
10744 self.pipe_placeholder_list(line)
10745 } else {
10746 self.parse_expression()?
10747 };
10748 Ok(Expr {
10749 kind: ExprKind::SortExpr {
10750 cmp: Some(SortComparator::Block(block)),
10751 list: Box::new(list),
10752 },
10753 line,
10754 })
10755 } else {
10756 let list = if self.in_pipe_rhs()
10759 && matches!(
10760 self.peek(),
10761 Token::Semicolon
10762 | Token::RBrace
10763 | Token::RParen
10764 | Token::Eof
10765 | Token::PipeForward
10766 ) {
10767 self.pipe_placeholder_list(line)
10768 } else {
10769 self.parse_expression()?
10770 };
10771 Ok(Expr {
10772 kind: ExprKind::SortExpr {
10773 cmp: None,
10774 list: Box::new(list),
10775 },
10776 line,
10777 })
10778 }
10779 }
10780 "reduce" | "fold" | "inject" => {
10781 let (block, list) = self.parse_block_list()?;
10782 Ok(Expr {
10783 kind: ExprKind::ReduceExpr {
10784 block,
10785 list: Box::new(list),
10786 },
10787 line,
10788 })
10789 }
10790 "pmap" => {
10792 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10793 Ok(Expr {
10794 kind: ExprKind::PMapExpr {
10795 block,
10796 list: Box::new(list),
10797 progress: progress.map(Box::new),
10798 flat_outputs: false,
10799 on_cluster: None,
10800 stream: false,
10801 },
10802 line,
10803 })
10804 }
10805 "pmap_on" => {
10806 let (cluster, block, list, progress) =
10807 self.parse_cluster_block_then_list_optional_progress()?;
10808 Ok(Expr {
10809 kind: ExprKind::PMapExpr {
10810 block,
10811 list: Box::new(list),
10812 progress: progress.map(Box::new),
10813 flat_outputs: false,
10814 on_cluster: Some(Box::new(cluster)),
10815 stream: false,
10816 },
10817 line,
10818 })
10819 }
10820 "pflat_map" => {
10821 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10822 Ok(Expr {
10823 kind: ExprKind::PMapExpr {
10824 block,
10825 list: Box::new(list),
10826 progress: progress.map(Box::new),
10827 flat_outputs: true,
10828 on_cluster: None,
10829 stream: false,
10830 },
10831 line,
10832 })
10833 }
10834 "pflat_map_on" => {
10835 let (cluster, block, list, progress) =
10836 self.parse_cluster_block_then_list_optional_progress()?;
10837 Ok(Expr {
10838 kind: ExprKind::PMapExpr {
10839 block,
10840 list: Box::new(list),
10841 progress: progress.map(Box::new),
10842 flat_outputs: true,
10843 on_cluster: Some(Box::new(cluster)),
10844 stream: false,
10845 },
10846 line,
10847 })
10848 }
10849 "pmaps" => {
10850 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10851 Ok(Expr {
10852 kind: ExprKind::PMapExpr {
10853 block,
10854 list: Box::new(list),
10855 progress: progress.map(Box::new),
10856 flat_outputs: false,
10857 on_cluster: None,
10858 stream: true,
10859 },
10860 line,
10861 })
10862 }
10863 "pflat_maps" => {
10864 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10865 Ok(Expr {
10866 kind: ExprKind::PMapExpr {
10867 block,
10868 list: Box::new(list),
10869 progress: progress.map(Box::new),
10870 flat_outputs: true,
10871 on_cluster: None,
10872 stream: true,
10873 },
10874 line,
10875 })
10876 }
10877 "pgreps" => {
10878 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10879 Ok(Expr {
10880 kind: ExprKind::PGrepExpr {
10881 block,
10882 list: Box::new(list),
10883 progress: progress.map(Box::new),
10884 stream: true,
10885 },
10886 line,
10887 })
10888 }
10889 "pmap_chunked" => {
10890 let chunk_size = self.parse_assign_expr()?;
10891 let block = self.parse_block_or_bareword_block()?;
10892 self.eat(&Token::Comma);
10893 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
10894 Ok(Expr {
10895 kind: ExprKind::PMapChunkedExpr {
10896 chunk_size: Box::new(chunk_size),
10897 block,
10898 list: Box::new(list),
10899 progress: progress.map(Box::new),
10900 },
10901 line,
10902 })
10903 }
10904 "pgrep" => {
10905 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10906 Ok(Expr {
10907 kind: ExprKind::PGrepExpr {
10908 block,
10909 list: Box::new(list),
10910 progress: progress.map(Box::new),
10911 stream: false,
10912 },
10913 line,
10914 })
10915 }
10916 "pfor" => {
10917 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
10918 Ok(Expr {
10919 kind: ExprKind::PForExpr {
10920 block,
10921 list: Box::new(list),
10922 progress: progress.map(Box::new),
10923 },
10924 line,
10925 })
10926 }
10927 "par_lines" | "par_walk" => {
10928 let args = self.parse_builtin_args()?;
10929 if args.len() < 2 {
10930 return Err(
10931 self.syntax_err(format!("{} requires at least two arguments", name), line)
10932 );
10933 }
10934
10935 if name == "par_lines" {
10936 Ok(Expr {
10937 kind: ExprKind::ParLinesExpr {
10938 path: Box::new(args[0].clone()),
10939 callback: Box::new(args[1].clone()),
10940 progress: None,
10941 },
10942 line,
10943 })
10944 } else {
10945 Ok(Expr {
10946 kind: ExprKind::ParWalkExpr {
10947 path: Box::new(args[0].clone()),
10948 callback: Box::new(args[1].clone()),
10949 progress: None,
10950 },
10951 line,
10952 })
10953 }
10954 }
10955 "pwatch" | "watch" => {
10956 let args = self.parse_builtin_args()?;
10957 if args.len() < 2 {
10958 return Err(
10959 self.syntax_err(format!("{} requires at least two arguments", name), line)
10960 );
10961 }
10962 Ok(Expr {
10963 kind: ExprKind::PwatchExpr {
10964 path: Box::new(args[0].clone()),
10965 callback: Box::new(args[1].clone()),
10966 },
10967 line,
10968 })
10969 }
10970 "fan" => {
10971 let (count, block) = self.parse_fan_count_and_block(line)?;
10977 let progress = self.parse_fan_optional_progress("fan")?;
10978 Ok(Expr {
10979 kind: ExprKind::FanExpr {
10980 count,
10981 block,
10982 progress,
10983 capture: false,
10984 },
10985 line,
10986 })
10987 }
10988 "fan_cap" => {
10989 let (count, block) = self.parse_fan_count_and_block(line)?;
10990 let progress = self.parse_fan_optional_progress("fan_cap")?;
10991 Ok(Expr {
10992 kind: ExprKind::FanExpr {
10993 count,
10994 block,
10995 progress,
10996 capture: true,
10997 },
10998 line,
10999 })
11000 }
11001 "async" => {
11002 if !matches!(self.peek(), Token::LBrace) {
11003 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
11004 }
11005 let block = self.parse_block()?;
11006 Ok(Expr {
11007 kind: ExprKind::AsyncBlock { body: block },
11008 line,
11009 })
11010 }
11011 "spawn" => {
11012 if !matches!(self.peek(), Token::LBrace) {
11013 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
11014 }
11015 let block = self.parse_block()?;
11016 Ok(Expr {
11017 kind: ExprKind::SpawnBlock { body: block },
11018 line,
11019 })
11020 }
11021 "trace" => {
11022 if !matches!(self.peek(), Token::LBrace) {
11023 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
11024 }
11025 let block = self.parse_block()?;
11026 Ok(Expr {
11027 kind: ExprKind::Trace { body: block },
11028 line,
11029 })
11030 }
11031 "timer" => {
11032 let block = self.parse_block_or_bareword_block_no_args()?;
11033 Ok(Expr {
11034 kind: ExprKind::Timer { body: block },
11035 line,
11036 })
11037 }
11038 "bench" => {
11039 let block = self.parse_block_or_bareword_block_no_args()?;
11040 let times = Box::new(self.parse_expression()?);
11041 Ok(Expr {
11042 kind: ExprKind::Bench { body: block, times },
11043 line,
11044 })
11045 }
11046 "spinner" => {
11047 let (message, body) = if matches!(self.peek(), Token::LBrace) {
11049 let body = self.parse_block()?;
11050 (
11051 Box::new(Expr {
11052 kind: ExprKind::String("working".to_string()),
11053 line,
11054 }),
11055 body,
11056 )
11057 } else {
11058 let msg = self.parse_assign_expr()?;
11059 let body = self.parse_block()?;
11060 (Box::new(msg), body)
11061 };
11062 Ok(Expr {
11063 kind: ExprKind::Spinner { message, body },
11064 line,
11065 })
11066 }
11067 "thread" | "t" => {
11068 self.parse_thread_macro(line, false)
11078 }
11079 "retry" => {
11080 let body = if matches!(self.peek(), Token::LBrace) {
11083 self.parse_block()?
11084 } else {
11085 let bw_line = self.peek_line();
11086 let Token::Ident(ref name) = self.peek().clone() else {
11087 return Err(self
11088 .syntax_err("retry: expected block or bareword function name", line));
11089 };
11090 let name = name.clone();
11091 self.advance();
11092 vec![Statement::new(
11093 StmtKind::Expression(Expr {
11094 kind: ExprKind::FuncCall { name, args: vec![] },
11095 line: bw_line,
11096 }),
11097 bw_line,
11098 )]
11099 };
11100 self.eat(&Token::Comma);
11101 match self.peek() {
11102 Token::Ident(ref s) if s == "times" => {
11103 self.advance();
11104 }
11105 _ => {
11106 return Err(self.syntax_err("retry: expected `times =>` after block", line));
11107 }
11108 }
11109 self.expect(&Token::FatArrow)?;
11110 let times = Box::new(self.parse_assign_expr()?);
11111 let mut backoff = RetryBackoff::None;
11112 if self.eat(&Token::Comma) {
11113 match self.peek() {
11114 Token::Ident(ref s) if s == "backoff" => {
11115 self.advance();
11116 }
11117 _ => {
11118 return Err(
11119 self.syntax_err("retry: expected `backoff =>` after comma", line)
11120 );
11121 }
11122 }
11123 self.expect(&Token::FatArrow)?;
11124 let Token::Ident(mode) = self.peek().clone() else {
11125 return Err(self.syntax_err(
11126 "retry: expected backoff mode (none, linear, exponential)",
11127 line,
11128 ));
11129 };
11130 backoff = match mode.as_str() {
11131 "none" => RetryBackoff::None,
11132 "linear" => RetryBackoff::Linear,
11133 "exponential" => RetryBackoff::Exponential,
11134 _ => {
11135 return Err(
11136 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
11137 );
11138 }
11139 };
11140 self.advance();
11141 }
11142 Ok(Expr {
11143 kind: ExprKind::RetryBlock {
11144 body,
11145 times,
11146 backoff,
11147 },
11148 line,
11149 })
11150 }
11151 "rate_limit" => {
11152 self.expect(&Token::LParen)?;
11153 let max = Box::new(self.parse_assign_expr()?);
11154 self.expect(&Token::Comma)?;
11155 let window = Box::new(self.parse_assign_expr()?);
11156 self.expect(&Token::RParen)?;
11157 let body = self.parse_block_or_bareword_block_no_args()?;
11158 let slot = self.alloc_rate_limit_slot();
11159 Ok(Expr {
11160 kind: ExprKind::RateLimitBlock {
11161 slot,
11162 max,
11163 window,
11164 body,
11165 },
11166 line,
11167 })
11168 }
11169 "every" => {
11170 let has_paren = self.eat(&Token::LParen);
11173 let interval = Box::new(self.parse_assign_expr()?);
11174 if has_paren {
11175 self.expect(&Token::RParen)?;
11176 }
11177 let body = if matches!(self.peek(), Token::LBrace) {
11178 self.parse_block()?
11179 } else {
11180 let bline = self.peek_line();
11181 let expr = self.parse_assign_expr()?;
11182 vec![Statement::new(StmtKind::Expression(expr), bline)]
11183 };
11184 Ok(Expr {
11185 kind: ExprKind::EveryBlock { interval, body },
11186 line,
11187 })
11188 }
11189 "gen" => {
11190 if !matches!(self.peek(), Token::LBrace) {
11191 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
11192 }
11193 let body = self.parse_block()?;
11194 Ok(Expr {
11195 kind: ExprKind::GenBlock { body },
11196 line,
11197 })
11198 }
11199 "yield" => {
11200 let e = self.parse_assign_expr()?;
11201 Ok(Expr {
11202 kind: ExprKind::Yield(Box::new(e)),
11203 line,
11204 })
11205 }
11206 "await" => {
11207 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11208 return Ok(e);
11209 }
11210 let a = self.parse_one_arg_or_default()?;
11213 Ok(Expr {
11214 kind: ExprKind::Await(Box::new(a)),
11215 line,
11216 })
11217 }
11218 "slurp" | "cat" | "c" => {
11219 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11220 return Ok(e);
11221 }
11222 let a = self.parse_one_arg_or_default()?;
11223 Ok(Expr {
11224 kind: ExprKind::Slurp(Box::new(a)),
11225 line,
11226 })
11227 }
11228 "capture" => {
11229 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11230 return Ok(e);
11231 }
11232 let a = self.parse_one_arg()?;
11233 Ok(Expr {
11234 kind: ExprKind::Capture(Box::new(a)),
11235 line,
11236 })
11237 }
11238 "fetch_url" => {
11239 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11240 return Ok(e);
11241 }
11242 let a = self.parse_one_arg()?;
11243 Ok(Expr {
11244 kind: ExprKind::FetchUrl(Box::new(a)),
11245 line,
11246 })
11247 }
11248 "pchannel" => {
11249 let capacity = if self.eat(&Token::LParen) {
11250 if matches!(self.peek(), Token::RParen) {
11251 self.advance();
11252 None
11253 } else {
11254 let e = self.parse_expression()?;
11255 self.expect(&Token::RParen)?;
11256 Some(Box::new(e))
11257 }
11258 } else {
11259 None
11260 };
11261 Ok(Expr {
11262 kind: ExprKind::Pchannel { capacity },
11263 line,
11264 })
11265 }
11266 "psort" => {
11267 if matches!(self.peek(), Token::LBrace)
11268 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
11269 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
11270 {
11271 let block = self.parse_block_or_bareword_cmp_block()?;
11272 let block_end_line = self.prev_line();
11280 self.eat(&Token::Comma);
11281 let use_placeholder = self.in_pipe_rhs()
11282 && (matches!(
11283 self.peek(),
11284 Token::Semicolon
11285 | Token::RBrace
11286 | Token::RParen
11287 | Token::Eof
11288 | Token::PipeForward
11289 ) || self.peek_line() > block_end_line);
11290 let (list, progress) = if use_placeholder {
11291 (self.pipe_placeholder_list(line), None)
11292 } else {
11293 self.parse_assign_expr_list_optional_progress()?
11294 };
11295 Ok(Expr {
11296 kind: ExprKind::PSortExpr {
11297 cmp: Some(block),
11298 list: Box::new(list),
11299 progress: progress.map(Box::new),
11300 },
11301 line,
11302 })
11303 } else {
11304 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11305 Ok(Expr {
11306 kind: ExprKind::PSortExpr {
11307 cmp: None,
11308 list: Box::new(list),
11309 progress: progress.map(Box::new),
11310 },
11311 line,
11312 })
11313 }
11314 }
11315 "preduce" => {
11316 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11317 Ok(Expr {
11318 kind: ExprKind::PReduceExpr {
11319 block,
11320 list: Box::new(list),
11321 progress: progress.map(Box::new),
11322 },
11323 line,
11324 })
11325 }
11326 "preduce_init" => {
11327 let (init, block, list, progress) =
11328 self.parse_init_block_then_list_optional_progress()?;
11329 Ok(Expr {
11330 kind: ExprKind::PReduceInitExpr {
11331 init: Box::new(init),
11332 block,
11333 list: Box::new(list),
11334 progress: progress.map(Box::new),
11335 },
11336 line,
11337 })
11338 }
11339 "pmap_reduce" => {
11340 let map_block = self.parse_block_or_bareword_block()?;
11341 let reduce_block = if matches!(self.peek(), Token::LBrace) {
11344 self.parse_block()?
11345 } else {
11346 self.expect(&Token::Comma)?;
11348 self.parse_block_or_bareword_cmp_block()?
11349 };
11350 self.eat(&Token::Comma);
11351 let line = self.peek_line();
11352 if let Token::Ident(ref kw) = self.peek().clone() {
11353 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11354 self.advance();
11355 self.expect(&Token::FatArrow)?;
11356 let prog = self.parse_assign_expr()?;
11357 return Ok(Expr {
11358 kind: ExprKind::PMapReduceExpr {
11359 map_block,
11360 reduce_block,
11361 list: Box::new(Expr {
11362 kind: ExprKind::List(vec![]),
11363 line,
11364 }),
11365 progress: Some(Box::new(prog)),
11366 },
11367 line,
11368 });
11369 }
11370 }
11371 if matches!(
11372 self.peek(),
11373 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11374 ) {
11375 return Ok(Expr {
11376 kind: ExprKind::PMapReduceExpr {
11377 map_block,
11378 reduce_block,
11379 list: Box::new(Expr {
11380 kind: ExprKind::List(vec![]),
11381 line,
11382 }),
11383 progress: None,
11384 },
11385 line,
11386 });
11387 }
11388 let mut parts = vec![self.parse_assign_expr()?];
11389 loop {
11390 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11391 break;
11392 }
11393 if matches!(
11394 self.peek(),
11395 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11396 ) {
11397 break;
11398 }
11399 if let Token::Ident(ref kw) = self.peek().clone() {
11400 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11401 self.advance();
11402 self.expect(&Token::FatArrow)?;
11403 let prog = self.parse_assign_expr()?;
11404 return Ok(Expr {
11405 kind: ExprKind::PMapReduceExpr {
11406 map_block,
11407 reduce_block,
11408 list: Box::new(merge_expr_list(parts)),
11409 progress: Some(Box::new(prog)),
11410 },
11411 line,
11412 });
11413 }
11414 }
11415 parts.push(self.parse_assign_expr()?);
11416 }
11417 Ok(Expr {
11418 kind: ExprKind::PMapReduceExpr {
11419 map_block,
11420 reduce_block,
11421 list: Box::new(merge_expr_list(parts)),
11422 progress: None,
11423 },
11424 line,
11425 })
11426 }
11427 "puniq" => {
11428 if self.pipe_supplies_slurped_list_operand() {
11429 return Ok(Expr {
11430 kind: ExprKind::FuncCall {
11431 name: "puniq".to_string(),
11432 args: vec![],
11433 },
11434 line,
11435 });
11436 }
11437 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11438 let mut args = vec![list];
11439 if let Some(p) = progress {
11440 args.push(p);
11441 }
11442 Ok(Expr {
11443 kind: ExprKind::FuncCall {
11444 name: "puniq".to_string(),
11445 args,
11446 },
11447 line,
11448 })
11449 }
11450 "pfirst" => {
11451 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11452 let cr = Expr {
11453 kind: ExprKind::CodeRef {
11454 params: vec![],
11455 body: block,
11456 },
11457 line,
11458 };
11459 let mut args = vec![cr, list];
11460 if let Some(p) = progress {
11461 args.push(p);
11462 }
11463 Ok(Expr {
11464 kind: ExprKind::FuncCall {
11465 name: "pfirst".to_string(),
11466 args,
11467 },
11468 line,
11469 })
11470 }
11471 "pany" => {
11472 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11473 let cr = Expr {
11474 kind: ExprKind::CodeRef {
11475 params: vec![],
11476 body: block,
11477 },
11478 line,
11479 };
11480 let mut args = vec![cr, list];
11481 if let Some(p) = progress {
11482 args.push(p);
11483 }
11484 Ok(Expr {
11485 kind: ExprKind::FuncCall {
11486 name: "pany".to_string(),
11487 args,
11488 },
11489 line,
11490 })
11491 }
11492 "uniq" | "distinct" => {
11493 if self.pipe_supplies_slurped_list_operand() {
11494 return Ok(Expr {
11495 kind: ExprKind::FuncCall {
11496 name: name.clone(),
11497 args: vec![],
11498 },
11499 line,
11500 });
11501 }
11502 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11503 if progress.is_some() {
11504 return Err(self.syntax_err(
11505 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
11506 line,
11507 ));
11508 }
11509 Ok(Expr {
11510 kind: ExprKind::FuncCall {
11511 name: name.clone(),
11512 args: vec![list],
11513 },
11514 line,
11515 })
11516 }
11517 "flatten" => {
11518 if self.pipe_supplies_slurped_list_operand() {
11519 return Ok(Expr {
11520 kind: ExprKind::FuncCall {
11521 name: "flatten".to_string(),
11522 args: vec![],
11523 },
11524 line,
11525 });
11526 }
11527 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11528 if progress.is_some() {
11529 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
11530 }
11531 Ok(Expr {
11532 kind: ExprKind::FuncCall {
11533 name: "flatten".to_string(),
11534 args: vec![list],
11535 },
11536 line,
11537 })
11538 }
11539 "set" => {
11540 if self.pipe_supplies_slurped_list_operand() {
11541 return Ok(Expr {
11542 kind: ExprKind::FuncCall {
11543 name: "set".to_string(),
11544 args: vec![],
11545 },
11546 line,
11547 });
11548 }
11549 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11550 if progress.is_some() {
11551 return Err(self.syntax_err("`progress =>` is not supported for set", line));
11552 }
11553 Ok(Expr {
11554 kind: ExprKind::FuncCall {
11555 name: "set".to_string(),
11556 args: vec![list],
11557 },
11558 line,
11559 })
11560 }
11561 "size" => {
11565 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11566 return Ok(e);
11567 }
11568 if self.pipe_supplies_slurped_list_operand() {
11569 return Ok(Expr {
11570 kind: ExprKind::FuncCall {
11571 name: "size".to_string(),
11572 args: vec![],
11573 },
11574 line,
11575 });
11576 }
11577 let a = self.parse_one_arg_or_default()?;
11578 Ok(Expr {
11579 kind: ExprKind::FuncCall {
11580 name: "size".to_string(),
11581 args: vec![a],
11582 },
11583 line,
11584 })
11585 }
11586 "list_count" | "list_size" | "count" | "len" | "cnt" => {
11587 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11588 return Ok(e);
11589 }
11590 if self.pipe_supplies_slurped_list_operand() {
11591 return Ok(Expr {
11592 kind: ExprKind::FuncCall {
11593 name: name.clone(),
11594 args: vec![],
11595 },
11596 line,
11597 });
11598 }
11599 let args = if matches!(self.peek(), Token::LParen) {
11613 self.advance();
11614 if matches!(self.peek(), Token::RParen) {
11615 self.advance();
11616 Vec::new()
11617 } else {
11618 let inner = self.parse_expression()?;
11619 self.expect(&Token::RParen)?;
11620 vec![inner]
11621 }
11622 } else if self.peek_is_named_unary_terminator() {
11623 Vec::new()
11624 } else {
11625 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11626 if progress.is_some() {
11627 return Err(self.syntax_err(
11628 "`progress =>` is not supported for list_count / list_size / count / cnt",
11629 line,
11630 ));
11631 }
11632 vec![list]
11633 };
11634 Ok(Expr {
11635 kind: ExprKind::FuncCall {
11636 name: name.clone(),
11637 args,
11638 },
11639 line,
11640 })
11641 }
11642 "shuffle" | "shuffled" => {
11643 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11644 return Ok(e);
11645 }
11646 if self.pipe_supplies_slurped_list_operand() {
11647 return Ok(Expr {
11648 kind: ExprKind::FuncCall {
11649 name: "shuffle".to_string(),
11650 args: vec![],
11651 },
11652 line,
11653 });
11654 }
11655 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11656 if progress.is_some() {
11657 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
11658 }
11659 Ok(Expr {
11660 kind: ExprKind::FuncCall {
11661 name: "shuffle".to_string(),
11662 args: vec![list],
11663 },
11664 line,
11665 })
11666 }
11667 "chunked" => {
11668 let mut parts = Vec::new();
11669 if self.eat(&Token::LParen) {
11670 if !matches!(self.peek(), Token::RParen) {
11671 parts.push(self.parse_assign_expr()?);
11672 while self.eat(&Token::Comma) {
11673 if matches!(self.peek(), Token::RParen) {
11674 break;
11675 }
11676 parts.push(self.parse_assign_expr()?);
11677 }
11678 }
11679 self.expect(&Token::RParen)?;
11680 } else {
11681 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11685 loop {
11686 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11687 break;
11688 }
11689 if matches!(
11690 self.peek(),
11691 Token::Semicolon
11692 | Token::RBrace
11693 | Token::RParen
11694 | Token::Eof
11695 | Token::PipeForward
11696 ) {
11697 break;
11698 }
11699 if self.peek_is_postfix_stmt_modifier_keyword() {
11700 break;
11701 }
11702 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11703 }
11704 }
11705 if parts.len() == 1 {
11706 let n = parts.pop().unwrap();
11707 return Ok(Expr {
11708 kind: ExprKind::FuncCall {
11709 name: "chunked".to_string(),
11710 args: vec![n],
11711 },
11712 line,
11713 });
11714 }
11715 if parts.is_empty() {
11716 return Ok(Expr {
11717 kind: ExprKind::FuncCall {
11718 name: "chunked".to_string(),
11719 args: parts,
11720 },
11721 line,
11722 });
11723 }
11724 if parts.len() == 2 {
11725 let n = parts.pop().unwrap();
11726 let list = parts.pop().unwrap();
11727 return Ok(Expr {
11728 kind: ExprKind::FuncCall {
11729 name: "chunked".to_string(),
11730 args: vec![list, n],
11731 },
11732 line,
11733 });
11734 }
11735 Err(self.syntax_err(
11736 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
11737 line,
11738 ))
11739 }
11740 "windowed" => {
11741 let mut parts = Vec::new();
11742 if self.eat(&Token::LParen) {
11743 if !matches!(self.peek(), Token::RParen) {
11744 parts.push(self.parse_assign_expr()?);
11745 while self.eat(&Token::Comma) {
11746 if matches!(self.peek(), Token::RParen) {
11747 break;
11748 }
11749 parts.push(self.parse_assign_expr()?);
11750 }
11751 }
11752 self.expect(&Token::RParen)?;
11753 } else {
11754 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11757 loop {
11758 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11759 break;
11760 }
11761 if matches!(
11762 self.peek(),
11763 Token::Semicolon
11764 | Token::RBrace
11765 | Token::RParen
11766 | Token::Eof
11767 | Token::PipeForward
11768 ) {
11769 break;
11770 }
11771 if self.peek_is_postfix_stmt_modifier_keyword() {
11772 break;
11773 }
11774 parts.push(self.parse_assign_expr_stop_at_pipe()?);
11775 }
11776 }
11777 if parts.len() == 1 {
11778 let n = parts.pop().unwrap();
11779 return Ok(Expr {
11780 kind: ExprKind::FuncCall {
11781 name: "windowed".to_string(),
11782 args: vec![n],
11783 },
11784 line,
11785 });
11786 }
11787 if parts.is_empty() {
11788 return Ok(Expr {
11789 kind: ExprKind::FuncCall {
11790 name: "windowed".to_string(),
11791 args: parts,
11792 },
11793 line,
11794 });
11795 }
11796 if parts.len() == 2 {
11797 let n = parts.pop().unwrap();
11798 let list = parts.pop().unwrap();
11799 return Ok(Expr {
11800 kind: ExprKind::FuncCall {
11801 name: "windowed".to_string(),
11802 args: vec![list, n],
11803 },
11804 line,
11805 });
11806 }
11807 Err(self.syntax_err(
11808 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
11809 line,
11810 ))
11811 }
11812 "any" | "all" | "none" => {
11813 if matches!(self.peek(), Token::LParen) {
11815 self.advance();
11816 let args = self.parse_arg_list()?;
11817 self.expect(&Token::RParen)?;
11818 return Ok(Expr {
11819 kind: ExprKind::FuncCall {
11820 name: name.clone(),
11821 args,
11822 },
11823 line,
11824 });
11825 }
11826 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11830 return Ok(Expr {
11831 kind: ExprKind::FuncCall {
11832 name: name.clone(),
11833 args,
11834 },
11835 line,
11836 });
11837 }
11838 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11840 if progress.is_some() {
11841 return Err(self.syntax_err(
11842 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
11843 line,
11844 ));
11845 }
11846 let cr = Expr {
11847 kind: ExprKind::CodeRef {
11848 params: vec![],
11849 body: block,
11850 },
11851 line,
11852 };
11853 Ok(Expr {
11854 kind: ExprKind::FuncCall {
11855 name: name.clone(),
11856 args: vec![cr, list],
11857 },
11858 line,
11859 })
11860 }
11861 "first" | "detect" | "find" => {
11863 if matches!(self.peek(), Token::LParen) {
11865 self.advance();
11866 let args = self.parse_arg_list()?;
11867 self.expect(&Token::RParen)?;
11868 return Ok(Expr {
11869 kind: ExprKind::FuncCall {
11870 name: "first".to_string(),
11871 args,
11872 },
11873 line,
11874 });
11875 }
11876 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11878 return Ok(Expr {
11879 kind: ExprKind::FuncCall {
11880 name: "first".to_string(),
11881 args,
11882 },
11883 line,
11884 });
11885 }
11886 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11888 if progress.is_some() {
11889 return Err(self.syntax_err(
11890 "`progress =>` is not supported for first/detect/find (use pfirst for parallel + progress)",
11891 line,
11892 ));
11893 }
11894 let cr = Expr {
11895 kind: ExprKind::CodeRef {
11896 params: vec![],
11897 body: block,
11898 },
11899 line,
11900 };
11901 Ok(Expr {
11902 kind: ExprKind::FuncCall {
11903 name: "first".to_string(),
11904 args: vec![cr, list],
11905 },
11906 line,
11907 })
11908 }
11909 "take_while" | "drop_while" | "skip_while" | "reject" | "grepv" | "tap" | "peek"
11910 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
11911 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
11913 return Ok(Expr {
11914 kind: ExprKind::FuncCall {
11915 name: name.to_string(),
11916 args,
11917 },
11918 line,
11919 });
11920 }
11921 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11922 if progress.is_some() {
11923 return Err(
11924 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
11925 );
11926 }
11927 let cr = Expr {
11928 kind: ExprKind::CodeRef {
11929 params: vec![],
11930 body: block,
11931 },
11932 line,
11933 };
11934 Ok(Expr {
11935 kind: ExprKind::FuncCall {
11936 name: name.to_string(),
11937 args: vec![cr, list],
11938 },
11939 line,
11940 })
11941 }
11942 "group_by" | "chunk_by" => {
11943 if matches!(self.peek(), Token::LBrace) {
11944 let (block, list) = self.parse_block_list()?;
11945 let cr = Expr {
11946 kind: ExprKind::CodeRef {
11947 params: vec![],
11948 body: block,
11949 },
11950 line,
11951 };
11952 Ok(Expr {
11953 kind: ExprKind::FuncCall {
11954 name: name.to_string(),
11955 args: vec![cr, list],
11956 },
11957 line,
11958 })
11959 } else {
11960 let key_expr = self.parse_assign_expr()?;
11961 self.expect(&Token::Comma)?;
11962 let list_parts = self.parse_list_until_terminator()?;
11963 let list_expr = if list_parts.len() == 1 {
11964 list_parts.into_iter().next().unwrap()
11965 } else {
11966 Expr {
11967 kind: ExprKind::List(list_parts),
11968 line,
11969 }
11970 };
11971 Ok(Expr {
11972 kind: ExprKind::FuncCall {
11973 name: name.to_string(),
11974 args: vec![key_expr, list_expr],
11975 },
11976 line,
11977 })
11978 }
11979 }
11980 "with_index" => {
11981 if self.pipe_supplies_slurped_list_operand() {
11982 return Ok(Expr {
11983 kind: ExprKind::FuncCall {
11984 name: "with_index".to_string(),
11985 args: vec![],
11986 },
11987 line,
11988 });
11989 }
11990 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11991 if progress.is_some() {
11992 return Err(
11993 self.syntax_err("`progress =>` is not supported for with_index", line)
11994 );
11995 }
11996 Ok(Expr {
11997 kind: ExprKind::FuncCall {
11998 name: "with_index".to_string(),
11999 args: vec![list],
12000 },
12001 line,
12002 })
12003 }
12004 "pcache" => {
12005 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12006 Ok(Expr {
12007 kind: ExprKind::PcacheExpr {
12008 block,
12009 list: Box::new(list),
12010 progress: progress.map(Box::new),
12011 },
12012 line,
12013 })
12014 }
12015 "pselect" => {
12016 let paren = self.eat(&Token::LParen);
12017 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
12018 if paren {
12019 self.expect(&Token::RParen)?;
12020 }
12021 if receivers.is_empty() {
12022 return Err(self.syntax_err("pselect needs at least one receiver", line));
12023 }
12024 Ok(Expr {
12025 kind: ExprKind::PselectExpr {
12026 receivers,
12027 timeout: timeout.map(Box::new),
12028 },
12029 line,
12030 })
12031 }
12032 "open" => {
12033 let paren = matches!(self.peek(), Token::LParen);
12034 if paren {
12035 self.advance();
12036 }
12037 if matches!(self.peek(), Token::Ident(ref s) if s == "my") {
12038 self.advance();
12039 let name = self.parse_scalar_var_name()?;
12040 self.expect(&Token::Comma)?;
12041 let mode = self.parse_assign_expr()?;
12042 let file = if self.eat(&Token::Comma) {
12043 Some(self.parse_assign_expr()?)
12044 } else {
12045 None
12046 };
12047 if paren {
12048 self.expect(&Token::RParen)?;
12049 }
12050 Ok(Expr {
12051 kind: ExprKind::Open {
12052 handle: Box::new(Expr {
12053 kind: ExprKind::OpenMyHandle { name },
12054 line,
12055 }),
12056 mode: Box::new(mode),
12057 file: file.map(Box::new),
12058 },
12059 line,
12060 })
12061 } else {
12062 let args = if paren {
12063 self.parse_arg_list()?
12064 } else {
12065 self.parse_list_until_terminator()?
12066 };
12067 if paren {
12068 self.expect(&Token::RParen)?;
12069 }
12070 if args.len() < 2 {
12071 return Err(self.syntax_err("open requires at least 2 arguments", line));
12072 }
12073 Ok(Expr {
12074 kind: ExprKind::Open {
12075 handle: Box::new(args[0].clone()),
12076 mode: Box::new(args[1].clone()),
12077 file: args.get(2).cloned().map(Box::new),
12078 },
12079 line,
12080 })
12081 }
12082 }
12083 "close" => {
12084 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12085 return Ok(e);
12086 }
12087 let a = self.parse_one_arg_or_default()?;
12088 Ok(Expr {
12089 kind: ExprKind::Close(Box::new(a)),
12090 line,
12091 })
12092 }
12093 "opendir" => {
12094 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12095 return Ok(e);
12096 }
12097 let args = self.parse_builtin_args()?;
12098 if args.len() != 2 {
12099 return Err(self.syntax_err("opendir requires two arguments", line));
12100 }
12101 Ok(Expr {
12102 kind: ExprKind::Opendir {
12103 handle: Box::new(args[0].clone()),
12104 path: Box::new(args[1].clone()),
12105 },
12106 line,
12107 })
12108 }
12109 "readdir" => {
12110 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12111 return Ok(e);
12112 }
12113 let a = self.parse_one_arg()?;
12114 Ok(Expr {
12115 kind: ExprKind::Readdir(Box::new(a)),
12116 line,
12117 })
12118 }
12119 "closedir" => {
12120 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12121 return Ok(e);
12122 }
12123 let a = self.parse_one_arg()?;
12124 Ok(Expr {
12125 kind: ExprKind::Closedir(Box::new(a)),
12126 line,
12127 })
12128 }
12129 "rewinddir" => {
12130 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12131 return Ok(e);
12132 }
12133 let a = self.parse_one_arg()?;
12134 Ok(Expr {
12135 kind: ExprKind::Rewinddir(Box::new(a)),
12136 line,
12137 })
12138 }
12139 "telldir" => {
12140 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12141 return Ok(e);
12142 }
12143 let a = self.parse_one_arg()?;
12144 Ok(Expr {
12145 kind: ExprKind::Telldir(Box::new(a)),
12146 line,
12147 })
12148 }
12149 "seekdir" => {
12150 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12151 return Ok(e);
12152 }
12153 let args = self.parse_builtin_args()?;
12154 if args.len() != 2 {
12155 return Err(self.syntax_err("seekdir requires two arguments", line));
12156 }
12157 Ok(Expr {
12158 kind: ExprKind::Seekdir {
12159 handle: Box::new(args[0].clone()),
12160 position: Box::new(args[1].clone()),
12161 },
12162 line,
12163 })
12164 }
12165 "eof" => {
12166 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12167 return Ok(e);
12168 }
12169 if matches!(self.peek(), Token::LParen) {
12170 self.advance();
12171 if matches!(self.peek(), Token::RParen) {
12172 self.advance();
12173 Ok(Expr {
12174 kind: ExprKind::Eof(None),
12175 line,
12176 })
12177 } else {
12178 let a = self.parse_expression()?;
12179 self.expect(&Token::RParen)?;
12180 Ok(Expr {
12181 kind: ExprKind::Eof(Some(Box::new(a))),
12182 line,
12183 })
12184 }
12185 } else {
12186 Ok(Expr {
12187 kind: ExprKind::Eof(None),
12188 line,
12189 })
12190 }
12191 }
12192 "system" => {
12193 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12194 return Ok(e);
12195 }
12196 let args = self.parse_builtin_args()?;
12197 Ok(Expr {
12198 kind: ExprKind::System(args),
12199 line,
12200 })
12201 }
12202 "exec" => {
12203 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12204 return Ok(e);
12205 }
12206 let args = self.parse_builtin_args()?;
12207 Ok(Expr {
12208 kind: ExprKind::Exec(args),
12209 line,
12210 })
12211 }
12212 "eval" => {
12213 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12214 return Ok(e);
12215 }
12216 let a = if matches!(self.peek(), Token::LBrace) {
12217 let block = self.parse_block()?;
12218 Expr {
12219 kind: ExprKind::CodeRef {
12220 params: vec![],
12221 body: block,
12222 },
12223 line,
12224 }
12225 } else {
12226 self.parse_one_arg_or_default()?
12227 };
12228 Ok(Expr {
12229 kind: ExprKind::Eval(Box::new(a)),
12230 line,
12231 })
12232 }
12233 "do" => {
12234 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12235 return Ok(e);
12236 }
12237 let a = self.parse_one_arg()?;
12238 Ok(Expr {
12239 kind: ExprKind::Do(Box::new(a)),
12240 line,
12241 })
12242 }
12243 "require" => {
12244 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12245 return Ok(e);
12246 }
12247 let a = self.parse_one_arg()?;
12248 Ok(Expr {
12249 kind: ExprKind::Require(Box::new(a)),
12250 line,
12251 })
12252 }
12253 "exit" => {
12254 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12255 return Ok(e);
12256 }
12257 if matches!(
12258 self.peek(),
12259 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
12260 ) {
12261 Ok(Expr {
12262 kind: ExprKind::Exit(None),
12263 line,
12264 })
12265 } else {
12266 let a = self.parse_one_arg()?;
12267 Ok(Expr {
12268 kind: ExprKind::Exit(Some(Box::new(a))),
12269 line,
12270 })
12271 }
12272 }
12273 "chdir" => {
12274 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12275 return Ok(e);
12276 }
12277 let a = self.parse_one_arg_or_default()?;
12278 Ok(Expr {
12279 kind: ExprKind::Chdir(Box::new(a)),
12280 line,
12281 })
12282 }
12283 "mkdir" => {
12284 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12285 return Ok(e);
12286 }
12287 let args = self.parse_builtin_args()?;
12288 Ok(Expr {
12289 kind: ExprKind::Mkdir {
12290 path: Box::new(args[0].clone()),
12291 mode: args.get(1).cloned().map(Box::new),
12292 },
12293 line,
12294 })
12295 }
12296 "unlink" | "rm" => {
12297 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12298 return Ok(e);
12299 }
12300 let args = self.parse_builtin_args()?;
12301 Ok(Expr {
12302 kind: ExprKind::Unlink(args),
12303 line,
12304 })
12305 }
12306 "rename" => {
12307 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12308 return Ok(e);
12309 }
12310 let args = self.parse_builtin_args()?;
12311 if args.len() != 2 {
12312 return Err(self.syntax_err("rename requires two arguments", line));
12313 }
12314 Ok(Expr {
12315 kind: ExprKind::Rename {
12316 old: Box::new(args[0].clone()),
12317 new: Box::new(args[1].clone()),
12318 },
12319 line,
12320 })
12321 }
12322 "chmod" => {
12323 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12324 return Ok(e);
12325 }
12326 let args = self.parse_builtin_args()?;
12327 if args.len() < 2 {
12328 return Err(self.syntax_err("chmod requires mode and at least one file", line));
12329 }
12330 Ok(Expr {
12331 kind: ExprKind::Chmod(args),
12332 line,
12333 })
12334 }
12335 "chown" => {
12336 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12337 return Ok(e);
12338 }
12339 let args = self.parse_builtin_args()?;
12340 if args.len() < 3 {
12341 return Err(
12342 self.syntax_err("chown requires uid, gid, and at least one file", line)
12343 );
12344 }
12345 Ok(Expr {
12346 kind: ExprKind::Chown(args),
12347 line,
12348 })
12349 }
12350 "stat" => {
12351 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12352 return Ok(e);
12353 }
12354 let args = self.parse_builtin_args()?;
12355 let arg = if args.len() == 1 {
12356 args[0].clone()
12357 } else if args.is_empty() {
12358 Expr {
12359 kind: ExprKind::ScalarVar("_".into()),
12360 line,
12361 }
12362 } else {
12363 return Err(self.syntax_err("stat requires zero or one argument", line));
12364 };
12365 Ok(Expr {
12366 kind: ExprKind::Stat(Box::new(arg)),
12367 line,
12368 })
12369 }
12370 "lstat" => {
12371 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12372 return Ok(e);
12373 }
12374 let args = self.parse_builtin_args()?;
12375 let arg = if args.len() == 1 {
12376 args[0].clone()
12377 } else if args.is_empty() {
12378 Expr {
12379 kind: ExprKind::ScalarVar("_".into()),
12380 line,
12381 }
12382 } else {
12383 return Err(self.syntax_err("lstat requires zero or one argument", line));
12384 };
12385 Ok(Expr {
12386 kind: ExprKind::Lstat(Box::new(arg)),
12387 line,
12388 })
12389 }
12390 "link" => {
12391 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12392 return Ok(e);
12393 }
12394 let args = self.parse_builtin_args()?;
12395 if args.len() != 2 {
12396 return Err(self.syntax_err("link requires two arguments", line));
12397 }
12398 Ok(Expr {
12399 kind: ExprKind::Link {
12400 old: Box::new(args[0].clone()),
12401 new: Box::new(args[1].clone()),
12402 },
12403 line,
12404 })
12405 }
12406 "symlink" => {
12407 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12408 return Ok(e);
12409 }
12410 let args = self.parse_builtin_args()?;
12411 if args.len() != 2 {
12412 return Err(self.syntax_err("symlink requires two arguments", line));
12413 }
12414 Ok(Expr {
12415 kind: ExprKind::Symlink {
12416 old: Box::new(args[0].clone()),
12417 new: Box::new(args[1].clone()),
12418 },
12419 line,
12420 })
12421 }
12422 "readlink" => {
12423 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12424 return Ok(e);
12425 }
12426 let args = self.parse_builtin_args()?;
12427 let arg = if args.len() == 1 {
12428 args[0].clone()
12429 } else if args.is_empty() {
12430 Expr {
12431 kind: ExprKind::ScalarVar("_".into()),
12432 line,
12433 }
12434 } else {
12435 return Err(self.syntax_err("readlink requires zero or one argument", line));
12436 };
12437 Ok(Expr {
12438 kind: ExprKind::Readlink(Box::new(arg)),
12439 line,
12440 })
12441 }
12442 "files" => {
12443 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12444 return Ok(e);
12445 }
12446 let args = self.parse_builtin_args()?;
12447 Ok(Expr {
12448 kind: ExprKind::Files(args),
12449 line,
12450 })
12451 }
12452 "filesf" | "f" => {
12453 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12454 return Ok(e);
12455 }
12456 let args = self.parse_builtin_args()?;
12457 Ok(Expr {
12458 kind: ExprKind::Filesf(args),
12459 line,
12460 })
12461 }
12462 "fr" => {
12463 let args = self.parse_builtin_args()?;
12464 Ok(Expr {
12465 kind: ExprKind::FilesfRecursive(args),
12466 line,
12467 })
12468 }
12469 "dirs" | "d" => {
12470 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12471 return Ok(e);
12472 }
12473 let args = self.parse_builtin_args()?;
12474 Ok(Expr {
12475 kind: ExprKind::Dirs(args),
12476 line,
12477 })
12478 }
12479 "dr" => {
12480 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12481 return Ok(e);
12482 }
12483 let args = self.parse_builtin_args()?;
12484 Ok(Expr {
12485 kind: ExprKind::DirsRecursive(args),
12486 line,
12487 })
12488 }
12489 "sym_links" => {
12490 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12491 return Ok(e);
12492 }
12493 let args = self.parse_builtin_args()?;
12494 Ok(Expr {
12495 kind: ExprKind::SymLinks(args),
12496 line,
12497 })
12498 }
12499 "sockets" => {
12500 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12501 return Ok(e);
12502 }
12503 let args = self.parse_builtin_args()?;
12504 Ok(Expr {
12505 kind: ExprKind::Sockets(args),
12506 line,
12507 })
12508 }
12509 "pipes" => {
12510 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12511 return Ok(e);
12512 }
12513 let args = self.parse_builtin_args()?;
12514 Ok(Expr {
12515 kind: ExprKind::Pipes(args),
12516 line,
12517 })
12518 }
12519 "block_devices" => {
12520 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12521 return Ok(e);
12522 }
12523 let args = self.parse_builtin_args()?;
12524 Ok(Expr {
12525 kind: ExprKind::BlockDevices(args),
12526 line,
12527 })
12528 }
12529 "char_devices" => {
12530 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12531 return Ok(e);
12532 }
12533 let args = self.parse_builtin_args()?;
12534 Ok(Expr {
12535 kind: ExprKind::CharDevices(args),
12536 line,
12537 })
12538 }
12539 "exe" | "executables" => {
12540 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12541 return Ok(e);
12542 }
12543 let args = self.parse_builtin_args()?;
12544 Ok(Expr {
12545 kind: ExprKind::Executables(args),
12546 line,
12547 })
12548 }
12549 "glob" => {
12550 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12551 return Ok(e);
12552 }
12553 let args = self.parse_builtin_args()?;
12554 Ok(Expr {
12555 kind: ExprKind::Glob(args),
12556 line,
12557 })
12558 }
12559 "glob_par" => {
12560 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12561 return Ok(e);
12562 }
12563 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12564 Ok(Expr {
12565 kind: ExprKind::GlobPar { args, progress },
12566 line,
12567 })
12568 }
12569 "par_sed" => {
12570 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12571 return Ok(e);
12572 }
12573 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
12574 Ok(Expr {
12575 kind: ExprKind::ParSed { args, progress },
12576 line,
12577 })
12578 }
12579 "bless" => {
12580 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12581 return Ok(e);
12582 }
12583 let args = self.parse_builtin_args()?;
12584 Ok(Expr {
12585 kind: ExprKind::Bless {
12586 ref_expr: Box::new(args[0].clone()),
12587 class: args.get(1).cloned().map(Box::new),
12588 },
12589 line,
12590 })
12591 }
12592 "caller" => {
12593 if matches!(self.peek(), Token::LParen) {
12594 self.advance();
12595 if matches!(self.peek(), Token::RParen) {
12596 self.advance();
12597 Ok(Expr {
12598 kind: ExprKind::Caller(None),
12599 line,
12600 })
12601 } else {
12602 let a = self.parse_expression()?;
12603 self.expect(&Token::RParen)?;
12604 Ok(Expr {
12605 kind: ExprKind::Caller(Some(Box::new(a))),
12606 line,
12607 })
12608 }
12609 } else {
12610 Ok(Expr {
12611 kind: ExprKind::Caller(None),
12612 line,
12613 })
12614 }
12615 }
12616 "wantarray" => {
12617 if matches!(self.peek(), Token::LParen) {
12618 self.advance();
12619 self.expect(&Token::RParen)?;
12620 }
12621 Ok(Expr {
12622 kind: ExprKind::Wantarray,
12623 line,
12624 })
12625 }
12626 "sub" => {
12627 if crate::no_interop_mode() {
12629 return Err(self.syntax_err(
12630 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
12631 line,
12632 ));
12633 }
12634 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
12636 let body = self.parse_block()?;
12637 Ok(Expr {
12638 kind: ExprKind::CodeRef { params, body },
12639 line,
12640 })
12641 }
12642 "fn" => {
12643 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
12645 self.parse_sub_attributes()?;
12646 let body = self.parse_fn_eq_body_or_block(false)?;
12647 Ok(Expr {
12648 kind: ExprKind::CodeRef { params, body },
12649 line,
12650 })
12651 }
12652 _ => {
12653 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name)
12658 {
12659 return Ok(Expr {
12660 kind: ExprKind::String(name),
12661 line,
12662 });
12663 }
12664 if Self::is_underscore_topic_slot(&name) {
12680 if matches!(self.peek(), Token::LBracket) && self.peek_line() == line {
12681 self.advance(); let index = self.parse_expression()?;
12683 self.expect(&Token::RBracket)?;
12684 return Ok(Expr {
12685 kind: ExprKind::ArrayElement {
12686 array: format!("__topicstr__{}", name),
12687 index: Box::new(index),
12688 },
12689 line,
12690 });
12691 }
12692 return Ok(Expr {
12693 kind: ExprKind::ScalarVar(name.clone()),
12694 line,
12695 });
12696 }
12697 if matches!(self.peek(), Token::LParen) {
12699 self.advance();
12700 let args = self.parse_arg_list()?;
12701 self.expect(&Token::RParen)?;
12702 Ok(Expr {
12703 kind: ExprKind::FuncCall { name, args },
12704 line,
12705 })
12706 } else if self.peek().is_term_start()
12707 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
12708 && matches!(self.peek_at(1), Token::Ident(_)))
12709 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
12710 && !(matches!(self.peek(), Token::LBrace)
12711 && self.peek_line() > self.prev_line())
12712 && !(matches!(self.peek(), Token::BitNot)
12713 && self.suppress_tilde_range == 0
12714 && matches!(
12715 self.peek_at(1),
12716 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
12717 ))
12718 {
12719 let args = self.parse_list_until_terminator()?;
12733 Ok(Expr {
12734 kind: ExprKind::FuncCall { name, args },
12735 line,
12736 })
12737 } else {
12738 Ok(Expr {
12744 kind: ExprKind::Bareword(name),
12745 line,
12746 })
12747 }
12748 }
12749 }
12750 }
12751
12752 fn parse_print_like(
12753 &mut self,
12754 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
12755 ) -> PerlResult<Expr> {
12756 let line = self.peek_line();
12757 let handle = if let Token::Ident(ref h) = self.peek().clone() {
12759 if h.chars().all(|c| c.is_uppercase() || c == '_')
12760 && !matches!(self.peek(), Token::LParen)
12761 {
12762 let h = h.clone();
12763 let saved = self.pos;
12764 self.advance();
12765 let is_tilde_range_after = matches!(self.peek(), Token::BitNot)
12772 && self.suppress_tilde_range == 0
12773 && matches!(
12774 self.peek_at(1),
12775 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
12776 );
12777 if !is_tilde_range_after
12778 && (self.peek().is_term_start()
12779 || matches!(
12780 self.peek(),
12781 Token::DoubleString(_)
12782 | Token::BacktickString(_)
12783 | Token::SingleString(_)
12784 ))
12785 {
12786 Some(h)
12787 } else {
12788 self.pos = saved;
12789 None
12790 }
12791 } else {
12792 None
12793 }
12794 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
12795 let v = v.clone();
12805 if v == "_" {
12806 None
12807 } else {
12808 let saved = self.pos;
12809 self.advance();
12810 let next = self.peek().clone();
12811 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
12812 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
12813 if !is_stmt_modifier
12814 && !matches!(next, Token::LBracket | Token::LBrace)
12815 && (next.is_term_start()
12816 || matches!(
12817 next,
12818 Token::DoubleString(_)
12819 | Token::BacktickString(_)
12820 | Token::SingleString(_)
12821 ))
12822 {
12823 Some(format!("${v}"))
12825 } else {
12826 self.pos = saved;
12827 None
12828 }
12829 }
12830 } else {
12831 None
12832 };
12833 let args =
12838 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
12839 let line_topic = self.peek_line();
12840 self.advance(); self.advance(); vec![Expr {
12843 kind: ExprKind::ScalarVar("_".into()),
12844 line: line_topic,
12845 }]
12846 } else {
12847 self.parse_list_until_terminator()?
12848 };
12849 Ok(Expr {
12850 kind: make(handle, args),
12851 line,
12852 })
12853 }
12854
12855 fn parse_block_list(&mut self) -> PerlResult<(Block, Expr)> {
12856 let block = self.parse_block()?;
12857 let block_end_line = self.prev_line();
12858 self.eat(&Token::Comma);
12859 if self.in_pipe_rhs()
12863 && (matches!(
12864 self.peek(),
12865 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
12866 ) || self.peek_line() > block_end_line)
12867 {
12868 let line = self.peek_line();
12869 return Ok((block, self.pipe_placeholder_list(line)));
12870 }
12871 let list = self.parse_expression()?;
12872 Ok((block, list))
12873 }
12874
12875 fn parse_comma_expr_list_with_timeout_tail(
12878 &mut self,
12879 paren: bool,
12880 ) -> PerlResult<(Vec<Expr>, Option<Expr>)> {
12881 let mut parts = vec![self.parse_assign_expr()?];
12882 loop {
12883 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12884 break;
12885 }
12886 if paren && matches!(self.peek(), Token::RParen) {
12887 break;
12888 }
12889 if matches!(
12890 self.peek(),
12891 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12892 ) {
12893 break;
12894 }
12895 if self.peek_is_postfix_stmt_modifier_keyword() {
12896 break;
12897 }
12898 if let Token::Ident(ref kw) = self.peek().clone() {
12899 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
12900 self.advance();
12901 self.expect(&Token::FatArrow)?;
12902 let t = self.parse_assign_expr()?;
12903 return Ok((parts, Some(t)));
12904 }
12905 }
12906 parts.push(self.parse_assign_expr()?);
12907 }
12908 Ok((parts, None))
12909 }
12910
12911 fn parse_init_block_then_list_optional_progress(
12913 &mut self,
12914 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
12915 let init = self.parse_assign_expr()?;
12916 self.expect(&Token::Comma)?;
12917 let block = self.parse_block_or_bareword_block()?;
12918 self.eat(&Token::Comma);
12919 let line = self.peek_line();
12920 if let Token::Ident(ref kw) = self.peek().clone() {
12921 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12922 self.advance();
12923 self.expect(&Token::FatArrow)?;
12924 let prog = self.parse_assign_expr()?;
12925 return Ok((
12926 init,
12927 block,
12928 Expr {
12929 kind: ExprKind::List(vec![]),
12930 line,
12931 },
12932 Some(prog),
12933 ));
12934 }
12935 }
12936 if matches!(
12937 self.peek(),
12938 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12939 ) {
12940 return Ok((
12941 init,
12942 block,
12943 Expr {
12944 kind: ExprKind::List(vec![]),
12945 line,
12946 },
12947 None,
12948 ));
12949 }
12950 let mut parts = vec![self.parse_assign_expr()?];
12951 loop {
12952 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12953 break;
12954 }
12955 if matches!(
12956 self.peek(),
12957 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
12958 ) {
12959 break;
12960 }
12961 if self.peek_is_postfix_stmt_modifier_keyword() {
12962 break;
12963 }
12964 if let Token::Ident(ref kw) = self.peek().clone() {
12965 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12966 self.advance();
12967 self.expect(&Token::FatArrow)?;
12968 let prog = self.parse_assign_expr()?;
12969 return Ok((init, block, merge_expr_list(parts), Some(prog)));
12970 }
12971 }
12972 parts.push(self.parse_assign_expr()?);
12973 }
12974 Ok((init, block, merge_expr_list(parts), None))
12975 }
12976
12977 fn parse_cluster_block_then_list_optional_progress(
12979 &mut self,
12980 ) -> PerlResult<(Expr, Block, Expr, Option<Expr>)> {
12981 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
12984 let cluster = self.parse_assign_expr();
12985 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
12986 let cluster = cluster?;
12987 self.eat(&Token::Comma);
12989 let block = self.parse_block_or_bareword_block()?;
12990 let block_end_line = self.prev_line();
12991 self.eat(&Token::Comma);
12992 let line = self.peek_line();
12993 if let Token::Ident(ref kw) = self.peek().clone() {
12994 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
12995 self.advance();
12996 self.expect(&Token::FatArrow)?;
12997 let prog = self.parse_assign_expr_stop_at_pipe()?;
12998 return Ok((
12999 cluster,
13000 block,
13001 Expr {
13002 kind: ExprKind::List(vec![]),
13003 line,
13004 },
13005 Some(prog),
13006 ));
13007 }
13008 }
13009 let empty_list_ok = matches!(
13010 self.peek(),
13011 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13012 ) || (self.in_pipe_rhs()
13013 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13014 if empty_list_ok {
13015 return Ok((
13016 cluster,
13017 block,
13018 Expr {
13019 kind: ExprKind::List(vec![]),
13020 line,
13021 },
13022 None,
13023 ));
13024 }
13025 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13026 loop {
13027 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13028 break;
13029 }
13030 if matches!(
13031 self.peek(),
13032 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13033 ) {
13034 break;
13035 }
13036 if self.peek_is_postfix_stmt_modifier_keyword() {
13037 break;
13038 }
13039 if let Token::Ident(ref kw) = self.peek().clone() {
13040 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13041 self.advance();
13042 self.expect(&Token::FatArrow)?;
13043 let prog = self.parse_assign_expr_stop_at_pipe()?;
13044 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
13045 }
13046 }
13047 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13048 }
13049 Ok((cluster, block, merge_expr_list(parts), None))
13050 }
13051
13052 fn parse_block_then_list_optional_progress(
13061 &mut self,
13062 ) -> PerlResult<(Block, Expr, Option<Expr>)> {
13063 let block = self.parse_block_or_bareword_block()?;
13064 let block_end_line = self.prev_line();
13065 self.eat(&Token::Comma);
13066 let line = self.peek_line();
13067 if let Token::Ident(ref kw) = self.peek().clone() {
13068 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13069 self.advance();
13070 self.expect(&Token::FatArrow)?;
13071 let prog = self.parse_assign_expr_stop_at_pipe()?;
13072 return Ok((
13073 block,
13074 Expr {
13075 kind: ExprKind::List(vec![]),
13076 line,
13077 },
13078 Some(prog),
13079 ));
13080 }
13081 }
13082 let empty_list_ok = matches!(
13090 self.peek(),
13091 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13092 ) || (self.in_pipe_rhs()
13093 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13094 if empty_list_ok {
13095 return Ok((
13096 block,
13097 Expr {
13098 kind: ExprKind::List(vec![]),
13099 line,
13100 },
13101 None,
13102 ));
13103 }
13104 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13105 loop {
13106 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13107 break;
13108 }
13109 if matches!(
13110 self.peek(),
13111 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13112 ) {
13113 break;
13114 }
13115 if self.peek_is_postfix_stmt_modifier_keyword() {
13116 break;
13117 }
13118 if let Token::Ident(ref kw) = self.peek().clone() {
13119 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13120 self.advance();
13121 self.expect(&Token::FatArrow)?;
13122 let prog = self.parse_assign_expr_stop_at_pipe()?;
13123 return Ok((block, merge_expr_list(parts), Some(prog)));
13124 }
13125 }
13126 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13127 }
13128 Ok((block, merge_expr_list(parts), None))
13129 }
13130
13131 fn parse_fan_count_and_block(&mut self, line: usize) -> PerlResult<(Option<Box<Expr>>, Block)> {
13133 if matches!(self.peek(), Token::LBrace) {
13135 let block = self.parse_block()?;
13136 return Ok((None, block));
13137 }
13138 let saved = self.pos;
13139 let first = self.parse_postfix()?;
13141 if matches!(self.peek(), Token::LBrace) {
13142 let block = self.parse_block()?;
13144 Ok((Some(Box::new(first)), block))
13145 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
13146 || (matches!(self.peek(), Token::Comma)
13147 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
13148 {
13149 let block = self.bareword_to_no_arg_block(first);
13151 Ok((None, block))
13152 } else if matches!(first.kind, ExprKind::Integer(_)) {
13153 self.eat(&Token::Comma);
13155 let body = self.parse_fan_blockless_body(line)?;
13156 Ok((Some(Box::new(first)), body))
13157 } else {
13158 self.pos = saved;
13161 let body = self.parse_fan_blockless_body(line)?;
13162 Ok((None, body))
13163 }
13164 }
13165
13166 fn parse_fan_blockless_body(&mut self, line: usize) -> PerlResult<Block> {
13168 if matches!(self.peek(), Token::LBrace) {
13169 return self.parse_block();
13170 }
13171 if let Token::Ident(ref name) = self.peek().clone() {
13173 if matches!(
13174 self.peek_at(1),
13175 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13176 ) {
13177 let name = name.clone();
13178 self.advance();
13179 let body = Expr {
13180 kind: ExprKind::FuncCall { name, args: vec![] },
13181 line,
13182 };
13183 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13184 }
13185 }
13186 let expr = self.parse_assign_expr_stop_at_pipe()?;
13188 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13189 }
13190
13191 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
13194 let line = expr.line;
13195 let body = match &expr.kind {
13196 ExprKind::Bareword(name) => Expr {
13197 kind: ExprKind::FuncCall {
13198 name: name.clone(),
13199 args: vec![],
13200 },
13201 line,
13202 },
13203 _ => expr,
13204 };
13205 vec![Statement::new(StmtKind::Expression(body), line)]
13206 }
13207
13208 fn parse_block_or_bareword_block(&mut self) -> PerlResult<Block> {
13217 if matches!(self.peek(), Token::LBrace) {
13218 return self.parse_block();
13219 }
13220 let line = self.peek_line();
13221 if let Token::Ident(ref name) = self.peek().clone() {
13224 if matches!(
13225 self.peek_at(1),
13226 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13227 ) {
13228 let name = name.clone();
13229 self.advance();
13230 let body = Expr {
13231 kind: ExprKind::FuncCall {
13232 name,
13233 args: vec![Expr {
13234 kind: ExprKind::ScalarVar("_".to_string()),
13235 line,
13236 }],
13237 },
13238 line,
13239 };
13240 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13241 }
13242 }
13243 let expr = self.parse_assign_expr_stop_at_pipe()?;
13245 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13246 }
13247
13248 fn parse_block_or_bareword_block_no_args(&mut self) -> PerlResult<Block> {
13253 if matches!(self.peek(), Token::LBrace) {
13254 return self.parse_block();
13255 }
13256 let line = self.peek_line();
13257 if let Token::Ident(ref name) = self.peek().clone() {
13258 if matches!(
13259 self.peek_at(1),
13260 Token::Comma
13261 | Token::Semicolon
13262 | Token::RBrace
13263 | Token::Eof
13264 | Token::PipeForward
13265 | Token::Integer(_)
13266 ) {
13267 let name = name.clone();
13268 self.advance();
13269 let body = Expr {
13270 kind: ExprKind::FuncCall { name, args: vec![] },
13271 line,
13272 };
13273 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13274 }
13275 }
13276 let expr = self.parse_postfix()?;
13277 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13278 }
13279
13280 fn is_known_bareword(name: &str) -> bool {
13288 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
13289 }
13290
13291 fn is_try_builtin_name(name: &str) -> bool {
13297 crate::builtins::BUILTIN_ARMS
13298 .iter()
13299 .any(|arm| arm.contains(&name))
13300 }
13301
13302 fn is_perl5_core(name: &str) -> bool {
13307 matches!(
13308 name,
13309 "map" | "grep" | "sort" | "reverse" | "join" | "split"
13311 | "push" | "pop" | "shift" | "unshift" | "splice"
13312 | "splice_last" | "splice1" | "spl_last"
13313 | "pack" | "unpack"
13314 | "unpack_first" | "unpack1" | "up1"
13315 | "keys" | "values" | "each"
13317 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
13319 | "lc" | "uc" | "lcfirst" | "ucfirst"
13320 | "length" | "substr" | "index" | "rindex"
13321 | "sprintf" | "printf" | "print" | "say"
13322 | "pos" | "quotemeta" | "study"
13323 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
13325 | "exp" | "log" | "rand" | "srand"
13326 | "time" | "localtime" | "gmtime"
13328 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
13330 | "caller" | "delete" | "exists" | "bless" | "prototype"
13331 | "tie" | "untie" | "tied"
13332 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
13334 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
13335 | "format" | "formline" | "select" | "vec"
13336 | "sysopen" | "sysread" | "sysseek" | "syswrite"
13337 | "stat" | "lstat" | "rename" | "unlink" | "utime"
13339 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
13340 | "glob" | "opendir" | "readdir" | "closedir"
13341 | "link" | "readlink" | "symlink"
13342 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
13344 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
13346 | "semctl" | "semget" | "semop"
13347 | "shmctl" | "shmget" | "shmread" | "shmwrite"
13348 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
13350 | "fork" | "wait" | "waitpid" | "kill" | "alarm" | "sleep"
13351 | "chroot" | "times" | "umask" | "reset"
13352 | "getpgrp" | "setpgrp" | "getppid"
13353 | "getpriority" | "setpriority"
13354 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
13356 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
13357 | "getpeername" | "getsockname"
13358 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
13360 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
13361 | "getlogin"
13362 | "gethostbyname" | "gethostbyaddr" | "gethostent"
13363 | "getnetbyname" | "getnetent"
13364 | "getprotobyname" | "getprotoent"
13365 | "getservbyname" | "getservent"
13366 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
13367 | "endpwent" | "endgrent"
13368 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
13369 | "return" | "do" | "eval" | "require"
13371 | "my" | "our" | "local" | "use" | "no"
13372 | "sub" | "if" | "unless" | "while" | "until"
13373 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
13374 | "not" | "and" | "or"
13375 | "qw" | "qq" | "q"
13377 | "BEGIN" | "END"
13379 )
13380 }
13381
13382 fn stryke_extension_name(name: &str) -> Option<&str> {
13385 match name {
13386 | "proceed" | "intercept_list" | "intercept_remove" | "intercept_clear"
13388 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
13390 | "pgrep" | "pfor" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
13391 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
13392 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
13393 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
13394 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
13395 | "pmaps" | "pflat_maps" | "pgreps"
13396 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "fi" | "find_all" | "reduce" | "fold"
13398 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
13399 | "first" | "detect" | "find" | "compact" | "concat" | "chain" | "reject" | "grepv" | "flatten" | "set"
13400 | "min_by" | "max_by" | "sort_by" | "tally" | "find_index"
13401 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
13402 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
13403 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
13404 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
13405 | "zip_with" | "count_by" | "skip" | "first_or"
13406 | "input" | "lines" | "words" | "chars" | "cindex" | "crindex"
13408 | "digits" | "letters" | "letters_uc" | "letters_lc"
13409 | "punctuation" | "punct"
13410 | "sentences" | "sents"
13411 | "paragraphs" | "paras" | "sections" | "sects"
13412 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
13413 | "trim" | "avg" | "stddev"
13414 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
13415 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
13416 | "frequencies" | "freq" | "pfrequencies" | "pfreq"
13417 | "interleave" | "ddump" | "stringify" | "str" | "top"
13418 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
13419 | "to_html" | "to_markdown" | "to_table" | "xopen"
13420 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
13421 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste" | "preview"
13422 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
13423 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
13424 | "to_hash" | "to_set"
13425 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
13426 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
13427 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
13428 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
13429 | "inc" | "dec" | "elapsed"
13430 | "files" | "filesf" | "f" | "fr" | "dirs" | "d" | "dr" | "sym_links"
13432 | "sockets" | "pipes" | "block_devices" | "char_devices" | "exe" | "executables"
13433 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
13434 | "copy" | "move" | "spurt" | "spit" | "read_bytes" | "which"
13435 | "getcwd" | "touch" | "gethostname" | "uname"
13436 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
13438 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
13439 | "par_fetch" | "par_csv_read" | "par_pipeline"
13440 | "json_encode" | "json_decode" | "json_jq"
13441 | "http_request" | "serve" | "ssh"
13442 | "html_parse" | "css_select" | "xml_parse" | "xpath"
13443 | "smtp_send"
13444 | "net_interfaces" | "net_ipv4" | "net_ipv6" | "net_mac"
13445 | "net_public_ip" | "net_dns" | "net_reverse_dns"
13446 | "net_ping" | "net_port_open" | "net_ports_scan"
13447 | "net_latency" | "net_download" | "net_headers"
13448 | "net_dns_servers" | "net_gateway" | "net_whois" | "net_hostname"
13449 | "git_log" | "git_status" | "git_diff" | "git_branches"
13451 | "git_tags" | "git_blame" | "git_authors" | "git_files"
13452 | "git_show" | "git_root"
13453 | "audio_convert" | "audio_info" | "id3_read" | "id3_write"
13455 | "to_pdf" | "pdf_text" | "pdf_pages"
13457 | "toml_encode" | "toml_decode"
13459 | "yaml_encode" | "yaml_decode"
13460 | "xml_encode" | "xml_decode"
13461 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
13463 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
13464 | "shake128" | "shake256"
13465 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
13466 | "uuid" | "crc32"
13467 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
13468 | "ripemd160" | "rmd160" | "md4"
13469 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
13470 | "murmur3" | "murmur3_32" | "murmur3_128"
13471 | "siphash" | "siphash_keyed"
13472 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
13473 | "poly1305" | "poly1305_mac"
13474 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
13475 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
13476 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
13477 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
13478 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
13479 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
13480 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
13481 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
13482 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
13483 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
13484 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
13485 | "secretbox" | "secretbox_seal" | "secretbox_open"
13486 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
13487 | "nacl_box_open" | "box_open"
13488 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
13489 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
13490 | "barcode_ean13" | "ean13" | "barcode_svg"
13491 | "argon2_hash" | "argon2" | "argon2_verify"
13492 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
13493 | "scrypt_hash" | "scrypt" | "scrypt_verify"
13494 | "pbkdf2" | "pbkdf2_derive"
13495 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
13496 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
13497 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
13498 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
13499 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
13500 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
13501 | "ecdsa_p256_verify" | "p256_verify"
13502 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
13503 | "ecdsa_p384_verify" | "p384_verify"
13504 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
13505 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
13506 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
13507 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
13508 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
13509 | "ed25519_verify" | "ed_verify"
13510 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
13511 | "base64_encode" | "base64_decode"
13512 | "hex_encode" | "hex_decode"
13513 | "url_encode" | "url_decode"
13514 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
13515 | "brotli" | "br" | "brotli_decode" | "ubr"
13516 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
13517 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
13518 | "lz4" | "lz4_decode" | "unlz4"
13519 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
13520 | "lzw" | "lzw_decode" | "unlzw"
13521 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
13522 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
13523 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
13524 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
13526 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
13527 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
13528 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
13529 | "gammaincc_reg" | "gamma_ur"
13530 | "datetime_utc" | "datetime_now_tz" | "now"
13532 | "datetime_format_tz" | "datetime_add_seconds"
13533 | "datetime_from_epoch"
13534 | "datetime_parse_rfc3339" | "datetime_parse_local"
13535 | "datetime_strftime"
13536 | "dateseq" | "dategrep" | "dateround" | "datesort"
13537 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
13539 | "log_info" | "log_warn" | "log_error"
13541 | "log_debug" | "log_trace" | "log_json" | "log_level"
13542 | "async" | "spawn" | "trace" | "timer" | "bench"
13544 | "eval_timeout" | "retry" | "rate_limit" | "every"
13545 | "gen" | "watch"
13546 | "cache_clear" | "cache_exists" | "cache_stats" | "cacheview"
13548 | "assert_eq" | "assert_ne" | "assert_ok" | "assert_err"
13550 | "assert_true" | "assert_false"
13551 | "assert_gt" | "assert_lt" | "assert_ge" | "assert_le"
13552 | "assert_match" | "assert_contains" | "assert_near" | "assert_dies"
13553 | "test_run" | "run_tests" | "test_skip" | "skip_test" | "skip_assert"
13554 | "mounts" | "du" | "du_tree" | "process_list"
13556 | "thread_count" | "pool_info" | "par_bench"
13557 | "stress_cpu" | "scpu" | "stress_mem" | "smem"
13559 | "stress_io" | "sio" | "stress_test" | "st"
13560 | "heat" | "fire" | "fire_and_forget" | "pin"
13561 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
13563 | "stdin"
13564 | "__stryke_rust_compile"
13566 | "vec_set_value"
13567 | "p" | "rev"
13569 | "even" | "odd" | "zero" | "nonzero"
13571 | "positive" | "pos_n" | "negative" | "neg_n"
13572 | "sign" | "negate" | "double" | "triple" | "half"
13573 | "identity" | "id"
13574 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
13575 | "gcd" | "lcm" | "min2" | "max2"
13576 | "log2" | "log10" | "hypot"
13577 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
13578 | "pow2" | "abs_diff"
13579 | "factorial" | "fact" | "fibonacci" | "fib"
13580 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
13581 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
13582 | "median" | "mode_val" | "variance"
13583 | "is_empty" | "is_blank" | "is_numeric"
13585 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
13586 | "is_space" | "is_whitespace"
13587 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
13588 | "capitalize" | "cap" | "swap_case" | "repeat"
13589 | "title_case" | "title" | "squish"
13590 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
13591 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
13592 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
13593 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
13595 | "is_code" | "is_coderef" | "is_ref"
13596 | "is_undef" | "is_defined" | "is_def"
13597 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
13598 | "invert" | "merge_hash"
13600 | "hash_map_values" | "hash_filter_keys" | "hash_filter_values"
13601 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
13602 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
13604 | "riffle" | "intersperse" | "every_nth"
13606 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
13607 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
13609 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
13610 | "bits_count" | "popcount" | "leading_zeros" | "lz"
13611 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
13612 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
13614 | "shift_left" | "shl" | "shift_right" | "shr"
13615 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
13616 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
13618 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
13620 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
13621 | "yards_to_m" | "m_to_yards"
13622 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
13624 | "stone_to_kg" | "kg_to_stone"
13625 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
13627 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
13628 | "kb_to_mb" | "mb_to_gb"
13629 | "bits_to_bytes" | "bytes_to_bits"
13630 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
13632 | "seconds_to_hours" | "hours_to_seconds"
13633 | "seconds_to_days" | "days_to_seconds"
13634 | "minutes_to_hours" | "hours_to_minutes"
13635 | "hours_to_days" | "days_to_hours"
13636 | "is_leap_year" | "is_leap" | "days_in_month"
13638 | "month_name" | "month_short"
13639 | "weekday_name" | "weekday_short" | "quarter_of"
13640 | "now_ms" | "now_us" | "now_ns"
13642 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
13643 | "rgb_to_hex" | "hex_to_rgb"
13645 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
13646 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
13647 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
13648 | "strip_ansi"
13649 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
13650 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
13651 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
13652 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
13653 | "bright_magenta" | "bright_cyan" | "bright_white"
13654 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
13655 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
13656 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
13657 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
13658 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
13659 | "white_bold" | "bold_white"
13660 | "blink" | "rapid_blink" | "hidden" | "overline"
13661 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
13662 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
13663 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
13664 | "ipv4_to_int" | "int_to_ipv4"
13666 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
13667 | "path_ext" | "path_stem" | "path_parent" | "path_join" | "path_split"
13669 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
13670 | "const_fn" | "always_true" | "always_false"
13672 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
13673 | "count_eq" | "count_ne" | "all_eq"
13675 | "all_distinct" | "all_unique" | "has_duplicates"
13676 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
13677 | "quote" | "single_quote" | "unquote"
13679 | "extract_between" | "ellipsis"
13680 | "coin_flip" | "dice_roll"
13682 | "random_int" | "random_float" | "random_bool"
13683 | "random_choice" | "random_between"
13684 | "random_string" | "random_alpha" | "random_digit"
13685 | "refresh_stashes"
13687 | "os_name" | "os_arch" | "num_cpus"
13689 | "pid" | "ppid" | "uid" | "gid"
13690 | "username" | "home_dir" | "temp_dir"
13691 | "mem_total" | "mem_free" | "mem_used"
13692 | "swap_total" | "swap_free" | "swap_used"
13693 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
13694 | "load_avg" | "sys_uptime" | "page_size"
13695 | "os_version" | "os_family" | "endianness" | "pointer_width"
13696 | "proc_mem" | "rss"
13697 | "transpose" | "unzip"
13699 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
13700 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
13701 | "tan" | "asin" | "acos" | "atan"
13703 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
13704 | "sqr" | "cube_fn"
13705 | "mod_op" | "ceil_div" | "floor_div"
13706 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
13707 | "degrees" | "radians"
13708 | "min_abs" | "max_abs"
13709 | "saturate" | "sat01" | "wrap_around"
13710 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
13712 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
13713 | "first_word" | "last_word"
13714 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
13715 | "lowercase" | "uppercase"
13716 | "pascal_case" | "pc_case"
13717 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
13718 | "is_palindrome" | "hamming_distance"
13719 | "longest_common_prefix" | "lcp"
13720 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
13721 | "replace_first" | "replace_all_str"
13722 | "contains_any" | "contains_all"
13723 | "starts_with_any" | "ends_with_any"
13724 | "is_pair" | "is_triple"
13726 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
13727 | "is_empty_arr" | "is_empty_hash"
13728 | "is_subset" | "is_superset" | "is_permutation"
13729 | "first_eq" | "last_eq"
13731 | "index_of" | "last_index_of" | "positions_of"
13732 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
13733 | "distinct_count" | "longest" | "shortest"
13734 | "array_union" | "list_union"
13735 | "array_intersection" | "list_intersection"
13736 | "array_difference" | "list_difference"
13737 | "symmetric_diff" | "group_of_n" | "chunk_n"
13738 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
13739 | "pick_keys" | "pick" | "omit_keys" | "omit"
13741 | "map_keys_fn" | "map_values_fn"
13742 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
13743 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
13744 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
13746 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
13748 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
13750 | "argc" | "script_name"
13751 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
13752 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
13754 | "email_domain" | "email_local"
13756 | "url_host" | "url_path" | "url_query" | "url_scheme"
13757 | "file_size" | "fsize" | "file_mtime" | "mtime"
13759 | "file_atime" | "atime" | "file_ctime" | "ctime"
13760 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
13761 | "path_is_abs" | "path_is_rel"
13762 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
13764 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
13765 | "reverse_list" | "list_reverse"
13766 | "without" | "without_nth" | "take_last" | "drop_last"
13767 | "pairwise" | "zipmap"
13768 | "format_bytes" | "human_bytes"
13769 | "format_duration" | "human_duration"
13770 | "format_number" | "group_number"
13771 | "format_percent" | "pad_number"
13772 | "spaceship" | "cmp_num" | "cmp_str"
13773 | "compare_versions" | "version_cmp"
13774 | "hash_insert" | "hash_update" | "hash_delete"
13775 | "matches_regex" | "re_match"
13776 | "count_regex_matches" | "regex_extract"
13777 | "regex_split_str" | "regex_replace_str"
13778 | "shuffle_chars" | "random_char" | "nth_word"
13779 | "head_lines" | "tail_lines" | "count_substring"
13780 | "is_valid_hex" | "hex_upper" | "hex_lower"
13781 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
13782 | "us_to_ns" | "ns_to_us"
13783 | "liters_to_gallons" | "gallons_to_liters"
13784 | "liters_to_ml" | "ml_to_liters"
13785 | "cups_to_ml" | "ml_to_cups"
13786 | "newtons_to_lbf" | "lbf_to_newtons"
13787 | "joules_to_cal" | "cal_to_joules"
13788 | "watts_to_hp" | "hp_to_watts"
13789 | "pascals_to_psi" | "psi_to_pascals"
13790 | "bar_to_pascals" | "pascals_to_bar"
13791 | "match"
13793 | "fst" | "rest" | "rst" | "second" | "snd"
13795 | "last_clj" | "lastc" | "butlast" | "bl"
13796 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
13797 | "cons" | "conj"
13798 | "peek_clj" | "pkc" | "pop_clj" | "popc"
13799 | "some" | "not_any" | "not_every"
13800 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
13801 | "fnil" | "juxt"
13802 | "memoize" | "memo" | "curry" | "once"
13803 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
13804 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
13805 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
13806 | "reductions" | "rdcs"
13807 | "partition_by" | "pby" | "partition_all" | "pall"
13808 | "split_at" | "spat" | "split_with" | "spw"
13809 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
13810 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
13811 | "apply" | "appl"
13812 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
13814 | "zip_longest" | "zipl" | "zip_fill" | "zipf" | "combinations" | "comb" | "permutations" | "perm"
13815 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
13816 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
13817 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
13818 | "each_slice" | "eslice" | "each_cons" | "econs"
13819 | "one" | "none_match" | "nonem"
13820 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
13821 | "minmax" | "mmx" | "minmax_by" | "mmxb"
13822 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
13823 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
13824 | "sum_by" | "sumb" | "uniq_by" | "uqb"
13825 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
13826 | "step" | "upto" | "downto"
13827 | "find_last" | "fndl" | "find_last_index" | "fndli"
13829 | "at_index" | "ati" | "replace_at" | "repa"
13830 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
13831 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
13832 | "object_keys" | "okeys" | "object_values" | "ovals"
13833 | "object_entries" | "oents" | "object_from_entries" | "ofents"
13834 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
13836 | "nub" | "sort_on" | "srton"
13837 | "intersperse_val" | "isp" | "intercalate" | "ical"
13838 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
13839 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
13840 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
13842 | "partition_either" | "peith" | "try_fold" | "tfld"
13843 | "map_while" | "mapw" | "inspect" | "insp"
13844 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
13846 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
13848 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
13849 | "lines_from" | "lfrm" | "unlines" | "unlns"
13850 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
13851 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
13852 | "interpose" | "ipos" | "partition_n" | "partn"
13853 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
13854 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
13855 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
13857 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
13858 | "each_with_object" | "ewo" | "reduce_right" | "redr"
13859 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
13860 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
13861 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
13862 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
13863 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
13864 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
13865 | "union_list" | "unionl" | "intersect_list" | "intl"
13866 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
13867 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
13869 | "split_regex" | "splre" | "replace_regex" | "replre"
13870 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
13871 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
13872 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
13873 | "pluralize" | "plur" | "ordinalize" | "ordn"
13874 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
13875 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
13876 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
13877 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
13878 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
13880 | "dot_product" | "dotp" | "cross_product" | "crossp"
13881 | "matrix_mul" | "matmul" | "mm"
13882 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
13883 | "distance" | "dist" | "manhattan_distance" | "mdist"
13884 | "covariance" | "cov" | "correlation" | "corr"
13885 | "iqr" | "quantile" | "qntl" | "quantiles" | "qntls"
13886 | "lsp_completion_words" | "lsp_words"
13887 | "doctor" | "health"
13888 | "clamp_int" | "clpi"
13889 | "in_range" | "inrng" | "wrap_range" | "wrprng"
13890 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
13891 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
13893 | "diff_days" | "diffd" | "diff_hours" | "diffh"
13894 | "start_of_day" | "sod" | "end_of_day" | "eod"
13895 | "start_of_hour" | "soh" | "start_of_minute" | "som"
13896 | "urle" | "urld"
13898 | "html_encode" | "htmle" | "html_decode" | "htmld"
13899 | "adler32" | "adl32" | "fnv1a" | "djb2"
13900 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
13902 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
13903 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
13904 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
13905 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
13907 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
13908 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
13909 | "partition_point" | "ppt" | "lower_bound" | "lbound"
13910 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
13911 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
13913 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
13914 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
13915 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
13916 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
13917 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
13918 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
13919 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
13921 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
13922 | "connected_components_graph" | "ccgraph"
13923 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
13924 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
13926 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
13927 | "is_hostname_valid" | "ishost"
13928 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
13929 | "is_iso_datetime" | "isisodtm"
13930 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
13931 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
13933 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
13934 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
13935 | "find_all_indices" | "fndalli"
13936 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
13937 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
13938 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
13940 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
13941 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
13942 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
13943 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
13944 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
13945 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
13946 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
13947 | "longest_run" | "lrun" | "longest_increasing" | "linc"
13949 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
13950 | "majority_element" | "majority" | "kth_largest" | "kthl"
13951 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
13952 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
13953 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
13955 | "overlap_coefficient" | "overlapcoef"
13956 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
13957 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
13959 | "hamdist" | "jaro_similarity" | "jarosim"
13960 | "longest_common_substring" | "lcsub"
13961 | "longest_common_subsequence" | "lcseq"
13962 | "count_words" | "wcount" | "count_lines" | "lcount"
13963 | "count_chars" | "ccount" | "count_bytes" | "bcount"
13964 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
13966 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
13967 | "mobius" | "mob" | "is_squarefree" | "issqfr"
13968 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
13969 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
13970 | "day_of_year" | "doy" | "week_of_year" | "woy"
13972 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
13973 | "age_in_years" | "ageyrs"
13974 | "when_true" | "when_false" | "if_else" | "clamp_fn"
13977 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
13978 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
13979 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
13980 | "coalesce" | "default_to" | "fallback"
13981 | "apply_list" | "zip_apply" | "scan"
13982 | "keep_if" | "reject_if" | "group_consecutive"
13983 | "after_n" | "before_n" | "clamp_list" | "normalize_list" | "softmax"
13984
13985 | "matrix_multiply" | "mat_mul"
13989 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
13990
13991
13992
13993 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
13994 | "linspace" | "arange"
13995 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
13997 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
13998 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
14000 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
14001 | "stack_new" | "queue_new" | "lru_new"
14003 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
14004 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
14005 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
14007 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
14008 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
14009 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
14010 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
14011 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
14013 | "planck" | "speed_of_light" | "sqrt2"
14014 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
14016 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
14017 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
14018 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
14019 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
14020 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
14021 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
14023 | "cosine_similarity" | "cube_root" | "entropy" | "float_bits" | "fma"
14024 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
14025 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
14026 | "sigmoid" | "signum" | "square_root"
14027 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
14029 | "squares_seq" | "triangular_seq"
14030 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
14032 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
14033 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
14034 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
14035 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
14036 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
14037 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
14038 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
14039 | "xor_strings"
14040 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
14042 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
14043 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
14044 | "group_by_size" | "hash_from_list"
14045 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
14046 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
14047 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
14048 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
14049 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
14050 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
14051 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
14052 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
14053 | "wrap_index" | "digits_of"
14054 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
14056 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
14057 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
14058 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
14059 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
14060 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
14061 | "count_digits" | "count_letters" | "count_lower" | "count_match"
14063 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
14064 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
14065 | "truthy_count" | "undef_count"
14066 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
14068 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
14069 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
14070 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
14071 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
14072 | "range_exclusive" | "range_inclusive"
14073 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
14075 | "collatz_length" | "collatz_sequence" | "convolution" | "cross_entropy"
14076 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
14077 | "epsilon" | "euclidean_distance" | "euler_number" | "exponential_moving_average"
14078 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
14079 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
14080 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
14081 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
14082 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
14083 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
14084 | "tribonacci" | "weighted_mean" | "winsorize"
14085 | "chi_square_stat" | "describe" | "five_number_summary"
14087 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
14088 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
14089 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
14090 | "z_score" | "z_scores"
14091 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
14093 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
14094 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
14095 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
14096 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
14098 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
14099 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
14100 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
14101 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
14103 | "circle_from_three_points" | "convex_hull" | "ellipse_perimeter"
14104 | "frustum_volume" | "haversine_distance" | "line_intersection"
14105 | "point_in_polygon" | "polygon_perimeter" | "pyramid_volume"
14106 | "reflect_point" | "scale_point" | "sector_area"
14107 | "torus_surface" | "torus_volume" | "translate_point"
14108 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
14109 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
14111 | "gravitational_constant" | "phi" | "pi" | "planck_constant" | "proton_mass"
14112 | "sol" | "tau"
14113 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
14115 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
14117 | "bond_price" | "bond_yield" | "capm" | "continuous_compound"
14118 | "discounted_payback" | "duration" | "irr"
14119 | "max_drawdown" | "modified_duration" | "nper" | "num_periods" | "payback_period"
14120 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
14121 | "wacc" | "xirr"
14122 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
14124 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
14125 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
14126 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
14127 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
14128 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
14129 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
14131 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
14132 | "to_emoji_num"
14133 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
14135 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
14137 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
14139 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
14140 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
14141 | "rgb_to_hsl" | "rgb_to_hsv"
14142 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
14144 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
14145 | "matrix_transpose"
14146 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
14148 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
14149 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
14150 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
14151 | "zero_crossings"
14152 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
14154 | "downsample" | "energy" | "envelope" | "highpass_filter" | "idft"
14155 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
14156 | "power_spectrum" | "resample" | "spectral_centroid" | "spectrogram" | "upsample"
14157 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
14158 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
14160 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
14161 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
14162 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
14164 | "sierpinski" | "tower_of_hanoi" | "truth_table"
14165 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
14167 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
14169 | "geometric_series" | "stirling_approx"
14170 | "double_factorial" | "rising_factorial" | "falling_factorial"
14171 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
14172 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
14173 | "map_range"
14174 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
14176 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
14177 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
14178 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
14179 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
14180 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
14181 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
14182 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
14183 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
14184 | "projectile_range" | "projectile_max_height" | "projectile_time"
14185 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
14186 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
14187 | "lens_power" | "thin_lens" | "magnification_lens"
14188 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
14190 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
14191 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
14192 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
14194 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
14195 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
14196 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
14197 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
14198 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
14199 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
14200 | "matrix_solve" | "msolve" | "solve"
14202 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
14203 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
14204 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
14205 | "matrix_pinv" | "mpinv" | "pinv"
14206 | "matrix_cholesky" | "mchol" | "cholesky"
14207 | "matrix_det_general" | "mdetg" | "det"
14208 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
14210 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
14211 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
14212 | "confidence_interval" | "ci"
14213 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
14215 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
14216 | "t_pdf" | "tpdf" | "student_pdf"
14217 | "f_pdf" | "fpdf" | "fisher_pdf"
14218 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
14219 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
14220 | "pareto_pdf" | "paretopdf"
14221 | "lagrange_interp" | "lagrange" | "linterp"
14223 | "cubic_spline" | "cspline" | "spline"
14224 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
14225 | "trapz" | "trapezoid" | "simpson" | "simps"
14227 | "numerical_diff" | "numdiff" | "diff_array"
14228 | "cumtrapz" | "cumulative_trapz"
14229 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
14231 | "golden_section" | "golden" | "gss"
14232 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
14234 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
14236 | "floyd_warshall" | "floydwarshall" | "apsp"
14237 | "prim_mst" | "mst" | "prim"
14238 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
14240 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
14242 | "silu" | "swish" | "mish" | "softplus"
14243 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
14244 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
14246 | "lambert_w" | "lambertw" | "productlog"
14247 | "bessel_j" | "bessel_y" | "bessel_i" | "bessel_k"
14249 | "hankel_h1" | "hankel_h2" | "bessel_j_zero"
14250 | "airy_ai" | "airy_bi" | "airy_ai_prime" | "airy_bi_prime"
14251 | "spherical_bessel_j" | "spherical_bessel_y"
14252 | "struve_h" | "struve_l" | "kelvin_ber" | "kelvin_bei"
14253 | "legendre_p" | "legendre_q" | "assoc_legendre_p"
14255 | "hermite_h" | "hermite_he" | "laguerre_l" | "assoc_laguerre_l"
14256 | "jacobi_p" | "gegenbauer_c" | "chebyshev_t" | "chebyshev_u"
14257 | "spherical_harmonic_y" | "zernike_r"
14258 | "elliptic_k" | "elliptic_e" | "elliptic_pi" | "elliptic_f"
14260 | "elliptic_e_inc" | "elliptic_pi_inc"
14261 | "carlson_rf" | "carlson_rd" | "carlson_rj"
14262 | "jacobi_sn" | "jacobi_cn" | "jacobi_dn" | "jacobi_am"
14263 | "elliptic_theta"
14264 | "weierstrass_p" | "weierstrass_zeta" | "weierstrass_sigma"
14265 | "zeta" | "riemann_zeta" | "hurwitz_zeta"
14267 | "polylog" | "dilog" | "lerch_phi"
14268 | "riemann_siegel_z" | "riemann_siegel_theta"
14269 | "dirichlet_eta" | "dirichlet_beta"
14270 | "hypergeometric_2f1" | "hyper_2f1"
14272 | "hypergeometric_1f1" | "hyper_1f1" | "kummer_m"
14273 | "hypergeometric_0f1" | "hyper_0f1"
14274 | "hypergeometric_pfq" | "hyper_pfq"
14275 | "hypergeometric_u" | "tricomi_u"
14276 | "dedekind_eta" | "klein_j" | "klein_invariant_j"
14278 | "modular_lambda" | "ramanujan_tau"
14279 | "sin_integral" | "si_int" | "cos_integral" | "ci_int"
14281 | "sinh_integral" | "shi_int" | "cosh_integral" | "chi_int"
14282 | "exp_integral_e" | "ei_n" | "exp_integral_ei" | "ei_int"
14283 | "log_integral" | "li_int" | "fresnel_s" | "fresnel_c"
14284 | "jacobi_symbol" | "kronecker_symbol"
14286 | "primitive_root" | "multiplicative_order"
14287 | "mangoldt_lambda" | "von_mangoldt" | "carmichael_lambda"
14288 | "squares_r" | "thue_morse" | "rudin_shapiro"
14289 | "farey_sequence" | "farey"
14290 | "frobenius_number" | "frobenius_solve" | "stern_brocot"
14291 | "stirling_s1" | "stirling_first" | "bell_polynomial_b" | "bell_y"
14293 | "clebsch_gordan" | "three_j_symbol" | "wigner_3j"
14294 | "six_j_symbol" | "wigner_6j" | "nine_j_symbol" | "wigner_9j"
14295 | "debruijn_sequence" | "debruijn" | "wigner_d"
14296 | "q_pochhammer" | "q_factorial" | "q_binomial"
14298 | "q_hypergeometric_pfq"
14299 | "mittag_leffler_e" | "mittag_leffler"
14300 | "coulomb_wave_f" | "coulomb_wave_g"
14301 | "inverse_erf" | "erfinv" | "inverse_erfc" | "erfcinv"
14303 | "inverse_gamma_regularized" | "gamma_lr_inv"
14304 | "inverse_beta_regularized" | "beta_reg_inv"
14305 | "inverse_jacobi_sn"
14306 | "dirac_delta" | "heaviside_theta" | "heaviside"
14308 | "unit_box" | "unit_triangle"
14309 | "square_wave" | "triangle_wave" | "sawtooth_wave" | "dirac_comb"
14310 | "liouville_lambda" | "jordan_totient" | "ramanujan_sum"
14312 | "cyclotomic_polynomial" | "cyclotomic" | "legendre_symbol"
14313 | "pythagorean_triple_q" | "gen_pythagorean_triple"
14314 | "sophie_germain_q" | "mersenne_q"
14315 | "lucas_lehmer_test" | "lucas_lehmer"
14316 | "continued_fraction" | "from_continued_fraction" | "convergents"
14317 | "best_rational_approximation" | "best_rational"
14318 | "motzkin_number" | "motzkin"
14320 | "narayana_number" | "narayana"
14321 | "delannoy_number" | "delannoy"
14322 | "schroder_number" | "schroder" | "large_schroder"
14323 | "small_schroder_number" | "small_schroder"
14324 | "eulerian_number"
14325 | "bernoulli_polynomial" | "euler_polynomial"
14326 | "pell_number" | "pell" | "pell_lucas_number" | "pell_lucas"
14327 | "perrin_number" | "perrin" | "padovan_number" | "padovan"
14328 | "kronecker_product" | "tensor_product" | "tensor_contract"
14330 | "matrix_rank" | "mrank"
14331 | "companion_matrix" | "companion"
14332 | "characteristic_polynomial" | "charpoly"
14333 | "singular_values" | "svals"
14334 | "nullspace" | "null_space" | "kernel"
14335 | "polynomial_gcd" | "polygcd"
14337 | "polynomial_quotient" | "polyquot"
14338 | "polynomial_remainder" | "polyrem"
14339 | "polynomial_resultant" | "resultant"
14340 | "polynomial_discriminant" | "discriminant"
14341 | "polynomial_roots" | "polyroots"
14342 | "gumbel_pdf" | "gumbel_cdf" | "gumbel_quantile"
14344 | "frechet_pdf" | "frechet_cdf" | "frechet_quantile"
14345 | "logistic_pdf" | "logistic_cdf" | "logistic_quantile"
14346 | "rayleigh_pdf" | "rayleigh_cdf" | "rayleigh_quantile"
14347 | "inverse_gamma_pdf" | "inverse_gamma_cdf" | "inverse_gamma_quantile"
14348 | "kumaraswamy_pdf" | "kumaraswamy_cdf" | "kumaraswamy_quantile"
14349 | "mathieu_a" | "mathieu_characteristic_a"
14351 | "mathieu_ce" | "mathieu_se"
14352 | "heun_g"
14354 | "haar_transform" | "haar" | "haar_inverse" | "ihaar"
14356 | "daubechies_db4" | "db4" | "daubechies_db4_inverse" | "idb4"
14357 | "topo_sort_adj"
14359 | "scc_tarjan" | "tarjan_scc" | "strongly_connected"
14360 | "bipartite_q" | "is_bipartite"
14361 | "max_flow_edmonds_karp" | "max_flow" | "edmonds_karp"
14362 | "min_cut" | "eccentricity"
14363 | "graph_diameter" | "graph_radius"
14364 | "stieltjes_constant" | "stieltjes"
14366 | "gauss_sum" | "kloosterman_sum"
14367 | "eta_quotient" | "root_approximant"
14368 | "numerical_gradient" | "ngrad"
14370 | "numerical_jacobian" | "njac"
14371 | "numerical_hessian" | "nhess"
14372 | "numerical_divergence" | "ndiv"
14373 | "numerical_curl" | "ncurl"
14374 | "numerical_laplacian" | "nlap"
14375 | "nelder_mead" | "simplex_min"
14377 | "gradient_descent" | "gd_min"
14378 | "bfgs_minimize" | "bfgs"
14379 | "levenberg_marquardt" | "lev_marq" | "lm_min"
14380 | "conjugate_gradient" | "cg_solve"
14381 | "least_squares" | "lstsq"
14382 | "romberg" | "romberg_int"
14384 | "gauss_legendre_quad" | "glquad" | "gl_quad"
14385 | "monte_carlo_integrate" | "mc_int"
14386 | "adaptive_simpson" | "asimp"
14387 | "lu_decompose" | "ludec"
14389 | "qr_decompose" | "qrdec"
14390 | "householder_reflector" | "householder"
14391 | "givens_rotation" | "givens"
14392 | "forward_substitute" | "fwdsub"
14393 | "back_substitute" | "backsub"
14394 | "hessenberg_reduce" | "hessen"
14395 | "poly_derivative" | "polyder"
14397 | "poly_integrate" | "polyint"
14398 | "poly_compose" | "poly_eval_horner" | "horner"
14399 | "pade_approximant" | "pade"
14400 | "quat_mul" | "quat_conj" | "quat_norm" | "quat_inv"
14402 | "quat_from_axis_angle" | "axis_angle_to_quat"
14403 | "quat_to_axis_angle"
14404 | "quat_to_matrix" | "quat_from_matrix" | "matrix_to_quat"
14405 | "quat_slerp" | "slerp"
14406 | "euler_zyx_to_matrix" | "matrix_to_euler_zyx"
14407 | "rotate_3d_vec"
14408 | "kl_divergence" | "kl_div"
14410 | "js_divergence" | "js_div"
14411 | "mutual_information" | "mi"
14412 | "cross_entropy_arr" | "cross_entropy_dist"
14413 | "renyi_entropy" | "tsallis_entropy"
14414 | "pauli_x" | "pauli_y" | "pauli_z"
14416 | "pauli_id" | "pauli_i" | "pauli_identity"
14417 | "ket_bra" | "density_matrix" | "expectation_value" | "expval"
14418 | "commutator" | "anticommutator"
14419 | "partial_trace" | "ptrace"
14420 | "von_neumann_entropy" | "vn_entropy"
14421 | "bose_einstein" | "fermi_dirac"
14423 | "maxwell_boltzmann_speed" | "mb_speed"
14424 | "partition_function" | "z_partition"
14425 | "helmholtz_free_energy" | "free_energy_f"
14426 | "boltzmann_factor"
14427 | "einstein_specific_heat" | "einstein_cv"
14428 | "fresnel_reflection_te" | "fresnel_reflection_tm"
14430 | "fresnel_transmission_te" | "fresnel_transmission_tm"
14431 | "abcd_thin_lens" | "abcd_free_space"
14432 | "gaussian_beam_q"
14433 | "kepler_solve"
14435 | "true_to_eccentric" | "eccentric_to_mean"
14436 | "julian_date" | "j_date"
14437 | "jd_to_gregorian" | "jd_to_date"
14438 | "sidereal_time_gmst" | "gmst"
14439 | "vis_viva" | "orbital_period_kepler"
14440 | "orbital_elements_to_state" | "elem_to_state"
14441 | "kalman_step" | "kalman_filter"
14443 | "exponential_smoothing" | "exp_smooth"
14444 | "holt_winters" | "arma_yw_fit" | "ar_yw"
14445 | "pagerank" | "betweenness_centrality" | "closeness_centrality"
14447 | "eigenvector_centrality" | "degree_centrality" | "triangle_count"
14448 | "rgumbel" | "rfrechet" | "rrayleigh"
14450 | "rlogistic" | "rkumaraswamy" | "rinverse_gamma" | "rinvgamma"
14451 | "graham_scan" | "convex_hull_2d"
14453 | "line_line_intersect_2d" | "ll_intersect_2d"
14454 | "point_segment_distance" | "p_seg_dist"
14455 | "forward_diff" | "fdiff"
14457 | "forward_diff_grad" | "fdiff_grad"
14458 | "bartlett_test" | "levene_test"
14460 | "fishers_exact_test_2x2" | "fishers_exact"
14461 | "mcnemar_test"
14462 | "runs_test" | "wald_wolfowitz"
14463 | "friedman_test" | "kruskal_wallis_test" | "kruskal"
14464 | "sign_test"
14465 | "anderson_darling_normality" | "ad_normality"
14466 | "jarque_bera_test" | "jb_test"
14467 | "ljung_box_test" | "ljung_box"
14468 | "durbin_watson_stat" | "durbin_watson"
14469 | "mahalanobis_distance" | "mahalanobis_dist"
14471 | "cosine_distance" | "canberra_distance"
14472 | "bray_curtis_distance" | "bray_curtis"
14473 | "l1_distance"
14474 | "chi_squared_distance"
14475 | "multivariate_normal_pdf" | "mvn_pdf"
14477 | "multivariate_normal_sample" | "rmvn"
14478 | "dirichlet_pdf" | "dirichlet_sample" | "rdirichlet"
14479 | "skellam_pmf"
14480 | "inverse_gaussian_pdf" | "wald_pdf"
14481 | "inverse_gaussian_cdf" | "wald_cdf"
14482 | "inverse_gaussian_sample" | "rwald"
14483 | "non_central_chi2_pdf" | "ncchi2_pdf"
14484 | "matrix_exp" | "expm" | "matrix_log" | "logm"
14486 | "matrix_sqrt" | "sqrtm" | "matrix_sin" | "sinm"
14487 | "matrix_cos" | "cosm"
14488 | "rk45_dormand_prince" | "rk45" | "dopri5"
14490 | "midpoint_step" | "ode_midpoint"
14491 | "heun_step" | "ode_heun"
14492 | "verlet_step" | "ode_verlet"
14493 | "logistic_regression" | "logit_fit"
14495 | "poisson_regression"
14496 | "ridge_regression" | "ridge"
14497 | "lasso_coord" | "lasso"
14498 | "bootstrap_mean_ci" | "boot_mean_ci"
14500 | "jackknife_estimate" | "jackknife"
14501 | "permutation_test_diff" | "perm_test_diff"
14502 | "acf_at_lag" | "diff_op" | "lag_op"
14504 | "decompose_classical" | "decompose_ts"
14505 | "combinations_list" | "permutations_list"
14507 | "cyclic_permutations" | "subsets_of_size"
14508 | "longest_increasing_subseq" | "lis"
14510 | "knapsack_01" | "knapsack"
14511 | "subset_sum_target" | "subset_sum"
14512 | "coin_change_min" | "coin_change_minimum"
14513 | "edit_distance_levenshtein" | "edit_distance"
14514 | "one_hot_encode" | "onehot" | "label_encode"
14516 | "categorical_cross_entropy" | "cce"
14517 | "classification_metrics" | "binary_metrics"
14518 | "roc_auc" | "auroc"
14519 | "gaussian_blur_kernel" | "sobel_x" | "sobel_y"
14521 | "prewitt_x" | "prewitt_y"
14522 | "laplacian_of_gaussian" | "log_kernel"
14523 | "brownian_path" | "wiener_path"
14525 | "geometric_brownian_path" | "gbm_path"
14526 | "poisson_process" | "random_walk_1d"
14527 | "lempel_ziv_complexity" | "lz_complexity"
14529 | "huffman_code_lengths" | "huffman"
14530 | "shannon_entropy_rate" | "block_entropy_rate"
14531 | "planck_blackbody" | "blackbody"
14533 | "rayleigh_jeans" | "compton_shift"
14534 | "rydberg_energy"
14535 | "hydrogen_radial_wavefunction" | "h_rad_psi"
14536 | "integer_log" | "ilog"
14538 | "aks_primality" | "aks"
14539 | "elliptic_curve_add" | "ec_add"
14540 | "berlekamp_massey" | "bm_lfsr"
14541 | "bezout_coefficients" | "bezout" | "extended_euclid"
14542 | "factor_quadratic" | "complete_square"
14544 | "partial_fraction_simple" | "partial_fraction"
14545 | "gauss_chebyshev_quad" | "gc_quad"
14547 | "gauss_hermite_quad" | "gh_quad"
14548 | "gauss_laguerre_quad" | "glag_quad"
14549 | "clenshaw_curtis_quad" | "cc_quad"
14550 | "tanh_sinh_quad" | "ts_quad"
14551 | "gauss_legendre_2d" | "gl_2d"
14552 | "monte_carlo_2d" | "mc_2d"
14553 | "simulated_annealing" | "sa_min"
14555 | "simplex_lp" | "lp_simplex"
14556 | "particle_swarm" | "pso_min"
14557 | "gev_pdf" | "gev_cdf" | "gev_sample" | "rgev"
14559 | "gen_pareto_pdf" | "gen_pareto_cdf"
14560 | "gen_pareto_sample" | "rgenpareto"
14561 | "skew_normal_pdf" | "skew_normal_cdf"
14562 | "mixture_normal_pdf"
14563 | "categorical_sample" | "rcat"
14564 | "multinomial_pmf" | "multinomial_sample" | "rmultinom"
14565 | "truncated_normal_pdf"
14566 | "truncated_normal_sample" | "rtnorm"
14567 | "dbscan" | "gmm_em_1d" | "gmm_1d"
14569 | "silhouette_score"
14570 | "davies_bouldin_index" | "db_index"
14571 | "calinski_harabasz_index" | "ch_index"
14572 | "mds_2d" | "pcoa_2d" | "mean_shift"
14573 | "batch_norm" | "layer_norm"
14575 | "dropout_mask"
14576 | "max_pool_1d" | "avg_pool_1d"
14577 | "attention_softmax" | "positional_encoding"
14578 | "glorot_init" | "xavier_init"
14579 | "he_init" | "kaiming_init"
14580 | "adam_step" | "rmsprop_step"
14581 | "ewma" | "ccf" | "periodogram"
14583 | "welch_psd" | "welch"
14584 | "lag_features"
14585 | "median_filter_2d"
14587 | "threshold_otsu" | "otsu"
14588 | "histogram_equalize" | "hist_eq"
14589 | "erode_2d" | "dilate_2d"
14590 | "mse_loss" | "mae_loss" | "huber_loss"
14592 | "vincenty_distance" | "vincenty"
14594 | "mercator_project"
14595 | "destination_from_bearing" | "dest_bearing"
14596 | "recaman" | "recaman_seq"
14598 | "sylvester" | "sylvester_seq"
14599 | "happy_q" | "is_happy"
14600 | "amicable_pair_q"
14601 | "aliquot_sequence"
14602 | "magic_constant"
14603 | "clustering_coefficient_local" | "cc_local"
14605 | "clustering_coefficient_global" | "cc_global"
14606 | "assortativity" | "common_neighbors" | "jaccard_neighbors"
14607 | "adamic_adar"
14608 | "preferential_attachment_score" | "pa_score"
14609 | "triangle_3d_normal" | "triangle_3d_area"
14611 | "tetrahedron_volume"
14612 | "plane_from_3_points" | "plane_from_pts"
14613 | "point_to_plane_distance" | "pt_plane_dist"
14614 | "ray_triangle_intersect" | "moller_trumbore"
14615 | "ray_sphere_intersect" | "aabb_overlap"
14616 | "gauss_seidel"
14618 | "jacobi_iteration" | "jacobi_solve"
14619 | "sor_solve" | "sor"
14620 | "thomas_tridiag_solve" | "thomas"
14621 | "richardson_extrapolation" | "richardson"
14622 | "finite_difference_5pt" | "fd5pt"
14623 | "tonelli_shanks_sqrt" | "tonelli_shanks"
14625 | "baby_step_giant_step" | "bsgs"
14626 | "pollard_rho_factor" | "pollard_rho"
14627 | "modular_lcm" | "mlcm"
14628 | "crt_general" | "crt_arbitrary"
14629 | "van_der_waals_p" | "vdw_pressure"
14631 | "nernst_equation" | "nernst"
14632 | "arrhenius_rate" | "arrhenius"
14633 | "reduced_mass"
14634 | "ph_to_concentration" | "ph_to_h"
14635 | "metropolis_hastings" | "mh_sampler"
14637 | "gibbs_sampler_step" | "gibbs_step"
14638 | "euler_maruyama" | "em_sde"
14639 | "milstein" | "milstein_sde"
14640 | "ornstein_uhlenbeck_path" | "ou_path"
14641 | "hmm_forward" | "hmm_viterbi" | "hmm_backward"
14642 | "kaplan_meier" | "km_estimator" | "log_rank_test"
14644 | "needleman_wunsch" | "nw_align"
14645 | "smith_waterman" | "sw_align"
14646 | "gibbs_free_energy" | "delta_g"
14648 | "henderson_hasselbalch" | "hh_eq"
14649 | "radioactive_decay"
14650 | "half_life_to_constant" | "hl_to_lambda"
14651 | "pid_step"
14653 | "transfer_function_eval" | "tf_eval"
14654 | "bode_magnitude_db" | "bode_mag_db"
14655 | "bode_phase_deg"
14656 | "lqr_2x2"
14657 | "nash_eq_2x2" | "nash_2x2"
14659 | "shapley_value" | "expected_utility"
14660 | "hungarian_assignment" | "hungarian"
14662 | "tsp_nearest_neighbor" | "tsp_nn"
14663 | "vertex_cover_2approx" | "vc_2approx"
14664 | "heat_eq_1d" | "wave_eq_1d"
14666 | "laplace_2d_jacobi" | "laplace_jacobi"
14667 | "beta_binomial_update"
14669 | "normal_normal_update"
14670 | "gamma_poisson_update"
14671 | "dirichlet_multinomial_update"
14672 | "hadamard_gate" | "h_gate"
14674 | "cnot_gate" | "cx_gate"
14675 | "swap_gate" | "cz_gate"
14676 | "qft_matrix" | "phase_gate"
14677 | "s_gate" | "t_gate"
14678 | "bezier_eval"
14680 | "catmull_rom_eval" | "cmr_eval"
14681 | "cubic_hermite_eval" | "ch_eval"
14682 | "bspline_basis" | "nik_basis"
14683 | "freq_to_midi" | "midi_to_freq"
14685 | "equal_temperament_freq"
14686 | "cents_difference" | "cents_diff"
14687 | "redshift_z" | "hubble_distance" | "luminosity_distance"
14689 | "reynolds_number" | "mach_number"
14691 | "prandtl_number" | "bernoulli_velocity"
14692 | "negative_binomial_pmf" | "nb_pmf"
14694 | "hypergeometric_pmf"
14695 | "beta_binomial_pmf" | "bb_pmf"
14696 | "von_mises_pdf" | "vmf_pdf"
14697 | "erdos_renyi_random" | "erdos_renyi"
14699 | "barabasi_albert_random" | "barabasi_albert"
14700 | "watts_strogatz_random" | "watts_strogatz"
14701 | "rgb_to_lab" | "lab_to_rgb"
14703 | "kelvin_to_rgb" | "color_temp_rgb"
14704 | "bell_triangle" | "surjection_count"
14706 | "distinct_partition_count" | "q_partition"
14707 | "fibonacci_q" | "is_fib_number"
14708 | "bonferroni_correction" | "bonferroni"
14710 | "benjamini_hochberg" | "bh_fdr"
14711 | "tukey_hsd"
14712 | "hellinger_distance"
14713 | "wasserstein_1d" | "earth_movers_1d"
14714 | "chi_squared_divergence"
14715 | "beta_geometric_pmf"
14716 | "generalized_gamma_pdf" | "gengamma_pdf"
14717 | "zip_pmf" | "zero_inflated_poisson_pmf"
14718 | "stefan_boltzmann_luminosity" | "stellar_luminosity"
14719 | "photon_momentum" | "photon_energy_ev"
14720 | "dipole_radiation_power" | "larmor_power"
14721 | "parallax_to_distance" | "hawking_temperature"
14722 | "roche_limit" | "apparent_magnitude" | "distance_modulus"
14723 | "beer_lambert" | "absorbance"
14724 | "rate_law_n"
14725 | "freezing_point_depression" | "fpd"
14726 | "mixed_nash_2x2" | "minimax_2x2"
14727 | "barycentric_coords_2d" | "barycentric_2d"
14729 | "bresenham_line" | "bilinear_interp_2d"
14730 | "point_in_polygon_2d"
14731 | "hilbert_transform" | "cepstrum"
14732 | "butterworth_lowpass_coeffs" | "butter_lp"
14733 | "savitzky_golay_coeffs" | "sg_coeffs"
14734 | "savitzky_golay_filter" | "sg_filter"
14735 | "canny_edge_intensity" | "canny_intensity"
14736 | "bilateral_filter_basic" | "bilateral_filter"
14737 | "kmeans_pp_init" | "kpp_init"
14738 | "elbow_score" | "wcss"
14739 | "young_tableaux_count" | "syt_count"
14740 | "euler_alt_permutation" | "euler_zigzag"
14741 | "genocchi_number" | "lattice_paths_count"
14742 | "tetration"
14743 | "ackermann_limited" | "ackermann"
14744 | "perfect_power_q" | "b_smooth_q"
14745 | "k_core"
14747 | "rich_club_coefficient" | "rich_club"
14748 | "rsa_basic_encrypt" | "rsa_enc_int"
14749 | "rsa_basic_decrypt" | "rsa_dec_int"
14750 | "dh_shared_secret"
14751 | "bell_state_phi_plus" | "bell_phi_plus"
14752 | "bell_state_psi_minus" | "bell_psi_minus"
14753 | "density_matrix_purity" | "rho_purity"
14754 | "concurrence_2qubit"
14755 | "point_in_circle"
14756 | "circle_circle_intersect_2d"
14757 | "polygon_centroid"
14758 | "sutherland_hodgman_clip" | "sh_clip"
14759 | "kalman_rts_smoother" | "rts_smoother"
14760 | "gc_content" | "codon_to_aa"
14762 | "reverse_complement_dna" | "rev_comp_dna"
14763 | "hamming_dna"
14764 | "blosum62_pair_score" | "blosum62"
14765 | "kmer_count"
14766 | "great_circle_bearing" | "gc_bearing"
14768 | "midpoint_lat_lon" | "mid_geo"
14769 | "utm_zone_for"
14770 | "area_polygon_lat_lon" | "geo_polygon_area"
14771 | "crr_binomial_option" | "crr_option"
14773 | "bond_price_clean"
14774 | "bond_yield_to_maturity" | "bond_ytm"
14775 | "modified_duration_bond"
14776 | "convexity_bond" | "bond_convexity"
14777 | "ssim" | "psnr" | "mssim"
14779 | "db_spl_from_pa" | "db_spl"
14781 | "a_weighting_factor" | "a_weight"
14782 | "octave_band_center" | "octave_center"
14783 | "semitone_ratio"
14784 | "hardy_weinberg"
14786 | "expected_heterozygosity" | "het_e"
14787 | "fst_simple"
14788 | "allele_frequencies"
14789 | "sir_step" | "sir_r0" | "doubling_time"
14791 | "theil_index"
14793 | "herfindahl_hirschman" | "hhi"
14794 | "atkinson_index"
14795 | "lorenz_curve_points"
14796 | "iota_range" | "iota"
14798 | "reshape_array" | "reshape"
14799 | "grade_up" | "grade_asc"
14800 | "grade_down" | "grade_desc"
14801 | "plasma_frequency" | "omega_p"
14803 | "debye_length" | "lambda_d"
14804 | "cyclotron_frequency" | "omega_c"
14805 | "larmor_radius" | "gyroradius"
14806 | "jaro_winkler_similarity" | "jaro_winkler"
14808 | "metaphone_simple"
14809 | "elo_rating_update" | "elo"
14811 | "glicko_rating_update" | "glicko"
14812 | "dice_sum_pmf"
14813 | "cohens_d" | "effect_size_d"
14815 | "cliff_delta"
14816 | "vargha_delaney_a12" | "a12"
14817 | "step_response_2nd_order" | "step_2nd"
14819 | "overshoot_2nd_order" | "overshoot_pct"
14820 | "frobenius_norm"
14822 | "spectral_norm" | "operator_norm_2"
14823 | "trace_matrix" | "tr_mat"
14824 | "homophily_index" | "homophily"
14826 | "dyad_census" | "triad_census"
14827 | "sigmoid_inverse" | "logit"
14829 | "partition_at" | "drop_at" | "insert_at_idx"
14831 | "replace_at_index" | "set_at"
14832 | "swap_indices" | "nth_largest" | "nth_smallest"
14833 | "position_of_all_matching" | "positions_of_all"
14834 | "string_take_first" | "string_take_last"
14835 | "string_drop_first" | "string_drop_last"
14836 | "pluralize_simple"
14837 | "singularize_simple" | "singularize"
14838 | "capitalize_words" | "title_words"
14839 | "format_table_simple" | "ascii_table"
14840 | "days_between" | "weeks_between"
14841 | "months_between" | "years_between"
14842 | "first_of_month" | "last_of_month"
14843 | "day_of_week_iso" | "iso_dow"
14844 | "easter_sunday" | "chinese_zodiac"
14845 | "iso_week_number" | "iso_week"
14846 | "relative_luminance" | "wcag_luminance"
14847 | "contrast_ratio_wcag" | "wcag_contrast"
14848 | "delta_e_76" | "delta_e"
14849 | "color_blend_t" | "lerp_color"
14850 | "chord_to_freqs" | "scale_to_intervals"
14851 | "interval_semitones"
14852 | "transpose_freq_semitones" | "transpose_semi"
14853 | "bpm_to_period" | "midi_to_pitch_class"
14854 | "key_signature_for" | "circle_of_fifths_step"
14855 | "moon_phase" | "equation_of_time"
14856 | "solar_declination" | "sidereal_day_period" | "ecliptic_obliquity"
14857 | "permutation_order"
14858 | "permutation_parity" | "perm_sign"
14859 | "identity_permutation"
14860 | "permutation_compose" | "perm_mul"
14861 | "flesch_reading_ease" | "flesch_kincaid_grade"
14862 | "gunning_fog"
14863 | "automated_readability_index" | "ari"
14864 | "lix"
14865 | "adjusted_r_squared" | "adj_r2"
14866 | "aic" | "bic"
14867 | "residuals_compute" | "compute_residuals"
14868 | "composition_count" | "weak_composition_count"
14869 | "necklace_count" | "bracelet_count"
14870 | "multiset_permutations_count" | "multinomial_count"
14871 | "pearson_hash_byte" | "pearson_hash"
14872 | "xorshift32_step" | "lcg_next_u32"
14873 | "fisher_yates_shuffle"
14874 | "tetrahedral_number" | "square_pyramidal_number"
14876 | "octahedral_number" | "pentagonal_pyramidal_number"
14877 | "cake_number" | "cuban_number" | "centered_hexagonal_number"
14878 | "carmichael_q" | "is_carmichael"
14879 | "sphenic_q" | "is_sphenic"
14880 | "seven_smooth_q" | "is_7_smooth"
14881 | "cartesian_product_n" | "cart_n"
14882 | "multiset_union" | "multiset_intersection" | "multiset_difference"
14883 | "polynomial_roots_dk" | "durand_kerner"
14884 | "lin_bairstow_step" | "bairstow"
14885 | "heap_sift_down"
14886 | "fenwick_build" | "bit_build"
14887 | "fenwick_query" | "bit_query"
14888 | "segment_tree_sum" | "seg_sum"
14889 | "kmp_failure" | "kmp"
14890 | "z_array" | "z_func"
14891 | "suffix_array_naive"
14892 | "manacher_radii" | "manacher"
14893 | "rabin_karp_hash" | "lcp_array"
14894 | "regex_escape_simple"
14895 | "horspool_search" | "bm_horspool"
14896 | "lpt_schedule" | "lpt"
14897 | "johnsons_rule" | "johnson_2m"
14898 | "bit_reverse_32" | "bit_reverse"
14899 | "bin_to_gray" | "gray_to_bin"
14900 | "swap_bits_pos" | "swap_bits"
14901 | "hamming_weight" | "popcnt"
14902 | "hamming_distance_int" | "hamdist_int"
14903 | "internal_rate_of_return"
14904 | "modified_irr" | "mirr"
14905 | "payback_period_simple" | "payback_simple"
14906 | "rfc3339_format" | "rfc3339"
14907 | "rfc3339_parse"
14908 | "iso_ordinal_date" | "ordinal_date"
14909 | "lazy_caterer" | "central_polygonal"
14911 | "centered_square" | "centered_triangular" | "centered_pentagonal"
14912 | "star_number" | "dodecahedral_number" | "icosahedral_number"
14913 | "pronic_number" | "squared_triangular"
14914 | "woodall_number" | "cullen_number"
14915 | "repunit" | "repdigit" | "kaprekar_routine_step"
14916 | "smith_q"
14917 | "keith_q" | "is_keith"
14918 | "armstrong_q" | "is_armstrong"
14919 | "fnv1a_hash" | "djb2_hash"
14920 | "jenkins_one_at_a_time" | "jenkins_oat"
14921 | "murmurhash3_x32"
14922 | "adler32_hash" | "crc16_ccitt"
14923 | "vec_dot"
14924 | "l1_norm" | "l2_norm" | "vec_l2"
14925 | "linf_norm" | "max_norm" | "lp_norm"
14926 | "unit_vector"
14927 | "vector_project" | "proj" | "vector_reject"
14928 | "orthogonalize_vectors" | "gram_schmidt"
14929 | "outer_product" | "vec_outer"
14930 | "matrix_diagonal" | "mdiagvec"
14931 | "matrix_anti_diagonal"
14932 | "matrix_symmetric_q" | "matrix_orthogonal_q"
14933 | "geometric_mean_arr" | "harmonic_mean_arr"
14934 | "quadratic_mean_arr" | "lehmer_mean"
14935 | "running_mean" | "running_variance"
14936 | "outlier_iqr_q" | "z_score_robust"
14937 | "geometric_sequence" | "arithmetic_sequence"
14938 | "log_sum_exp" | "lse"
14939 | "log_sigmoid" | "log1p_exp"
14940 | "string_chars"
14941 | "string_words_count" | "word_count_simple"
14942 | "string_lines_count" | "line_count_simple"
14943 | "string_intersperse" | "string_replicate"
14944 | "string_uniq_chars" | "string_letter_frequency"
14945 | "anagram_q" | "is_anagram_q"
14946 | "string_take_while" | "string_drop_while"
14947 | "string_split_at_first" | "string_partition_at_word"
14948 | "relativistic_kinetic"
14950 | "lorentz_factor_v" | "doppler_relativistic"
14951 | "drag_force_quadratic" | "terminal_velocity"
14952 | "carnot_efficiency" | "otto_efficiency"
14953 | "brayton_efficiency" | "diesel_efficiency"
14954 | "specific_heat_const_v" | "speed_of_sound_ideal"
14955 | "kepler_period_au" | "synodic_period"
14956 | "hill_radius" | "jeans_length"
14957 | "chandrasekhar_mass" | "eddington_luminosity"
14958 | "schwarzschild_radius_m" | "gravity_at_radius"
14959 | "gravitational_pe"
14960 | "freefall_time" | "pendulum_freq" | "spring_period"
14961 | "centripetal_accel" | "lens_focal_length"
14962 | "avogadros_number" | "boltzmann_const"
14963 | "planck_const_h" | "gas_constant_r"
14964 | "concentration_dilute" | "partial_pressure"
14965 | "mole_fraction" | "molarity" | "molality"
14966 | "normality_chem" | "ionic_strength"
14967 | "titration_volume"
14968 | "atomic_radius_pm" | "de_broglie_wavelength_kg"
14969 | "lotka_volterra_step"
14970 | "michaelis_menten" | "hill_equation"
14971 | "lineweaver_burk" | "eadie_hofstee_y"
14972 | "arrhenius_temp_q10"
14973 | "body_surface_area_dubois" | "bsa_dubois"
14974 | "bmr_harris_benedict_male" | "bmr_harris_benedict_female"
14975 | "max_heart_rate" | "target_heart_rate"
14976 | "vo2_max_estimate" | "pulse_pressure"
14977 | "mean_arterial_pressure" | "map_bp"
14978 | "dew_point_magnus" | "heat_index_celsius"
14979 | "wind_chill_celsius" | "pressure_altitude_m"
14980 | "density_altitude_m" | "saturation_vapor_pressure"
14981 | "humidex" | "utci_simple"
14982 | "resistance_parallel" | "r_parallel"
14983 | "resistance_series" | "r_series"
14984 | "capacitance_parallel" | "c_parallel"
14985 | "capacitance_series" | "c_series"
14986 | "inductance_parallel" | "l_parallel"
14987 | "inductance_series" | "l_series"
14988 | "voltage_divider" | "current_divider"
14989 | "lc_resonant" | "q_factor_rlc"
14990 | "skin_depth" | "wire_resistance"
14991 | "motor_torque" | "efficiency_ratio"
14992 | "dB_voltage" | "db_voltage"
14993 | "dB_power" | "db_power"
14994 | "bfs_distances" | "dfs_preorder" | "connected_components"
14996 | "graph_is_tree" | "graph_density"
14997 | "graph_average_degree" | "graph_max_degree" | "graph_min_degree"
14998 | "graph_complement"
14999 | "in_degree_directed" | "out_degree_directed"
15000 | "graph_eccentricity_all" | "is_connected"
15001 | "articulation_points" | "bridges_edges"
15002 | "eulerian_path_q" | "hamiltonian_brute"
15003 | "string_to_charcodes" | "charcodes_to_string"
15004 | "string_xor"
15005 | "string_camel_to_snake" | "string_snake_to_camel"
15006 | "string_kebab_to_snake" | "string_snake_to_kebab"
15007 | "palindromic_q" | "substring_count"
15008 | "string_truncate_ellipsis" | "string_expand_tabs"
15009 | "string_normalize_spaces"
15010 | "days_in_year" | "quarter_of_year"
15011 | "zeller_day_of_week" | "age_from_birthdate"
15012 | "business_days_between" | "unix_epoch_to_iso"
15013 | "loan_payment_pmt" | "loan_balance"
15014 | "amortization_total_interest"
15015 | "apr_to_apy" | "apy_to_apr"
15016 | "compound_interest_periods" | "simple_interest_compute"
15017
15018 | "perpetuity_value" | "growing_perpetuity"
15019 | "annuity_present_value" | "annuity_future_value"
15020 | "capm_expected_return"
15021 | "treynor_ratio"
15022 | "jensens_alpha" | "information_ratio"
15023 | "friction_factor_laminar" | "swamee_jain_factor"
15024 | "pipe_pressure_drop" | "orifice_velocity"
15025 | "chezy_velocity" | "manning_velocity"
15026 | "froude_number" | "weber_number" | "grashof_number"
15027 | "nusselt_dittus_boelter"
15028 | "mollweide_project" | "robinson_project" | "sinusoidal_project"
15030 | "equirectangular_project" | "lambert_azimuthal_project" | "albers_conic_project"
15031 | "geohash_encode" | "geohash_decode" | "geohash_neighbor" | "geohash_bbox"
15032 | "gabor_kernel" | "unsharp_mask_kernel" | "emboss_kernel"
15033 | "box_blur_kernel" | "motion_blur_kernel" | "sharpen_kernel"
15034 | "edge_detect_kernel" | "sobel_diagonal_kernel" | "haar_2d_step"
15035 | "db4_coeffs" | "db6_coeffs" | "sym4_coeffs" | "coif1_coeffs"
15036 | "aes_sbox_byte" | "aes_inv_sbox_byte"
15037 | "chacha20_qround" | "xtea_round" | "speck_round" | "simon_round"
15038 | "kepler_hyperbolic" | "hohmann_dv1" | "hohmann_dv2" | "hohmann_total"
15039 | "bielliptic_total" | "lambert_simple"
15040 | "horizon_distance" | "solar_zenith_angle" | "air_mass_kasten"
15041 | "solar_constant" | "julian_centuries_j2000"
15042 | "mean_solar_longitude" | "mean_solar_anomaly" | "lst_to_solar"
15043 | "ra_dec_to_az_alt" | "ecliptic_to_equatorial" | "equatorial_to_galactic"
15044 | "orbital_eccentricity" | "semi_major_axis"
15045 | "specific_orbital_energy" | "specific_angular_momentum"
15046 | "toffoli_gate" | "ccx_gate" | "fredkin_gate" | "cswap_gate"
15047 | "iswap_gate" | "sqrt_swap_gate"
15048 | "rx_gate" | "ry_gate" | "rz_gate"
15049 | "ghz_state_n" | "w_state_n"
15050 | "depolarizing_channel" | "dephasing_channel" | "amplitude_damping_channel"
15051 | "quantum_fidelity_pure" | "trace_distance"
15052 | "bell_inequality_chsh" | "pauli_decomposition_2x2"
15053 | "quantum_relative_entropy" | "qft_4_real"
15054 | "bwt_encode" | "bwt_decode" | "mtf_encode" | "mtf_decode"
15055
15056 | "lyndon_factorize" | "christoffel_word" | "sturmian_word"
15057 | "z_function_alt" | "period_of_string" | "borders_of_string"
15058 | "thue_morse_string" | "fibonacci_word"
15059 | "mann_kendall_tau" | "theil_sen_slope" | "hodges_lehmann"
15060 | "huber_m_estimator" | "winsorized_variance_arr"
15061 | "bowley_skewness" | "pearson_skewness_2"
15062 | "concordance_correlation" | "quantile_p"
15063 | "label_propagation_step" | "modularity_q"
15064 | "clique_count_3" | "local_efficiency" | "global_efficiency"
15065 | "diameter_unweighted"
15066 | "aitken_delta_squared" | "wynn_epsilon"
15067 | "shanks_transform" | "levin_t_transform"
15068 | "harmonic_seq_sum" | "alternating_seq_sum"
15069 | "sparse_csr_build" | "sparse_csr_mul_vec" | "sparse_density"
15071 | "lower_triangular_q" | "upper_triangular_q"
15072 | "diagonal_dominance_q" | "matrix_zero_q" | "matrix_identity_q"
15073 | "matrix_random_uniform" | "matrix_random_normal"
15074 | "andrew_monotone_chain" | "polygon_area_signed"
15075 | "polygon_convex_q" | "iou_2d_axis_aligned" | "hausdorff_distance_2d"
15076 | "minkowski_sum_simple" | "circle_3_points"
15077 | "polygon_winding_number" | "segment_length"
15078 | "segments_parallel_q" | "segments_perpendicular_q"
15079 | "burr_xii_pdf" | "burr_xii_cdf" | "dagum_pdf" | "lomax_pdf"
15080 | "birnbaum_saunders_pdf" | "tukey_lambda_quantile"
15081 | "half_cauchy_pdf" | "half_logistic_pdf" | "reciprocal_pdf"
15082 | "levy_pdf" | "voigt_profile_simple"
15083 | "gompertz_pdf" | "inverse_weibull_pdf"
15084 | "log_gamma_simple" | "inverse_chi2_pdf"
15085 | "poly1305_block_step" | "x25519_field_mul" | "curve25519_mul_simple"
15086 | "secp256k1_y_recover" | "hmac_step_xor"
15087 | "pkcs7_pad" | "pkcs7_unpad" | "xor_byte_string"
15088 | "atbash_cipher"
15089 | "vigenere_encrypt" | "vigenere_decrypt" | "xor_brute_keylen"
15090 | "arima_diff" | "seasonal_diff"
15091 | "garch_step" | "egarch_step"
15092 | "realized_volatility" | "max_drawdown_arr"
15093 | "calmar_ratio" | "omega_ratio" | "kelly_criterion"
15094 | "var_historical" | "cvar_historical"
15095 | "graph_degree_distribution" | "graph_count_edges"
15096 | "graph_bipartite_match_simple" | "graph_count_triangles"
15097 | "graph_avg_clustering" | "graph_transitivity"
15098 | "graph_max_clique_brute" | "graph_independent_set_brute"
15099 | "graph_count_paths_length_k" | "graph_pagerank_simple"
15100 | "boole_rule" | "boole_int"
15102 | "gauss_legendre_5" | "gl5"
15103 | "gauss_kronrod_15" | "gk15"
15104
15105 | "midpoint_rule"
15106 | "adams_bashforth_4" | "ab4"
15107 | "heun_method" | "rk45_cash_karp" | "rkck"
15108 | "milne_pc" | "milne"
15109 | "modified_midpoint_ode" | "modmidpoint"
15110 | "backward_euler" | "implicit_euler"
15111 | "crank_nicolson_ode" | "cn_ode"
15112 | "brent_root" | "brent" | "ridders_root" | "ridders"
15113 | "steffensen_root" | "steffensen" | "halley_root" | "halley"
15114 | "householder_root" | "muller_root" | "muller"
15115 | "regula_falsi" | "false_position"
15116 | "secant_root" | "secant"
15117 | "anderson_step" | "aberth_step" | "inverse_quad_interp"
15118 | "lm_step" | "gradient_descent_step"
15119 | "nesterov_step" | "adagrad_step"
15120 | "cg_beta_pr" | "cg_beta_fr" | "bfgs_h_update_1d"
15121 | "wolfe_strong_q" | "dogleg_step"
15122 | "nelder_mead_reflect" | "nelder_mead_expand" | "nelder_mead_contract"
15123 | "sa_accept_prob" | "sa_boltzmann_temp" | "sa_cauchy_temp"
15124 | "sa_geometric_temp" | "acceptance_target"
15125 | "bs_call" | "blackscholes_call" | "bs_put" | "blackscholes_put"
15127 | "bs_theta_call" | "bs_rho_call"
15128 | "bachelier_call" | "black76_call"
15129 | "crr_american_call" | "crr_american_put" | "jr_european_call"
15130 | "trinomial_call" | "heston_price_simple" | "sabr_implied_vol"
15131 | "merton_jump_call" | "asian_call_mc" | "barrier_up_out_call"
15132 | "digital_call" | "lookback_call"
15133 | "macaulay_duration" | "forward_rate"
15134 | "discount_continuous" | "ytm_newton"
15135 | "vasicek_bond" | "cir_bond" | "hull_white_drift"
15136 | "cds_upfront" | "black_karasinski_drift" | "quanto_adjustment"
15137 | "fx_forward" | "garman_kohlhagen_call" | "margrabe" | "stulz_min_call"
15138 | "sharpe_annualized"
15139 | "jensen_alpha" | "modified_sharpe"
15140 | "ph_from_h" | "poh_from_oh" | "pka_from_ka"
15142 | "henderson_base"
15143 | "arrhenius_k" | "eyring_k"
15144 | "first_order_concentration" | "first_order_half_life"
15145 | "second_order_concentration" | "second_order_half_life"
15146 | "zero_order_concentration"
15147
15148 | "ideal_gas_n" | "redlich_kwong_p"
15149 | "compressibility_z"
15150 | "kc_from_rates" | "kp_from_kc" | "reaction_quotient" | "rxn_q"
15151 | "le_chatelier_dir"
15152 | "dg_from_k" | "k_from_dg" | "vant_hoff" | "clausius_clapeyron" | "antoine_p"
15153 | "emf_from_half_cells" | "faraday_mass_deposited"
15154 | "transmittance" | "ksp_from_concs"
15155 | "debye_huckel"
15156 | "cp_monatomic_ideal" | "cv_monatomic_ideal"
15157 | "heat_capacity_q" | "calorimeter_dt" | "enthalpy_reaction"
15158 | "avogadro_count" | "moles_from_mass"
15159 | "dilution_v2" | "raoult_law" | "bp_elevation" | "fp_depression"
15160 | "osmotic_pressure" | "rydberg_lambda" | "bohr_radius_n"
15161 | "bohr_energy_ev" | "photon_energy_freq" | "photon_energy_lambda"
15162 | "de_broglie"
15163 | "logistic_growth_step" | "logistic_growth_analytic"
15165 | "gompertz_growth_step" | "allee_growth_step"
15166 | "growth_rate_from_ratio"
15167 | "seir_step" | "seird_step" | "sis_step"
15168 | "r0_basic" | "rt_effective" | "herd_immunity_threshold" | "generation_time"
15169 | "inverse_simpson"
15170 | "pielou_evenness" | "margalef_richness" | "menhinick_richness"
15171 | "berger_parker" | "sorensen_dice"
15172 | "rao_quadratic_entropy"
15173 | "selection_step" | "nei_genetic_distance"
15174 | "effective_pop_size" | "carrying_capacity_from_data"
15175 | "petersen_estimator" | "chapman_estimator"
15176 | "lv_competition_step"
15177 | "holling_type1" | "holling_type2" | "holling_type3"
15178 | "leslie_step" | "net_reproductive_rate" | "generation_time_demo"
15179 | "finite_rate_lambda" | "kleibers_law" | "bergmann_adjust"
15180 | "q10" | "species_area" | "intrinsic_growth_rate"
15181 | "macarthur_wilson_immigration" | "macarthur_wilson_extinction"
15182 | "island_equilibrium"
15183 | "efield_point" | "epotential_point"
15185 | "capacitor_charge"
15186 | "ohm_voltage" | "power_vi" | "power_i2r"
15187
15188 | "capacitance_parallel_sum"
15189 | "bfield_wire" | "bfield_solenoid" | "lorentz_force_mag"
15190 | "faraday_emf"
15191 | "lc_frequency" | "lc_omega"
15192 | "rc_tau" | "rl_tau"
15193 | "poynting_magnitude" | "em_intensity" | "radiation_pressure"
15194 | "em_wavelength" | "em_frequency"
15195 | "snell_theta2"
15196 | "index_from_speed" | "fresnel_reflection_normal"
15197 | "fresnel_rs" | "fresnel_rp"
15198 | "lensmaker" | "thin_lens_v" | "mirror_equation_v"
15199 | "lens_magnification" | "diffraction_grating_angle"
15200 | "single_slit_min" | "rayleigh_resolution"
15201 | "lorentz_gamma"
15202 | "rel_momentum" | "rel_ke" | "rel_total_energy" | "rel_energy_pm"
15203 | "relativistic_doppler" | "rel_velocity_add"
15204
15205 | "wave_string_speed" | "sound_solid" | "sound_gas"
15206 | "doppler_classical" | "standing_wave_fundamental"
15207 | "open_pipe_harmonic" | "closed_pipe_harmonic"
15208 | "sound_db"
15209 | "alfven_speed"
15210 | "grav_time_dilation" | "grav_redshift"
15211 | "kosaraju_scc" | "bridges"
15213 | "max_flow_ek" | "min_cut_value" | "hopcroft_karp"
15214
15215 | "katz_centrality" | "hits_simple"
15216 | "pagerank_damped" | "cc_count" | "cc_labels"
15217 | "topological_sort_kahn" | "has_cycle_directed" | "has_cycle_undirected"
15218 | "diameter_bfs" | "radius_bfs"
15219 | "num_edges" | "k_coreness"
15220 | "greedy_coloring" | "chromatic_number_greedy"
15221 | "sum_degrees" | "avg_degree" | "max_degree"
15222 | "is_tree" | "girth"
15223 | "hamming_window" | "hann_window" | "blackman_window"
15225 | "blackman_harris_window" | "bartlett_window" | "welch_window"
15226 | "kaiser_window" | "tukey_window" | "gaussian_window"
15227 | "hilbert_envelope"
15228 | "biquad_step" | "biquad_lowpass_coeffs" | "biquad_highpass_coeffs"
15229 | "biquad_bandpass_coeffs" | "biquad_notch_coeffs" | "biquad_allpass_coeffs"
15230 | "biquad_peak_coeffs" | "biquad_lowshelf_coeffs" | "biquad_highshelf_coeffs"
15231 | "butterworth_prewarp" | "butterworth_order"
15232 | "fir_moving_average" | "fir_lowpass_design"
15233 | "spectrogram_simple"
15234 | "zero_pad" | "resample_nearest" | "resample_linear" | "quantize"
15235 | "mu_law_encode" | "mu_law_decode" | "a_law_encode" | "a_law_decode"
15236 | "chirp_linear"
15237 | "fnv1a_32" | "fnv1a_64" | "sdbm_hash"
15239 | "siphash24"
15240 | "pbkdf2_hmac_step" | "scrypt_round" | "bcrypt_cost_iters"
15241 | "argon2_block_mix" | "hkdf_expand_step"
15242 | "lfsr_galois_step" | "mt19937_temper" | "xorshift64" | "xorshift32"
15243 | "pcg32_step" | "lcg_numrec_step" | "splitmix64_step" | "wyhash_mix"
15244
15245 | "xor_cipher_byte"
15246 | "railfence_encrypt" | "beaufort" | "affine_encrypt" | "substitution_encrypt"
15247 | "letter_frequency" | "english_chi2" | "index_of_coincidence" | "kasiski_repeats"
15248 | "deterministic_prime" | "dh_shared" | "rsa_encrypt_simple"
15249 | "monobit_test" | "approximate_entropy"
15250 | "gini_impurity" | "entropy_bits" | "information_gain" | "gain_ratio"
15252 | "nb_gaussian_likelihood" | "nb_bernoulli_likelihood" | "nb_multinomial_log_likelihood"
15253 | "adaboost_alpha" | "hinge_loss" | "squared_hinge"
15254 | "logistic_loss"
15255 | "sigmoid_grad" | "tanh_grad"
15256 | "relu_grad"
15257 | "softsign" | "prelu" | "threshold_act"
15258 | "confusion_counts" | "mcc" | "f_beta" | "specificity"
15259 | "balanced_accuracy" | "cohen_kappa" | "brier_score" | "log_loss"
15260 | "tversky" | "mahalanobis_1d"
15261 | "log_softmax" | "one_hot" | "topk_indices"
15262 | "minmax_scale" | "zscore_norm" | "robust_scale"
15263 | "triangle_area_heron" | "triangle_area_pts"
15265 | "triangle_inradius" | "triangle_circumradius"
15266 | "regular_ngon_area" | "regular_ngon_inradius" | "regular_ngon_circumradius"
15267 | "n_ball_volume"
15268 | "cylinder_surface" | "cone_surface"
15269
15270 | "ellipsoid_volume" | "ellipsoid_surface_approx"
15271 | "dist_point_line_2d" | "dist_point_plane_3d" | "closest_pt_segment_2d"
15272 | "bbox_from_points"
15273 | "euclidean_distance_nd"
15274 | "hamming_distance_str"
15275 | "great_circle_law_of_cos"
15276 | "initial_bearing" | "midpoint_great_circle"
15277 | "shoelace_area" | "polygon_is_convex" | "convex_hull_jarvis"
15278 | "euler_characteristic" | "genus_from_euler"
15279 | "spherical_triangle_area" | "polygon_with_holes_area" | "picks_theorem"
15280 | "centroid_nd" | "covariance_matrix_pts" | "simplex_volume_3d"
15281 | "hyper2f1" | "hyper1f1" | "hyper0f1" | "pochhammer"
15283 | "mathieu_ce0" | "mathieu_se1" | "parabolic_d0" | "parabolic_d1"
15284 | "whittaker_m" | "struve_h0" | "struve_h1"
15285 | "lambert_w0" | "wright_omega"
15286 | "sinhc" | "cosh_minus1_over_x2"
15287 | "sine_integral_si" | "cosine_integral_ci" | "exp_integral_e1"
15288 | "dawson_function" | "owen_t"
15289 | "spherical_bessel_j0" | "spherical_bessel_j1"
15290 | "spherical_bessel_y0" | "spherical_bessel_y1"
15291 | "mod_sph_bessel_i0" | "mod_sph_bessel_i1" | "mod_sph_bessel_k0"
15292 | "coulomb_f0"
15293 | "polylog_li2" | "polylog_n"
15294
15295 | "ti2" | "clausen_cl2"
15296 | "bose_einstein_g" | "fermi_dirac_int"
15297 | "theta3" | "theta2"
15298 | "jacobi_sn_small_q" | "jacobi_cn_small_q" | "jacobi_dn_small_q"
15299 | "riemann_xi" | "bessel_jn_general" | "bessel_in_general"
15300 | "absolute_magnitude"
15302 | "pc_to_ly" | "ly_to_pc" | "pc_to_au" | "au_to_m"
15303 | "solar_mass_to_kg" | "solar_luminosity_to_w"
15304 | "hubble_distance_mpc" | "comoving_distance_approx" | "critical_density"
15305 | "et_freq_ratio" | "midi_to_hz" | "hz_to_midi" | "cents_between"
15306 | "just_intonation_ratio" | "pythagorean_ratio"
15307 | "beat_frequency" | "bpm_to_spb" | "note_name_to_midi"
15308 | "rgb_to_yiq" | "rgb_to_yuv601"
15309 | "srgb_to_xyz" | "xyz_to_lab" | "delta_e_94"
15310
15311
15312 | "feet_to_meters" | "meters_to_feet"
15313 | "lb_to_kg" | "kg_to_lb"
15314 | "mph_to_kmh" | "kmh_to_mph" | "mps_to_kmh" | "kmh_to_mps" | "knots_to_kmh"
15315 | "atm_to_pa" | "pa_to_atm" | "mmhg_to_pa"
15316 | "ev_to_joules" | "joules_to_ev" | "btu_to_joules" | "kwh_to_joules"
15317 | "bpm_to_midi_tick_us" | "iso226_phon_adjustment"
15318 | "db_to_amp" | "amp_to_db"
15319 | "roman_encode" | "roman_decode" | "number_to_english"
15320 | "hubble_lcdm" | "hubble_time" | "hubble_distance_si" | "critical_density_si"
15322 | "comoving_distance" | "angular_diameter_distance"
15323 | "lookback_time" | "age_at_z" | "scale_factor" | "redshift_from_a"
15324 | "omega_m_at_z" | "lcdm_eos" | "cpl_w" | "deceleration_q"
15325 | "schwarzschild_radius_kg" | "kerr_ergosphere_eq" | "kerr_horizon"
15326 | "bh_entropy" | "bh_evaporation_time"
15327 | "schwarzschild_isco" | "photon_sphere_radius"
15328 | "tidal_force" | "grav_dilation_factor" | "lense_thirring_omega"
15329 | "gw_strain_amplitude" | "chirp_mass" | "grav_binding_energy"
15330 | "roche_limit_rigid" | "roche_limit_fluid"
15331 | "lagrange_l1" | "sphere_of_influence"
15332 | "freefall_velocity_schwarzschild" | "einstein_ring_radius"
15333 | "microlensing_magnification" | "cosmic_distance_modulus_si"
15334 | "cmb_temperature" | "cmb_temperature_at_z"
15335 | "stefan_boltzmann_si" | "planck_spectral_radiance"
15336 | "schwarzschild_g_tt" | "schwarzschild_g_rr" | "kretschmann_schwarzschild"
15337 | "hill_velocity" | "vacuum_energy_density"
15338 | "sound_horizon_recomb" | "bao_scale_today" | "sigma8_default"
15339 | "lensing_convergence" | "sigma_crit"
15340 | "perihelion_precession" | "shapiro_delay" | "light_deflection_angle"
15341 | "tov_mass_limit"
15342 | "main_sequence_lifetime" | "schwarzschild_freefall_time"
15343 | "friedmann_density_total" | "cosmological_constant"
15344
15345 | "planck_energy"
15346 | "pure_state_density" | "purity"
15348 | "linear_entropy" | "quantum_mutual_info"
15349 | "eof_from_concurrence"
15350 | "bell_state_index" | "chsh_expectation" | "tsirelson_bound"
15351 | "pauli_real_part" | "pauli_y_imag"
15352 | "bloch_to_density_real" | "bloch_purity_check"
15353 | "fidelity_pure_real" | "l1_coherence" | "relative_entropy_coherence"
15354 | "kraus_apply" | "bit_flip_prob" | "phase_flip_prob"
15355 | "depolarizing_density_2x2" | "amplitude_damping_excited"
15356 | "quantum_fisher_info" | "cramer_rao_bound" | "squeezing_db" | "heisenberg_min"
15357 | "coherent_mean_photons" | "thermal_mean_photons" | "poisson_photon_pmf"
15358 | "bose_einstein_pmf" | "mandel_q" | "g2_zero"
15359 | "free_particle_energy" | "infinite_well_energy" | "harmonic_oscillator_energy"
15360 | "hydrogen_energy_n" | "stark_shift_linear"
15361 | "zeeman_energy" | "larmor_frequency" | "rabi_frequency"
15362 | "schrodinger_step_real" | "probability_density" | "state_norm" | "state_normalize"
15363 | "quantum_variance" | "spin_casimir"
15364 | "cg_simple" | "wigner_3j_bound" | "qho_ground_state"
15365 | "tunneling_prob" | "gamow_factor" | "compton_wavelength" | "uncertainty_position"
15366 | "berry_phase_spin_half" | "zeno_survival" | "decoherence_time"
15367 | "ramsey_visibility" | "fermi_golden_rule"
15368 | "needleman_wunsch_score" | "smith_waterman_score" | "pam250_score"
15370 | "tanimoto_bits" | "translate_dna" | "transcribe_dna_rna" | "reverse_transcribe"
15371 | "at_content" | "tm_wallace" | "tm_marmur" | "codon_adaptation_index"
15372 | "kmer_jaccard" | "sequence_shannon_info" | "pwm_score"
15373 | "msa_column_entropy" | "seq_logo_information"
15374 | "damerau_levenshtein" | "lcs_length"
15375 | "hirschberg_lcs_length" | "common_kmers"
15376 | "jukes_cantor_distance" | "kimura_2p_distance" | "felsenstein_step"
15377 | "branch_length_substitutions" | "num_unrooted_trees" | "bayes_posterior"
15378 | "hw_expected_counts" | "allele_frequency" | "ld_d" | "ld_r_squared"
15379 | "heterozygosity" | "ne_from_variance"
15380 | "expected_coverage" | "lander_waterman_gaps"
15381 | "bh_adjusted_p" | "zscore_count"
15382 | "go_enrichment_p" | "blosum45_score"
15383 | "henikoff_weight" | "hamming_protein" | "codon_usage_variance"
15384 | "dnds_ratio" | "mutation_rate" | "tajimas_d" | "wattersons_theta"
15385 | "coalescent_expected_time" | "coalescent_tree_length" | "nm_from_fst"
15386 | "bdf1_step" | "bdf2_step" | "bdf3_step" | "bdf4_step" | "bdf5_step" | "bdf6_step"
15388 | "ab1_step" | "ab2_step" | "ab3_step"
15389 | "am2_step" | "am3_step" | "am4_step"
15390 | "ros2_step" | "imex_euler_step" | "symplectic_euler_step"
15391 | "leapfrog_step" | "stormer_verlet_step"
15392 | "rk4_single" | "dopri5_combine" | "rkf45_error"
15393 | "lobatto_iiia_2" | "lobatto_iiic_3" | "gauss_irk_2_stage" | "magnus_1st"
15394 | "euler_lte" | "trapezoidal_lte" | "pi_step_size"
15395 | "stiffness_ratio" | "spectral_radius"
15396 | "heun_euler_step" | "bogacki_shampine_step" | "verner_8_combine"
15397 | "rk_combine" | "ab_coeff_sum"
15398 | "newmark_beta_step" | "wilson_theta_step"
15399 | "strang_split" | "lie_split"
15400 | "exp_euler_step" | "etd_rk2" | "dde_euler_step"
15401 | "em_step" | "milstein_step" | "heun_sde_step" | "stratonovich_correction"
15402 | "predictor_corrector" | "numerical_jacobian_col"
15403 | "cn_coefficient" | "imex_theta_split" | "bulirsch_stoer_step"
15404 | "cfl_number" | "diffusion_stability"
15405 | "lax_friedrichs_flux" | "lax_wendroff_flux"
15406 | "van_leer_limiter" | "minmod_limiter" | "superbee_limiter" | "mc_limiter"
15407 | "pollard_p_minus_1" | "fermat_factor"
15409 | "trial_smallest_factor" | "bsgs_discrete_log"
15410 | "mertens" | "liouville"
15411 | "is_b_smooth" | "primorial_n"
15412 | "pseudoprime_base2" | "strong_pseudoprime"
15413 | "aks_witness_count" | "qs_relation"
15414 | "index_calculus_naive" | "lll_2x2_step" | "coppersmith_bound"
15415 | "shor_period_prob" | "rsa_d_from_e" | "dh_secret"
15416 | "elgamal_encrypt" | "ecc_point_double" | "continued_fraction_sqrt"
15417 | "pell_fundamental" | "sum_two_squares" | "class_number_bound"
15418 | "smith_normal_2x2_step" | "regulator_naive"
15419 | "power_residue_check" | "wieferich_check" | "wilson_test"
15420 | "goldbach_pair" | "english_likeness" | "xor_break_singlebyte"
15421 | "bit_reverse_64"
15422 | "gf256_multiply" | "hash_combine"
15423 | "arch_lm_test" | "breusch_pagan_test" | "white_robust_se"
15425 | "newey_west_se" | "hansen_j_test" | "gmm_moment_condition"
15426 | "hausman_test" | "breusch_godfrey_test" | "box_pierce_test"
15427 | "adf_test_stat" | "pp_test_stat" | "kpss_test_stat"
15428 | "dickey_fuller_critical" | "engle_granger_step"
15429 | "johansen_trace_step" | "vecm_alpha_beta"
15430 | "panel_within_estimator" | "panel_between_estimator"
15431 | "panel_random_effects" | "arellano_bond_step"
15432 | "ols_estimator" | "ols_residual_variance" | "ols_r_squared"
15433 | "ols_adjusted_r2" | "akaike_info_crit" | "bayesian_info_crit"
15434 | "hannan_quinn_ic" | "f_statistic_pooled" | "breusch_pagan_lm"
15435 | "ramsey_reset_test" | "chow_test_stat" | "white_test_stat"
15436 | "goldfeld_quandt" | "wald_test_stat" | "score_test_stat"
15437 | "likelihood_ratio_test" | "two_sls_iv" | "iv_estimator"
15438 | "mle_normal_log_lik" | "mle_exponential_log_lik"
15439 | "mle_poisson_log_lik" | "gmm_moment_function"
15440 | "pooling_test_stat" | "heteroskedasticity_test"
15441 | "robust_se_huber_white" | "bootstrap_se_estimate"
15442 | "heckman_correction" | "tobit_log_likelihood"
15443 | "probit_log_likelihood" | "logit_log_likelihood"
15444 | "multinomial_logit_prob" | "ordered_probit_threshold"
15445 | "panel_var_step" | "impulse_response_step"
15446 | "variance_decomposition" | "granger_causality_chi2"
15447 | "cointegration_residual" | "error_correction_step"
15448 | "random_walk_innovation" | "random_walk_drift_step"
15449 | "ar_model_likelihood" | "ma_model_likelihood"
15450 | "arma_model_innovation"
15451 | "euler_char_complex" | "betti_zero" | "betti_one" | "betti_two"
15453 | "genus_surface" | "chern_first_2d" | "genus_curve_arith"
15454 | "genus_curve_geo" | "hodge_diamond_value" | "poincare_duality"
15455 | "fundamental_group_zn" | "homology_rank" | "cohomology_rank"
15456 | "homotopy_group_sphere_pi" | "mapping_class_torus"
15457 | "linking_number_two" | "writhe_polygon" | "torsion_coefficient"
15458 | "simplex_volume_n" | "simplicial_volume" | "nerve_complex_count"
15459 | "cech_zero_cohomology" | "de_rham_zero"
15460 | "poincare_polynomial_eval" | "chromatic_homology_rank"
15461 | "khovanov_q_grading" | "hochschild_zero" | "cyclic_homology_step"
15462 | "group_cohomology_dim" | "group_homology_dim"
15463 | "abelianization_quotient" | "free_group_rank_lower"
15464 | "nilpotency_class_lower" | "solvable_length_upper"
15465 | "schreier_index" | "todd_genus_eval" | "hirzebruch_signature"
15466 | "chern_simons_action" | "gauss_bonnet_total"
15467 | "seifert_genus_lower" | "alexander_polynomial_at_one"
15468 | "jones_polynomial_at_minus_one" | "jones_polynomial_at_i"
15469 | "homfly_evaluation" | "kauffman_bracket_eval"
15470 | "cabling_pair_signature" | "seifert_form_2x2"
15471 | "turaev_alexander_step" | "v_polynomial_eval"
15472 | "polynomial_jones_skein" | "delta_complex_count"
15473 | "poset_zeta_two" | "mobius_poset_two" | "mobius_function_pair"
15474 | "mobius_inversion_step" | "incidence_algebra_dim"
15475 | "quiver_path_count" | "representation_dim_step"
15476 | "weyl_group_order" | "root_system_count"
15477 | "cartan_determinant_a2" | "cartan_matrix_b2"
15478 | "killing_form_su2" | "casimir_eigenvalue_su2"
15479 | "universal_enveloping_dim" | "verma_character_step"
15480 | "plethystic_substitution_value" | "schur_polynomial_eval"
15481 | "hall_inner_product_two" | "plactic_class_size"
15482 | "robinson_schensted_pair" | "yamanouchi_word_count"
15483 | "rsk_size" | "character_su2" | "character_sun"
15484 | "quantum_dimension_su2" | "quantum_dimension_q"
15485 | "fusion_rule_su2_step" | "modular_data_s_value"
15486 | "modular_data_t_value" | "verlinde_count_step"
15487 | "quantum_invariant_eval" | "operad_count_two"
15488 | "moduli_dimension_curves" | "hodge_polynomial_eval"
15489 | "mirror_symmetry_check" | "gromov_witten_invariant"
15490 | "donaldson_invariant" | "seiberg_witten_value"
15491 | "floer_homology_rank" | "khovanov_rasmussen_s"
15492 | "ozsvath_szabo_tau" | "heegaard_genus_lower"
15493 | "fintushel_stern_step" | "bauer_furuta_step"
15494 | "geometric_intersection_number"
15495 | "algebraic_intersection_number"
15496 | "nernst_potential_full" | "electrode_potential_step"
15498 | "exchange_current_density" | "butler_volmer_current"
15499 | "tafel_anodic_current" | "tafel_cathodic_current"
15500 | "mass_transport_overpotential" | "limiting_current_density"
15501 | "diffusion_layer_thickness" | "faradaic_efficiency"
15502 | "coulombic_efficiency_cell" | "energy_efficiency_cell"
15503 | "voltaic_efficiency" | "charge_capacity_battery"
15504 | "energy_density_battery" | "power_density_battery"
15505 | "specific_capacity_active" | "columbic_capacity_lihalfcell"
15506 | "ragone_point" | "peukert_capacity" | "peukert_exponent_fit"
15507 | "shepherd_voltage_step" | "nernst_planck_flux"
15508 | "debye_length_electrolyte" | "debye_huckel_activity"
15509 | "gouy_chapman_potential" | "stern_layer_capacitance"
15510 | "double_layer_capacitance" | "helmholtz_capacitance"
15511 | "zeta_potential_estimate" | "electroosmotic_velocity"
15512 | "hagen_poiseuille_eo" | "diffuse_layer_thickness"
15513 | "poisson_boltzmann_step" | "linearized_pb_step"
15514 | "electrochem_impedance_z" | "randles_circuit_z"
15515 | "warburg_impedance" | "cole_cole_eis" | "nyquist_phase"
15516 | "charge_transfer_resistance" | "solution_resistance_estimate"
15517 | "ionic_conductivity_arrhenius" | "nernst_einstein_diffusivity"
15518 | "walden_product" | "kohlrausch_law"
15519 | "onsager_relation_two_species" | "trasatti_voltammetry_charge"
15520 | "randles_sevcik_peak" | "levich_current_rde"
15521 | "koutecky_levich_intercept" | "mott_schottky_capacitance"
15522 | "flat_band_potential" | "schottky_barrier_height"
15523 | "photocurrent_density" | "quantum_efficiency_photo"
15524 | "overall_efficiency_pec" | "fuel_cell_polarization"
15525 | "electrolyzer_voltage" | "faraday_efficiency_h2"
15526 | "overpotential_oer" | "overpotential_her"
15527 | "electrocrystallization_step" | "nucleation_rate_constant"
15528 | "metal_corrosion_rate" | "pourbaix_line_value"
15529 | "mixed_potential_step" | "electrochemiluminescence_yield"
15530 | "solid_electrolyte_capacity" | "ionic_liquid_viscosity_step"
15531 | "lithium_ion_diffusivity" | "soc_estimate_coulomb"
15532 | "soh_capacity_fade" | "ocv_lithium_ion_step"
15533 | "state_of_charge_kalman" | "thermal_runaway_threshold"
15534 | "joule_heating_battery" | "calorimetric_heat_battery"
15535 | "abuse_test_voltage" | "swelling_strain_step"
15536 | "sei_resistance_growth" | "binder_content_optimal"
15537 | "porosity_active_layer" | "tortuosity_estimate_bruggeman"
15538 | "electrolyte_decomposition_temp" | "gibbs_thomson_undercooling"
15539 | "nernst_diffusion_layer" | "diff_coeff_aqueous_estimate"
15540 | "salt_activity_coefficient" | "mean_activity_coeff_pitzer"
15541 | "osmotic_coefficient_pitzer" | "debye_huckel_screening_factor"
15542 | "ph_at_isoelectric" | "buffer_capacity_acid_base"
15543 | "henderson_hasselbalch_solve" | "titration_endpoint_index"
15544 | "tensor_contract_two" | "tensor_outer_two" | "tensor_trace_index"
15546 | "tensor_symmetrize_two" | "tensor_antisymmetrize_two"
15547 | "levi_civita_three" | "levi_civita_four"
15548 | "kronecker_three" | "kronecker_four"
15549 | "metric_minkowski_eta_step" | "metric_schwarzschild_step"
15550 | "metric_kerr_step_simple" | "metric_frw_lapse"
15551 | "christoffel_first_kind_step" | "christoffel_second_kind_step"
15552 | "riemann_tensor_step_zero" | "riemann_curvature_normal_form"
15553 | "ricci_tensor_step_zero" | "scalar_curvature_step"
15554 | "einstein_tensor_step" | "weyl_tensor_step_zero"
15555 | "schouten_tensor_step" | "geodesic_equation_step_zero"
15556 | "parallel_transport_step" | "covariant_derivative_step"
15557 | "christoffel_symbol_normalize" | "ricci_identity_step"
15558 | "bianchi_first_identity_check" | "bianchi_second_identity_check"
15559 | "killing_vector_lie_step" | "lie_derivative_scalar_step"
15560 | "lie_derivative_vector_step" | "exterior_derivative_one_form"
15561 | "hodge_star_one_form" | "codifferential_step"
15562 | "laplace_de_rham_step" | "volume_form_riemannian"
15563 | "hodge_inner_product_one" | "sectional_curvature_two_plane"
15564 | "gauss_codazzi_step" | "mainardi_codazzi_step"
15565 | "weingarten_map_step" | "shape_operator_eig"
15566 | "mean_curvature_step" | "gaussian_curvature_step"
15567 | "extrinsic_principal_curv" | "intrinsic_principal_curv"
15568 | "geodesic_curvature_step" | "darboux_frame_step"
15569 | "fermi_normal_step" | "synge_world_function"
15570 | "raychaudhuri_step" | "expansion_scalar_step"
15571 | "shear_tensor_step" | "twist_tensor_step"
15572 | "optical_scalars_step" | "peeling_step_psi4"
15573 | "ads_metric_step" | "de_sitter_metric_step"
15574 | "warped_product_step_zero" | "kaluza_klein_step"
15575 | "brans_dicke_step" | "horndeski_step"
15576 | "einstein_dilaton_step" | "gauss_bonnet_term_2d"
15577 | "chern_pontryagin_4d_step" | "adm_mass_step"
15578 | "komar_mass_step" | "bondi_mass_step"
15579 | "brown_york_quasilocal" | "isolated_horizon_charge"
15580 | "trapped_surface_check" | "apparent_horizon_step"
15581 | "event_horizon_check" | "cosmological_constant_term"
15582 | "de_sitter_radius_step" | "anti_de_sitter_radius_step"
15583 | "penrose_diagram_factor" | "conformal_compactification_step"
15584 | "schwarzschild_kruskal_step" | "gullstrand_painleve_step"
15585 | "kerr_newman_charge_term" | "boyer_lindquist_step"
15586 | "hartle_thorne_metric" | "oppenheimer_volkoff_step"
15587 | "post_newtonian_step" | "shapiro_delay_step"
15588 | "mercury_perihelion_advance"
15589 | "gravitational_wave_quadrupole"
15590 | "plus_polarization_amp" | "cross_polarization_amp"
15591 | "chirp_mass_inspiral_step" | "isco_radius_kerr_step"
15592 | "spin_orbit_coupling_term" | "spin_spin_coupling_term"
15593 | "hawking_area_increase" | "unruh_temperature_full"
15594 | "bekenstein_entropy_step" | "holographic_entanglement_step"
15595 | "ryu_takayanagi_step" | "swampland_distance_check"
15596 | "conditional_entropy_step" | "joint_entropy_step"
15598 | "relative_entropy_kl" | "mutual_information_step"
15599 | "chain_rule_entropy" | "fano_inequality_bound"
15600 | "data_processing_inequality" | "arithmetic_coding_interval"
15601 | "range_coding_step" | "golomb_rice_code"
15602 | "elias_gamma_code" | "elias_delta_code" | "exp_golomb_code"
15603 | "fibonacci_code" | "shannon_fano_elias_code"
15604 | "huffman_balanced_step" | "arithmetic_decode_interval"
15605 | "range_decode_step" | "universal_code_length"
15606 | "ziv_lempel_estimate" | "lz77_match_length"
15607 | "lz78_dictionary_growth" | "lzw_step_dict"
15608 | "ppm_predict_prob" | "deflate_huffman_lit"
15609 | "brotli_distance_code_count" | "zstd_window_size_log"
15610 | "mpeg_quant_value" | "jpeg_zig_zag_index"
15611 | "jpeg_dct_8x8_quant" | "hadamard_walsh_transform_step"
15612 | "karhunen_loeve_step" | "discrete_haar_step"
15613 | "db4_wavelet_step" | "biorthogonal_step"
15614 | "beylkin_wavelet_step" | "coiflet_wavelet_step"
15615 | "mallat_pyramid_step" | "threshold_soft_value"
15616 | "threshold_hard_value" | "median_filter_window"
15617 | "mean_filter_window" | "gaussian_filter_window"
15618 | "unsharp_mask_step" | "sobel_kernel_value"
15619 | "prewitt_kernel_value" | "roberts_kernel_value"
15620 | "laplacian_kernel_value" | "canny_threshold_step"
15621 | "hough_accumulator_step" | "ransac_iteration_count"
15622 | "optical_flow_lk_step" | "horn_schunck_step"
15623 | "kalman_predict_state" | "kalman_update_state"
15624 | "particle_filter_resample" | "unscented_sigma_point"
15625 | "ekf_jacobian_step" | "markov_decision_value"
15626 | "bellman_equation_step" | "q_learning_update"
15627 | "policy_iteration_step" | "value_iteration_step"
15628 | "sarsa_update" | "double_q_learning_step"
15629 | "ucb1_action_value" | "thompson_sample_beta"
15630 | "boltzmann_softmax_action" | "explore_exploit_epsilon"
15631 | "montecarlo_returns_step" | "td_zero_update"
15632 | "td_lambda_update" | "gradient_temporal_diff"
15633 | "deep_q_target" | "ddpg_critic_loss_step"
15634 | "ppo_clip_term" | "trpo_kl_constraint"
15635 | "a3c_advantage_step" | "ppo_advantage_step"
15636 | "gae_advantage_step" | "generalized_advantage"
15637 | "information_bottleneck_step" | "free_energy_principle"
15638 | "fisher_info_metric" | "kullback_jensen_div"
15639 | "hellinger_distance_step" | "total_variation_distance"
15640 | "bhattacharyya_coefficient" | "wasserstein_dist_emp"
15641 | "chisquare_metric" | "hellinger_kernel"
15642 | "jensen_shannon_div" | "renyi_divergence_step"
15643 | "amari_alpha_div" | "csiszar_phi_div"
15644 | "sinkhorn_iteration_step" | "sliced_wasserstein"
15645 | "gromov_wasserstein_step" | "spectral_signature_match"
15646 | "mfcc_coeff_step" | "chroma_feature_step"
15647 | "tsp_lower_bound_mst" | "tsp_held_karp_step"
15649 | "christofides_ratio_bound" | "two_opt_swap_delta"
15650 | "or_opt_delta" | "three_opt_delta" | "lin_kernighan_step"
15651 | "nearest_neighbor_tour_step" | "greedy_edge_tour"
15652 | "nearest_insertion_step" | "farthest_insertion_step"
15653 | "cheapest_insertion_step" | "max_flow_ford_fulkerson_step"
15654 | "edmonds_karp_step" | "dinic_blocking_flow"
15655 | "push_relabel_step" | "boykov_kolmogorov_step"
15656 | "mincut_stoer_wagner" | "gomory_hu_step"
15657 | "karger_contract_edge" | "karger_min_cut_count"
15658 | "maximum_bipartite_matching" | "hopcroft_karp_phase"
15659 | "blossom_match_step" | "weighted_match_kuhn_step"
15660 | "hungarian_method_step" | "ap_jonker_volgenant_step"
15661 | "assignment_lower_bound" | "job_shop_makespan_lower"
15662 | "flow_shop_johnson_step" | "parallel_machine_lpt"
15663 | "parallel_machine_spt" | "list_scheduling_step"
15664 | "graham_2approx_bound" | "chc_bound_makespan"
15665 | "bin_packing_first_fit" | "bin_packing_best_fit"
15666 | "bin_packing_next_fit" | "bin_packing_lower_bound_l1"
15667 | "multidim_packing_step" | "knapsack_01_dp_value"
15668 | "knapsack_unbounded_dp" | "knapsack_fractional_step"
15669 | "knapsack_branch_bound" | "knapsack_lp_relaxation"
15670 | "multi_knapsack_step" | "quadratic_assignment_step"
15671 | "qap_lower_bound" | "graph_coloring_dsatur_step"
15672 | "graph_coloring_welsh_powell"
15673 | "graph_coloring_brooks_bound" | "graph_coloring_lp_bound"
15674 | "fractional_chromatic_lower" | "list_coloring_step"
15675 | "edge_coloring_vizing_step" | "clique_number_lower"
15676 | "independence_number_upper" | "vertex_cover_lp_round"
15677 | "dominating_set_greedy_step" | "dominating_set_lp_bound"
15678 | "set_cover_greedy_step" | "set_cover_lp_round"
15679 | "hitting_set_greedy" | "weighted_set_cover_step"
15680 | "matroid_greedy_step" | "matroid_intersection_step"
15681 | "submodular_greedy_step" | "submodular_curvature_bound"
15682 | "nemhauser_wolsey_bound" | "lp_relax_round"
15683 | "branch_and_bound_step" | "cutting_plane_step"
15684 | "gomory_cut_step" | "chvatal_gomory_cut"
15685 | "mixed_integer_round_up" | "mixed_integer_round_down"
15686 | "sos_constraint_check" | "column_generation_step"
15687 | "benders_decomposition_step" | "dantzig_wolfe_step"
15688 | "lagrangian_relax_step" | "lagrangian_dual_step"
15689 | "subgradient_step_size" | "nonlinear_dual_step"
15690 | "augmented_lagrangian_step" | "admm_primal_step"
15691 | "admm_dual_step" | "proximal_gradient_step"
15692 | "nesterov_accelerate_step" | "fista_step" | "ista_step"
15693 | "mirror_descent_step" | "frank_wolfe_step"
15694 | "conditional_gradient_step" | "greedy_set_cover_round"
15695 | "local_search_swap_step" | "tabu_search_move_score"
15696 | "simulated_annealing_step" | "genetic_crossover_one_point"
15697 | "mutation_bit_flip_prob" | "roulette_wheel_select_index"
15698 | "stefan_boltzmann_radiation" | "emissivity_grey_body"
15700 | "albedo_blackbody_balance" | "solar_constant_at_distance"
15701 | "total_solar_irradiance_step" | "absorbed_short_wave"
15702 | "emitted_long_wave" | "clausius_clapeyron_full"
15703 | "relative_humidity_step" | "dewpoint_temperature_full"
15704 | "wet_bulb_potential" | "virtual_temperature_full"
15705 | "density_altitude_full" | "geopotential_height_full"
15706 | "geometric_height_full" | "adiabatic_lapse_rate_dry"
15707 | "adiabatic_lapse_rate_moist" | "brunt_vaisala_full"
15708 | "richardson_number_step" | "gradient_richardson_full"
15709 | "flux_richardson_full" | "turbulent_kinetic_energy_step"
15710 | "mixing_length_prandtl" | "monin_obukhov_length"
15711 | "similarity_function_phi" | "log_law_wind_profile"
15712 | "power_law_wind_profile" | "ekman_layer_depth"
15713 | "ekman_pumping_step" | "geostrophic_wind_step"
15714 | "gradient_wind_step" | "thermal_wind_step"
15715 | "quasi_geostrophic_omega" | "omega_equation_step"
15716 | "potential_temperature_step" | "equivalent_potential_temp"
15717 | "saturation_equivalent_pt" | "ipv_potential_vorticity"
15718 | "ertel_pv_step" | "absolute_vorticity_step"
15719 | "relative_vorticity_step" | "divergence_omega_step"
15720 | "streamfunction_step" | "velocity_potential_step"
15721 | "helmholtz_decomp_step" | "courant_friedrichs_lewy"
15722 | "peclet_number_step" | "prandtl_number_step"
15723 | "reynolds_full_number" | "schmidt_number_step"
15724 | "sherwood_number_step" | "nusselt_full_number"
15725 | "grashof_number_step" | "rayleigh_number_step"
15726 | "weber_number_step" | "froude_number_step"
15727 | "strouhal_full" | "mach_full_step"
15728 | "biot_number_step" | "fourier_number_step"
15729 | "turbulence_intensity_step" | "hurst_exponent_estimate"
15730 | "detrended_fluct_alpha" | "power_spectrum_slope"
15731 | "spectral_kappa_minus53" | "batchelor_scale_step"
15732 | "kolmogorov_microscale" | "taylor_microscale_step"
15733 | "integral_length_scale" | "turbulent_dissipation_eps"
15734 | "isotropic_relation_check" | "sst_anomaly_step"
15735 | "enso_index_step" | "amo_index_step" | "nao_index_step"
15736 | "soi_oscillation_index" | "pdo_index_step" | "mjo_phase_step"
15737 | "walker_circulation_step" | "hadley_cell_max_lat"
15738 | "ferrel_cell_step" | "itcz_position_lat" | "trade_wind_speed"
15739 | "westerlies_jet_speed" | "polar_vortex_radius"
15740 | "arctic_oscillation_step" | "indian_monsoon_index"
15741 | "african_monsoon_index" | "qbo_oscillation_step"
15742 | "solar_cycle_phase" | "sunspot_relative_number"
15743 | "geomagnetic_kp_index" | "ozone_dobson_total"
15744 | "chlorine_radical_decay" | "montreal_protocol_track"
15745 | "co2_growth_rate_step" | "methane_growth_rate"
15746 | "aerosol_optical_depth" | "ice_age_milankovitch"
15747 | "greenhouse_forcing_step"
15748 | "game_two_player_value" | "nash_equilibrium_pair"
15750 | "mixed_strategy_value" | "zero_sum_minmax"
15751 | "saddle_point_check" | "correlated_equilibrium_value"
15752 | "shapley_value_two_step" | "banzhaf_index_two"
15753 | "nucleolus_lp_step" | "core_membership_check"
15754 | "imputation_efficient_check" | "imputation_individual_rational"
15755 | "prisoners_dilemma_payoff" | "matching_pennies_payoff"
15756 | "chicken_game_payoff" | "stag_hunt_payoff"
15757 | "battle_sexes_payoff" | "public_goods_game_payoff"
15758 | "tragedy_commons_metric" | "ultimatum_acceptance_prob"
15759 | "dictator_game_share" | "trust_game_repayment"
15760 | "cooperative_game_value" | "characteristic_function"
15761 | "bargaining_set_check" | "kalai_smorodinsky_step"
15762 | "nash_bargaining_solution" | "egalitarian_solution"
15763 | "utilitarian_solution" | "social_welfare_sum"
15764 | "arrow_impossibility_check" | "gibbard_satterthwaite_check"
15765 | "borda_count_step" | "condorcet_winner_check"
15766 | "plurality_winner_step" | "kemeny_score_step"
15767 | "dodgson_swap_count" | "coombs_runoff_step"
15768 | "single_transferable_vote" | "range_voting_score"
15769 | "approval_voting_max" | "schulze_method_step"
15770 | "copeland_score_step" | "black_method_winner"
15771 | "median_voter_step" | "hotelling_location_step"
15772 | "arrow_pareto_check" | "fair_division_envy_free"
15773 | "proportional_share" | "maximin_share"
15774 | "egalitarian_split" | "nash_social_welfare"
15775 | "divisible_goods_proportional" | "indivisible_envy_free_check"
15776 | "adjusted_winner_pct" | "sealed_bid_first_price"
15777 | "sealed_bid_second_price" | "english_auction_step"
15778 | "dutch_auction_step" | "all_pay_auction_step"
15779 | "vcg_payment_step" | "revenue_equivalence_check"
15780 | "truthful_mechanism_check" | "incentive_compatibility_check"
15781 | "mechanism_design_obj" | "double_auction_step"
15782 | "combinatorial_auction_step" | "posted_price_offer_accept"
15783 | "matching_market_step" | "deferred_acceptance_step"
15784 | "boston_mechanism_step" | "top_trading_cycles_step"
15785 | "school_choice_match" | "roommate_match_step"
15786 | "network_formation_step" | "coordination_game_payoff"
15787 | "evolutionary_stable_strategy" | "replicator_dynamics_step"
15788 | "hawk_dove_payoff" | "fictitious_play_step"
15789 | "best_response_dynamic" | "quantal_response_logit"
15790 | "level_k_step" | "cognitive_hierarchy_step"
15791 | "sequential_eq_check" | "subgame_perfect_eq"
15792 | "stackelberg_step" | "cournot_quantity_step"
15793 | "bertrand_price_step" | "hotelling_price_step"
15794 | "collusion_payoff_step" | "folk_theorem_value"
15795 | "repeated_game_avg_payoff" | "discount_factor_step"
15796 | "trigger_strategy_payoff" | "grim_trigger_step"
15797 | "tit_for_tat_step" | "prisoners_repeated_eq"
15798 | "mertens_zamir_step" | "ex_post_value_check"
15799 | "ex_ante_value_check" | "common_knowledge_iterations"
15800 | "cas_simplify_term" | "cas_expand_two_terms"
15802 | "cas_factor_quadratic" | "cas_partial_fraction_simple"
15803 | "cas_polynomial_gcd_step" | "cas_polynomial_div_step"
15804 | "cas_lagrange_interpolate" | "cas_chebyshev_eval"
15805 | "cas_legendre_eval" | "cas_hermite_eval"
15806 | "cas_laguerre_eval" | "cas_jacobi_eval"
15807 | "cas_gegenbauer_eval" | "cas_taylor_coefficient"
15808 | "cas_padé_diagonal" | "cas_continued_fraction_step"
15809 | "cas_resultant_two" | "cas_subresultant_two"
15810 | "cas_groebner_lt_step" | "cas_buchberger_step"
15811 | "cas_macaulay_matrix_step" | "cas_modular_inverse"
15812 | "cas_extended_euclid_step" | "cas_smith_normal_step"
15813 | "cas_hermite_normal_step" | "cas_radical_simplify"
15814 | "cas_minimal_polynomial" | "cas_gcd_polynomial_step"
15815 | "cas_resultant_x_y" | "cas_solve_linear"
15816 | "cas_solve_quadratic" | "cas_solve_cubic"
15817 | "cas_solve_quartic" | "cas_solve_polynomial_n"
15818 | "cas_root_isolate_step" | "cas_sturm_sequence_step"
15819 | "cas_descartes_rule_count" | "cas_companion_matrix_root"
15820 | "cas_polynomial_roots_kahan"
15821 | "cas_eigenvalue_inverse_iteration" | "cas_qr_iteration_step"
15822 | "cas_jacobi_eigen_step" | "cas_lanczos_iteration_step"
15823 | "cas_arnoldi_iteration_step" | "cas_givens_rotation_apply"
15824 | "cas_householder_reflection" | "cas_modified_gram_schmidt"
15825 | "cas_classical_gram_schmidt" | "cas_rank_revealing_qr"
15826 | "cas_pivoted_lu_step" | "cas_block_lu_step"
15827 | "cas_cholesky_step" | "cas_modified_cholesky"
15828 | "cas_ldlt_step" | "cas_bunch_kaufman_step"
15829 | "cas_woodbury_identity" | "cas_matrix_pencil_step"
15830 | "cas_generalized_eigen" | "cas_singular_value_step"
15831 | "cas_truncated_svd_value" | "cas_pseudoinverse_step"
15832 | "cas_polar_decomposition" | "cas_schur_decomposition_step"
15833 | "cas_quasi_triangular" | "cas_riccati_continuous_step"
15834 | "cas_riccati_discrete_step" | "cas_lyapunov_continuous_step"
15835 | "cas_lyapunov_discrete_step" | "cas_sylvester_equation_step"
15836 | "cas_kronecker_product_step" | "cas_vec_operator_step"
15837 | "cas_matrix_function_step" | "cas_matrix_log_step"
15838 | "cas_matrix_exp_pade" | "cas_matrix_sqrt_step"
15839 | "cas_drazin_inverse_step" | "cas_moore_penrose_step"
15840 | "cas_least_squares_solve" | "cas_total_least_squares"
15841 | "cas_constrained_ls_step" | "cas_truncated_lsq"
15842 | "cas_regularized_lsq_tikhonov" | "cas_basis_pursuit_step"
15843 | "cas_lasso_soft_threshold" | "cas_elastic_net_step"
15844 | "cas_omp_step" | "cas_iht_iteration"
15845 | "cas_cosamp_step" | "cas_admm_lasso_step"
15846 | "cas_proximal_l1_step" | "cas_proximal_l2_step"
15847 | "cas_proximal_l_inf_step" | "cas_indicator_simplex_proj"
15848 | "cas_proj_l1_ball" | "cas_proj_l2_ball"
15849 | "cas_proj_box" | "cas_proj_psd_cone"
15850 | "cas_proj_soc_step" | "cas_proj_exp_cone"
15851 | "cas_dykstra_step" | "cas_alternating_projection"
15852 | "cas_polya_enumeration_step" | "cas_burnside_count_step"
15853 | "ml_relu_step" | "ml_leaky_relu_step" | "ml_elu_step"
15855 | "ml_selu_step" | "ml_gelu_step" | "ml_swish_step"
15856 | "ml_mish_step" | "ml_softplus_step" | "ml_softsign_step"
15857 | "ml_hard_sigmoid" | "ml_hard_tanh" | "ml_prelu_step"
15858 | "ml_celu_step" | "ml_silu_step" | "ml_logsumexp_step"
15859 | "ml_log_softmax_step" | "ml_log_sigmoid"
15860 | "ml_glu_step" | "ml_geglu_step" | "ml_swiglu_step"
15861 | "ml_attention_score_step" | "ml_scaled_dot_product"
15862 | "ml_multihead_avg" | "ml_softmax_temperature"
15863 | "ml_dropout_mask_prob" | "ml_layer_norm_step"
15864 | "ml_batch_norm_step" | "ml_group_norm_step"
15865 | "ml_rms_norm_step" | "ml_instance_norm_step"
15866 | "ml_weight_norm_step" | "ml_spectral_norm_step"
15867 | "ml_l2_normalize_step" | "ml_huber_loss_step"
15868 | "ml_smooth_l1_loss" | "ml_focal_loss_step"
15869 | "ml_dice_loss_step" | "ml_iou_loss_step"
15870 | "ml_giou_loss_step" | "ml_diou_loss_step"
15871 | "ml_ciou_loss_step" | "ml_contrastive_loss"
15872 | "ml_triplet_loss_step" | "ml_arcface_loss_step"
15873 | "ml_center_loss_step" | "ml_kl_divergence_loss"
15874 | "ml_cross_entropy_loss" | "ml_binary_cross_entropy"
15875 | "ml_label_smoothing" | "ml_mixup_lambda"
15876 | "ml_cutmix_box_iou" | "ml_random_erasing_step"
15877 | "ml_cosine_lr_schedule" | "ml_warmup_lr_step"
15878 | "ml_step_lr_schedule" | "ml_exponential_lr"
15879 | "ml_polynomial_lr" | "ml_one_cycle_lr"
15880 | "ml_inverse_sqrt_lr" | "ml_cyclic_lr_step"
15881 | "ml_sgd_step" | "ml_momentum_step"
15882 | "ml_nesterov_momentum" | "ml_adagrad_step"
15883 | "ml_rmsprop_step" | "ml_adam_step"
15884 | "ml_adamw_step" | "ml_adamax_step"
15885 | "ml_nadam_step" | "ml_radam_step"
15886 | "ml_lookahead_step" | "ml_lamb_step"
15887 | "ml_lars_step" | "ml_yogi_step"
15888 | "ml_amsgrad_step" | "ml_adabelief_step"
15889 | "ml_shampoo_step" | "ml_lion_step"
15890 | "ml_sophia_step" | "ml_gradient_clip_norm"
15891 | "ml_gradient_clip_value" | "ml_gradient_accumulate"
15892 | "ml_gradient_centralize" | "ml_weight_decay_step"
15893 | "ml_he_init_value" | "ml_xavier_init_value"
15894 | "ml_glorot_init_value" | "ml_orthogonal_init"
15895 | "ml_truncnormal_init" | "ml_kaiming_init"
15896 | "ml_lecun_init_value" | "ml_zero_init"
15897 | "ml_constant_init" | "ml_uniform_init"
15898 | "ml_one_hot_index" | "ml_label_to_id"
15899 | "ml_id_to_label_step" | "ml_token_logit_top_k"
15900 | "ml_topk_argmax" | "ml_nucleus_sample_p"
15901 | "ml_temperature_decay" | "ml_repetition_penalty"
15902 | "ml_eos_logit_boost"
15903 | "nlp_bm25_score" | "nlp_tf_idf_step" | "nlp_okapi_score"
15905 | "nlp_word_freq_value" | "nlp_doc_freq_step"
15906 | "nlp_inverse_doc_freq" | "nlp_cosine_similarity_two"
15907 | "nlp_jaccard_similarity_two" | "nlp_overlap_coefficient"
15908 | "nlp_dice_coefficient_two" | "nlp_simpson_coefficient"
15909 | "nlp_levenshtein_dist" | "nlp_damerau_levenshtein"
15910 | "nlp_jaro_distance" | "nlp_jaro_winkler"
15911 | "nlp_hamming_distance" | "nlp_lcs_length" | "nlp_lcs_ratio"
15912 | "nlp_meteor_score" | "nlp_bleu_score_n"
15913 | "nlp_rouge_score_n" | "nlp_chrf_score" | "nlp_ter_score"
15914 | "nlp_wer_score" | "nlp_cer_score" | "nlp_perplexity_value"
15915 | "nlp_bits_per_character" | "nlp_char_ngram_count"
15916 | "nlp_word_ngram_count" | "nlp_skip_gram_count"
15917 | "nlp_byte_pair_merge_step" | "nlp_wordpiece_score"
15918 | "nlp_unigram_lm_score" | "nlp_kneser_ney_step"
15919 | "nlp_witten_bell_step" | "nlp_good_turing_count"
15920 | "nlp_laplace_smoothing" | "nlp_lidstone_smoothing"
15921 | "nlp_jelinek_mercer" | "nlp_dirichlet_smoothing"
15922 | "nlp_query_likelihood_step" | "nlp_kl_lm_div"
15923 | "nlp_pmi_score" | "nlp_npmi_score"
15924 | "nlp_chi2_collocation" | "nlp_loglikelihood_collocation"
15925 | "nlp_t_score_collocation" | "nlp_dunning_log_likelihood"
15926 | "nlp_lda_alpha_step" | "nlp_lda_beta_step"
15927 | "nlp_lda_topic_dist" | "nlp_plsa_step"
15928 | "nlp_word2vec_skipgram_loss" | "nlp_word2vec_cbow_loss"
15929 | "nlp_glove_loss_step" | "nlp_fasttext_subword_count"
15930 | "nlp_byte_level_bpe_step" | "nlp_sentencepiece_score"
15931 | "nlp_unigram_subword_loss" | "nlp_subword_regularization"
15932 | "nlp_pointwise_attn_score" | "nlp_relative_position_bias"
15933 | "nlp_alibi_position_bias" | "nlp_rope_rotary_angle"
15934 | "nlp_rope_apply_step" | "nlp_position_encoding_sin"
15935 | "nlp_position_encoding_cos" | "nlp_pe_freq_band"
15936 | "nlp_max_seq_len_check" | "nlp_token_drop_rate"
15937 | "nlp_byte_frequency" | "nlp_char_frequency"
15938 | "nlp_punct_ratio" | "nlp_uppercase_ratio"
15939 | "nlp_digit_ratio" | "nlp_emoji_ratio"
15940 | "nlp_url_count" | "nlp_email_count" | "nlp_phone_count"
15941 | "nlp_hashtag_count" | "nlp_mention_count"
15942 | "nlp_token_overlap_two" | "nlp_word_mover_dist"
15943 | "nlp_sif_weight_step" | "nlp_doc_embedding_avg"
15944 | "nlp_attention_pool_step" | "nlp_max_pool_step"
15945 | "nlp_avg_pool_step" | "nlp_sum_pool_step"
15946 | "nlp_self_attn_compute_step" | "nlp_cross_attn_compute_step"
15947 | "nlp_window_attn_step" | "nlp_strided_attn_step"
15948 | "nlp_block_attn_step" | "nlp_sliding_window_step"
15949 | "nlp_local_attn_step" | "nlp_dilated_attn_step"
15950 | "nlp_global_attn_step" | "nlp_sparse_attn_score"
15951 | "nlp_linformer_step" | "nlp_performer_step"
15952 | "nlp_reformer_step" | "nlp_longformer_step"
15953 | "nlp_bigbird_step" | "nlp_routing_attn_step"
15954 | "gfx_perspective_proj_x" | "gfx_perspective_proj_y"
15956 | "gfx_orthographic_proj" | "gfx_view_matrix_step"
15957 | "gfx_lookat_forward" | "gfx_lookat_right" | "gfx_lookat_up"
15958 | "gfx_quat_to_axis_angle" | "gfx_axis_angle_to_quat"
15959 | "gfx_quat_slerp_step" | "gfx_quat_nlerp_step"
15960 | "gfx_quat_dot_two" | "gfx_quat_inverse_step"
15961 | "gfx_quat_to_euler_pitch" | "gfx_quat_to_euler_yaw"
15962 | "gfx_quat_to_euler_roll" | "gfx_euler_to_quat_x"
15963 | "gfx_euler_to_quat_y" | "gfx_euler_to_quat_z"
15964 | "gfx_euler_to_quat_w" | "gfx_rotation_matrix_xx"
15965 | "gfx_rotation_matrix_yy" | "gfx_rotation_matrix_zz"
15966 | "gfx_translation_matrix_step" | "gfx_scale_matrix_step"
15967 | "gfx_shear_matrix_xy" | "gfx_homogeneous_divide"
15968 | "gfx_screen_space_x" | "gfx_screen_space_y"
15969 | "gfx_ndc_to_screen_x" | "gfx_ndc_to_screen_y"
15970 | "gfx_screen_to_ndc_x" | "gfx_screen_to_ndc_y"
15971 | "gfx_clip_polygon_step" | "gfx_sutherland_hodgman"
15972 | "gfx_cohen_sutherland_code" | "gfx_liang_barsky_t"
15973 | "gfx_bresenham_step_x" | "gfx_bresenham_step_y"
15974 | "gfx_xiaolin_wu_intensity" | "gfx_aabb_intersect_check"
15975 | "gfx_obb_overlap_step" | "gfx_sphere_intersect_t"
15976 | "gfx_ray_triangle_t" | "gfx_ray_plane_t" | "gfx_ray_box_t"
15977 | "gfx_ray_sphere_t" | "gfx_ray_disk_t"
15978 | "gfx_ray_cylinder_t" | "gfx_ray_cone_t"
15979 | "gfx_ray_ellipsoid_t" | "gfx_ray_torus_t_approx"
15980 | "gfx_barycentric_alpha" | "gfx_barycentric_beta"
15981 | "gfx_barycentric_gamma" | "gfx_phong_diffuse_step"
15982 | "gfx_phong_specular_step" | "gfx_phong_ambient_step"
15983 | "gfx_blinn_specular_step" | "gfx_lambert_term"
15984 | "gfx_oren_nayar_term" | "gfx_cook_torrance_d_ggx"
15985 | "gfx_cook_torrance_g_smith" | "gfx_cook_torrance_f_schlick"
15986 | "gfx_disney_principled_d" | "gfx_microfacet_brdf_step"
15987 | "gfx_subsurface_scattering_term" | "gfx_translucent_falloff"
15988 | "gfx_normal_distribution_ggx"
15989 | "gfx_geometric_attenuation_smith"
15990 | "gfx_fresnel_dielectric_step" | "gfx_fresnel_conductor_step"
15991 | "gfx_index_of_refraction" | "gfx_snells_law_angle"
15992 | "gfx_total_internal_reflection" | "gfx_refract_direction_x"
15993 | "gfx_reflect_direction_x" | "gfx_environment_map_uv_u"
15994 | "gfx_environment_map_uv_v" | "gfx_cube_map_face_index"
15995 | "gfx_octahedral_encode_x" | "gfx_octahedral_encode_y"
15996 | "gfx_spherical_harmonic_y00" | "gfx_spherical_harmonic_y10"
15997 | "gfx_spherical_harmonic_y11" | "gfx_spherical_harmonic_y20"
15998 | "gfx_zonal_harmonic_step" | "gfx_irradiance_sh_eval"
15999 | "gfx_radiance_sh_eval" | "gfx_skybox_uv_u" | "gfx_skybox_uv_v"
16000 | "gfx_tonemap_reinhard" | "gfx_tonemap_aces"
16001 | "gfx_tonemap_uncharted2" | "gfx_tonemap_filmic"
16002 | "gfx_gamma_correct_step" | "gfx_srgb_to_linear"
16003 | "gfx_linear_to_srgb" | "gfx_dither_bayer_4x4"
16004 | "gfx_dither_floyd_steinberg" | "gfx_oklab_l_step"
16005 | "gfx_oklab_a_step" | "gfx_oklab_b_step"
16006 | "gfx_oklch_chroma" | "gfx_oklch_hue"
16007 | "gfx_pcg_hash_step" | "gfx_xorshift_step"
16008 | "gfx_halton_step" | "gfx_sobol_step"
16009 | "gfx_van_der_corput" | "gfx_low_discrepancy_step"
16010 | "gfx_blue_noise_value" | "gfx_perlin_noise_step"
16011 | "gfx_simplex_noise_step" | "gfx_fbm_noise_step"
16012 | "gfx_worley_noise_step" | "gfx_voronoi_distance"
16013 | "gfx_curl_noise_step" | "gfx_gradient_noise_step"
16014 | "gfx_value_noise_step" | "gfx_signed_distance_box"
16015 | "gfx_signed_distance_sphere" | "gfx_signed_distance_capsule"
16016 | "db_b_tree_split" | "db_b_tree_merge"
16018 | "db_lsm_compaction_step" | "db_skiplist_height_pick"
16019 | "db_bloom_filter_bit_index" | "db_cuckoo_filter_fingerprint"
16020 | "db_quotient_filter_canonical" | "db_count_min_sketch_bin"
16021 | "db_hyperloglog_register_max" | "db_min_hash_value"
16022 | "db_simhash_bit" | "db_consistent_hash_index"
16023 | "db_rendezvous_hash_score" | "db_jump_hash_bucket"
16024 | "db_maglev_hash_step" | "db_lru_cache_eviction_age"
16025 | "db_lfu_cache_decay" | "db_arc_cache_score"
16026 | "db_clock_cache_hand" | "db_tinylfu_admit_score"
16027 | "db_w_tinylfu_freq" | "db_buffer_pool_score"
16028 | "db_query_plan_cost_step" | "db_join_selectivity_step"
16029 | "db_index_seek_cost" | "db_seq_scan_cost"
16030 | "db_index_scan_cost" | "db_sort_cost_estimate"
16031 | "db_hash_join_cost" | "db_merge_join_cost"
16032 | "db_nested_loop_cost" | "db_query_cardinality"
16033 | "db_histogram_bucket_index" | "db_quantile_estimate_p99"
16034 | "db_t_digest_centroid" | "db_kll_quantile_step"
16035 | "db_dd_sketch_bin" | "db_reservoir_sample_index"
16036 | "db_chao_estimator_step" | "db_jaccard_minhash_estimate"
16037 | "db_distinct_estimate_lpc" | "db_distinct_estimate_hll"
16038 | "db_throttle_token_step" | "db_leaky_bucket_step"
16039 | "db_token_bucket_step" | "db_circuit_breaker_step"
16040 | "db_two_phase_commit_step" | "db_three_phase_commit_step"
16041 | "db_paxos_propose_id" | "db_raft_term_advance"
16042 | "db_raft_log_match_check" | "db_zab_epoch_step"
16043 | "db_chubby_lease_step" | "db_logical_clock_step"
16044 | "db_lamport_timestamp" | "db_vector_clock_merge"
16045 | "db_hybrid_logical_clock" | "db_crdt_g_counter_merge"
16046 | "db_crdt_pn_counter_merge" | "db_crdt_lww_register_merge"
16047 | "db_crdt_set_or_merge" | "db_consensus_quorum_size"
16048 | "db_replication_lag_step" | "db_partitions_for_n"
16049 | "db_consistent_lookup_id" | "db_chord_finger_index"
16050 | "db_kademlia_xor_distance" | "db_pastry_routing_step"
16051 | "db_dht_replicate_factor" | "db_partition_failure_check"
16052 | "db_byzantine_quorum_size" | "db_pbft_view_change"
16053 | "db_honey_badger_step" | "db_avalanche_query_step"
16054 | "db_quorum_intersection_check" | "db_anti_entropy_step"
16055 | "db_merkle_node_hash" | "db_merkle_path_verify"
16056 | "db_gossip_fanout_step" | "db_anti_entropy_pull_step"
16057 | "db_split_brain_check" | "db_clock_skew_estimate"
16058 | "db_freshness_score" | "db_read_repair_step"
16059 | "db_hinted_handoff_step" | "db_compaction_score"
16060 | "db_levelled_compaction_step" | "db_size_tiered_compaction"
16061 | "db_universal_compaction_step" | "db_write_amplification"
16062 | "db_read_amplification" | "db_space_amplification"
16063 | "db_block_cache_hit_rate" | "db_page_cache_eviction_age"
16064 | "db_wal_fsync_cost" | "db_group_commit_count"
16065 | "db_replica_lag_threshold" | "db_synchronous_commit_check"
16066 | "db_async_commit_check" | "db_eventual_consistency_check"
16067 | "db_strong_consistency_check" | "db_linearizability_check"
16068 | "db_causal_consistency_check"
16069 | "net_tcp_cwnd_step" | "net_tcp_ssthresh_update"
16071 | "net_tcp_reno_step" | "net_tcp_cubic_step"
16072 | "net_tcp_bbr_step" | "net_tcp_vegas_step"
16073 | "net_tcp_westwood_step" | "net_tcp_compound_step"
16074 | "net_tcp_dctcp_step" | "net_tcp_yeah_step"
16075 | "net_tcp_htcp_step" | "net_tcp_hybla_step"
16076 | "net_tcp_illinois_step" | "net_tcp_lp_step"
16077 | "net_tcp_scalable_step" | "net_tcp_veno_step"
16078 | "net_aiad_step" | "net_aimd_step"
16079 | "net_miad_step" | "net_mimd_step"
16080 | "net_aqm_red_drop_prob" | "net_aqm_codel_target"
16081 | "net_aqm_pie_drop_rate" | "net_aqm_fq_codel_step"
16082 | "net_aqm_blue_step" | "net_aqm_choke_step"
16083 | "net_aqm_sfq_step" | "net_aqm_drr_step"
16084 | "net_aqm_wrr_step" | "net_token_rate_limit"
16085 | "net_traffic_shaper_step" | "net_priority_queue_index"
16086 | "net_packet_loss_estimate" | "net_jitter_estimate"
16087 | "net_latency_avg" | "net_rtt_smoothed"
16088 | "net_rtt_variation" | "net_rto_compute"
16089 | "net_bandwidth_delay_product" | "net_path_capacity_kleinrock"
16090 | "net_loss_rate_to_throughput" | "net_throughput_padhye"
16091 | "net_throughput_mathis" | "net_throughput_response"
16092 | "net_router_buffer_size" | "net_drop_tail_check"
16093 | "net_burst_size_compute" | "net_packet_pacing_step"
16094 | "net_link_capacity_share" | "net_proportional_fair_share"
16095 | "net_max_min_fair_step" | "net_alpha_fair_step"
16096 | "net_kelly_pricing_step" | "net_network_utility_max"
16097 | "net_lyapunov_drift_plus_penalty" | "net_backpressure_step"
16098 | "net_max_weight_match" | "net_qcsma_propose"
16099 | "net_csma_back_off" | "net_alohanet_throughput"
16100 | "net_slotted_aloha_throughput" | "net_csma_efficiency"
16101 | "net_token_ring_efficiency" | "net_polling_efficiency"
16102 | "net_radio_path_loss" | "net_friis_received_power"
16103 | "net_two_ray_ground_loss" | "net_okumura_hata_loss"
16104 | "net_log_distance_path" | "net_shadowing_normal"
16105 | "net_rician_k_factor" | "net_rayleigh_envelope"
16106 | "net_doppler_shift" | "net_capacity_shannon"
16107 | "net_mimo_capacity_step" | "net_zero_forcing_beam"
16108 | "net_mmse_beam_step" | "net_water_filling_power"
16109 | "net_amc_threshold_index" | "net_harq_combining_gain"
16110 | "net_turbo_decode_iter" | "net_ldpc_iteration_step"
16111 | "net_polar_decode_step" | "net_viterbi_step"
16112 | "net_bcjr_step" | "net_outage_probability"
16113 | "net_diversity_gain" | "net_array_gain"
16114 | "net_multiplexing_gain" | "net_coding_gain"
16115 | "net_pruning_gain" | "net_macro_diversity_step"
16116 | "net_micro_diversity_step" | "net_handoff_threshold"
16117 | "net_call_admission_check" | "net_blocking_probability"
16118 | "net_erlang_b_formula" | "net_erlang_c_formula"
16119 | "net_engset_formula" | "net_little_law_l"
16120 | "net_throughput_law" | "net_response_time_law"
16121 | "net_utilization_law" | "net_forced_flow_law"
16122 | "os_priority_aging_step" | "os_mlfq_demote_step"
16124 | "os_mlfq_promote_step" | "os_round_robin_quantum"
16125 | "os_completely_fair_vruntime" | "os_lottery_ticket_count"
16126 | "os_stride_pass_step" | "os_eevdf_eligible"
16127 | "os_cfs_load_balance_step" | "os_eas_energy_estimate"
16128 | "os_smt_threading_share" | "os_numa_node_distance"
16129 | "os_cpu_affinity_score" | "os_thread_migration_cost"
16130 | "os_load_average_decay" | "os_runqueue_depth"
16131 | "os_io_scheduler_deadline" | "os_io_scheduler_cfq_step"
16132 | "os_io_scheduler_noop_step" | "os_io_scheduler_bfq_step"
16133 | "os_io_scheduler_kyber_step" | "os_io_scheduler_mq_deadline"
16134 | "os_anticipation_window" | "os_elevator_step"
16135 | "os_disk_seek_time" | "os_disk_rotational_lat"
16136 | "os_disk_transfer_time" | "os_pre_fetch_window"
16137 | "os_buffer_cache_pages" | "os_dirty_page_threshold"
16138 | "os_writeback_step" | "os_swappiness_factor"
16139 | "os_kswapd_wake_threshold" | "os_oom_score_step"
16140 | "os_page_replacement_lru" | "os_page_replacement_clock"
16141 | "os_page_replacement_2q" | "os_working_set_size"
16142 | "os_thrashing_threshold" | "os_demand_paging_step"
16143 | "os_copy_on_write_check" | "os_zero_page_optimization"
16144 | "os_huge_page_threshold" | "os_transparent_hugepage"
16145 | "os_kasan_shadow_offset" | "os_kfence_check"
16146 | "os_kfence_alloc_index" | "os_slub_object_size_round"
16147 | "os_slab_color_offset" | "os_per_cpu_cache_size"
16148 | "os_buddy_order_pick" | "os_compact_memory_step"
16149 | "os_kvm_vmcs_field_offset" | "os_apic_irq_priority"
16150 | "os_msi_x_vector_count" | "os_iommu_domain_step"
16151 | "os_pci_bus_address" | "os_acpi_state_transition"
16152 | "os_cpufreq_governor_step" | "os_intel_pstate_target"
16153 | "os_amd_pstate_target" | "os_thermal_zone_trip"
16154 | "os_throttle_temperature" | "os_battery_capacity_pct"
16155 | "os_powertop_score" | "os_idle_state_select"
16156 | "os_c_state_residency" | "os_p_state_voltage"
16157 | "os_dvfs_step" | "os_voltage_scaling_step"
16158 | "os_frequency_scaling_step" | "os_inotify_event_count"
16159 | "os_epoll_ctl_count" | "os_io_uring_sqe_count"
16160 | "os_io_uring_cqe_count" | "os_kqueue_event_count"
16161 | "os_systemd_journal_size" | "os_dmesg_severity_level"
16162 | "os_audit_event_priority" | "os_apparmor_profile_active"
16163 | "os_selinux_context_match" | "os_smack_label_compare"
16164 | "os_capability_check" | "os_seccomp_filter_step"
16165 | "os_namespace_isolation" | "os_cgroup_v1_count"
16166 | "os_cgroup_v2_count" | "os_pid_max_value"
16167 | "os_thread_max_value" | "os_file_max_value"
16168 | "os_open_files_count" | "os_socket_max_value"
16169 | "os_inotify_max_watches" | "os_oom_kill_score"
16170 | "os_zswap_compress_ratio" | "os_zram_compress_ratio"
16171 | "os_swap_pressure_score" | "os_pressure_stall_step"
16172 | "os_psi_avg10_step" | "os_psi_avg60_step"
16173 | "os_psi_avg300_step" | "os_load_proc_avg"
16174 | "os_load_user_avg" | "os_load_iowait_avg"
16175 | "sec_argon2_memcost" | "sec_argon2_timecost"
16177 | "sec_argon2_parallelism" | "sec_argon2_block_step"
16178 | "sec_pbkdf2_iter" | "sec_scrypt_n_param"
16179 | "sec_scrypt_r_param" | "sec_scrypt_p_param"
16180 | "sec_balloon_hash_step" | "sec_yescrypt_step"
16181 | "sec_bcrypt_cost_factor" | "sec_bcrypt_round_step"
16182 | "sec_password_strength_zxcvbn" | "sec_haveibeenpwned_check"
16183 | "sec_diceware_word_index" | "sec_xkcd_passphrase_score"
16184 | "sec_passphrase_entropy" | "sec_chosen_charset_strength"
16185 | "sec_keystroke_timing_var" | "sec_2fa_totp_window"
16186 | "sec_totp_drift_check" | "sec_hotp_counter_step"
16187 | "sec_yubikey_otp_check" | "sec_webauthn_attestation_check"
16188 | "sec_fido2_assertion_check" | "sec_certificate_chain_depth"
16189 | "sec_revocation_ocsp_check" | "sec_crl_age_seconds"
16190 | "sec_pki_path_validate" | "sec_x509_subject_match"
16191 | "sec_san_match_count" | "sec_basic_constraints_ca"
16192 | "sec_pinning_compare" | "sec_certificate_transparency"
16193 | "sec_dane_tlsa_match" | "sec_hpkp_pin_match"
16194 | "sec_csp_directive_match" | "sec_csrf_token_match"
16195 | "sec_cors_origin_match" | "sec_xss_filter_score"
16196 | "sec_html_escape_check" | "sec_url_safe_encode_check"
16197 | "sec_path_traversal_detect" | "sec_sqli_pattern_score"
16198 | "sec_xxe_pattern_score" | "sec_xxe_dtd_check"
16199 | "sec_command_injection_score" | "sec_idor_check"
16200 | "sec_jwt_alg_safe" | "sec_jwt_kid_match"
16201 | "sec_jwt_signature_verify" | "sec_oauth2_state_validate"
16202 | "sec_oauth2_pkce_step" | "sec_oauth_nonce_check"
16203 | "sec_session_lifetime" | "sec_idle_timeout_step"
16204 | "sec_login_throttle_step" | "sec_account_lockout_step"
16205 | "sec_password_history_check" | "sec_complexity_policy_score"
16206 | "sec_dictionary_attack_check" | "sec_brute_force_attempts"
16207 | "sec_credential_stuffing_score" | "sec_kerberos_ticket_age"
16208 | "sec_kerberos_pac_check" | "sec_kerberos_pre_auth"
16209 | "sec_ldap_bind_step" | "sec_radius_auth_step"
16210 | "sec_diameter_avp_step" | "sec_saml_assertion_age"
16211 | "sec_oidc_id_token_age" | "sec_acme_dns_challenge"
16212 | "sec_dnssec_signature_check" | "sec_spf_pass_check"
16213 | "sec_dkim_signature_check" | "sec_dmarc_policy_check"
16214 | "sec_arc_chain_step" | "sec_smtp_ssl_check"
16215 | "sec_imap_starttls_check" | "sec_pop3_security_step"
16216 | "sec_tls_alert_severity" | "sec_tls13_handshake_step"
16217 | "sec_tls12_handshake_step" | "sec_tls11_deprecation_check"
16218 | "sec_ssl3_disabled_check" | "sec_cipher_suite_strength"
16219 | "sec_cbc_mac_block_count" | "sec_gcm_iv_unique_check"
16220 | "sec_chachapoly_nonce_check" | "sec_x25519_clamping_step"
16221 | "sec_ed25519_signature_step" | "sec_ed448_signature_step"
16222 | "sec_p384_curve_step" | "sec_secp256k1_step"
16223 | "sec_blake3_chunk_step" | "sec_keccak_round_step"
16224 | "sec_sha3_padding_step" | "sec_argon2_state_advance"
16225 | "sec_chacha20_quarterround" | "sec_aes_round_step"
16226 | "sec_aes_keyschedule_step" | "sec_des_round_step"
16227 | "sec_blowfish_round_step" | "sec_serpent_round_step"
16228 | "sec_twofish_round_step"
16229 | "fixed_from_gregorian" | "gregorian_from_fixed"
16231 | "fixed_from_julian" | "julian_from_fixed"
16232 | "iso_week_date" | "hebrew_leap_year"
16233 | "hebrew_year_length" | "fixed_from_hebrew"
16234 | "islamic_leap_year" | "fixed_from_islamic"
16235 | "persian_arithmetic_leap" | "fixed_from_persian"
16236 | "coptic_from_fixed" | "ethiopic_from_fixed"
16237 | "french_revolutionary_leap" | "fixed_from_french"
16238 | "chinese_year_zodiac" | "chinese_lunation_winter"
16239 | "hindu_solar_year" | "hindu_lunisolar_month"
16240 | "maya_long_count_from_fixed" | "mayan_haab_from_fixed"
16241 | "mayan_tzolkin_from_fixed" | "badi_year_from_fixed"
16242 | "bahai_from_fixed" | "easter_gregorian_year"
16243 | "easter_orthodox_year" | "easter_julian_year"
16244 | "day_of_week_zeller" | "iso_day_number"
16245 | "weekday_name_short" | "leap_year_gregorian"
16246
16247 | "dnorm" | "dt" | "df_dist" | "dchisq"
16249 | "glm" | "aov" | "shapiro_wilk" | "anderson_darling"
16250 | "kolmogorov_smirnov" | "spearmanr" | "kendalltau" | "pearsonr"
16251 | "mannwhitneyu" | "wilcoxon" | "kruskal_h"
16252
16253 | "iota_n" | "reduce_axis" | "scan_axis" | "fold_axis"
16255 | "rotate_axis" | "transpose_axis" | "reshape_dim"
16256 | "encode_base" | "decode_base" | "nub_list" | "nub_count"
16257 | "membership_idx" | "deal_n_k" | "roll_n"
16258 | "permute_idx" | "invert_perm"
16259
16260 | "julian_day" | "jd_to_calendar" | "tt_to_tdb"
16262 | "ra_dec_to_alt_az" | "alt_az_to_ra_dec"
16263 | "precession_iau2006" | "nutation_iau2000a"
16264 | "aberration_annual" | "proper_motion_apply"
16265 | "parallax_correction" | "sun_position_low" | "sun_distance_au"
16266 | "moon_position_low" | "moon_phase_age" | "lunation_index"
16267 | "eclipse_magnitude" | "saros_cycle" | "metonic_cycle"
16268 | "orbit_kepler3" | "orbital_period_au" | "orbit_eccentric_anomaly"
16269 | "escape_velocity_body" | "hill_sphere_radius" | "tisserand_param"
16270 | "tle_mean_motion" | "sgp4_propagate_step" | "airy_disk_radius"
16271 | "rayleigh_criterion" | "strehl_ratio" | "au_to_km"
16272
16273 | "elo_expected" | "elo_update" | "glicko_rating"
16275 | "trueskill_update" | "trueskill_match_quality"
16276 | "pythagorean_expectation" | "war_above_replacement"
16277 | "woba_weight" | "wrc_plus" | "ops_plus" | "era_plus"
16278 | "fip" | "xfip" | "siera" | "babip" | "wpa"
16279 | "win_probability" | "leverage_index" | "clutch_score"
16280 | "shooting_pct" | "save_pct" | "corsi_for" | "fenwick_for"
16281 | "goals_above_avg" | "tackle_efficiency" | "yards_per_attempt"
16282 | "qbr_metric" | "epa_per_play"
16283
16284 | "vlookup" | "hlookup" | "xlookup" | "index_match"
16286 | "indirect" | "choose" | "offset"
16287 | "sumif" | "countif" | "averageif"
16288 | "sumifs" | "countifs" | "averageifs"
16289 | "sumproduct" | "rank_eq" | "rank_avg" | "percentrank"
16290 | "quartile_inc" | "quartile_exc"
16291 | "xnpv" | "ppmt" | "ipmt" | "rate"
16292 | "macauley_duration" | "convexity" | "yield_to_maturity"
16293 | "accrued_interest" | "clean_price" | "dirty_price"
16294 | "coupon_count" | "skill_score" | "reliability_diagram"
16295 | "taylor_diagram_score"
16296
16297 | "geohash_neighbors" | "h3_index" | "h3_geo_to_h3"
16299 | "h3_h3_to_geo" | "h3_k_ring" | "h3_neighbor" | "h3_resolution"
16300 | "s2_cell_id" | "s2_cell_at_lat_lng" | "s2_cell_neighbors"
16301 | "utm_from_lat_lng" | "utm_to_lat_lng"
16302 | "mgrs_encode" | "mgrs_decode"
16303 | "lat_lng_to_xy_mercator" | "lat_lng_to_xy_lambert"
16304 | "haversine_dist" | "vincenty_dist" | "andoyer_dist"
16305 | "rhumb_line_bearing"
16306 | "destination_point" | "tile_xyz_to_lat_lng" | "lat_lng_to_tile_xyz"
16307 | "polygon_winding_order" | "point_in_polygon_ray"
16308 | "point_in_polygon_winding" | "segment_intersection"
16309 | "segment_distance_point" | "convex_hull_chan"
16310
16311 | "pid_anti_windup" | "pid_ziegler_nichols"
16313 | "smith_predictor_step" | "lqr_gain_continuous"
16314 | "lqr_gain_discrete" | "lqg_step" | "h_infinity_norm"
16315 | "bode_gain_margin" | "bode_phase_margin"
16316 | "nyquist_encirclement" | "nichols_chart_step"
16317 | "servo_position_velocity" | "servo_torque_step"
16318 | "imu_madgwick_step" | "imu_mahony_step" | "quaternion_from_imu"
16319 | "denavit_hartenberg_h" | "forward_kinematics_dh"
16320 | "inverse_kinematics_2link" | "jacobian_2dof"
16321 | "manipulability_yoshikawa" | "singularity_check_2link"
16322 | "path_dubins_lsl" | "path_dubins_rsr" | "path_reeds_shepp"
16323 | "rrt_extend" | "rrt_star_rewire" | "prm_node_connect"
16324
16325 | "life_expectancy_e0" | "force_of_mortality" | "select_ultimate"
16327 | "annuity_due_an" | "annuity_immediate_an"
16328 | "term_life_a_n_t" | "whole_life_a"
16329 | "endowment_pure_e" | "endowment_combined_a"
16330 | "premium_net" | "level_premium"
16331 | "reserve_prospective" | "reserve_retrospective"
16332 | "gross_premium_load" | "experience_factor"
16333 | "mortality_table_q" | "select_period_step"
16334 | "multi_decrement_q" | "multi_state_pij"
16335 | "credibility_buhlmann" | "loss_severity_lognormal"
16336 | "loss_frequency_poisson" | "ruin_probability_lundberg"
16337 | "cramer_lundberg_step" | "bornhuetter_ferguson"
16338 | "chain_ladder_step" | "ibnr_estimate" | "run_off_triangle_step"
16339
16340 | "r_naught_basic" | "r_effective_t" | "doubling_time_growth"
16342 | "sirs_step" | "seirs_step" | "susceptible_to_infected"
16343 | "attack_rate" | "vaccination_coverage_required"
16344 | "cfr_case_fatality" | "ifr_infection_fatality"
16345 | "dalys_disability_weight" | "qaly_lifetime" | "ylll_pml"
16346 | "rt_serial_interval" | "generation_time_step"
16347 | "gini_inequality_health" | "standardized_mortality_smr"
16348 | "indirect_age_adjusted" | "direct_age_adjusted"
16349 | "odds_ratio_2x2" | "risk_ratio_2x2" | "number_needed_to_treat"
16350 | "attributable_fraction_pop" | "preventive_fraction"
16351 | "contact_tracing_eff" | "cluster_attack_rate"
16352 | "transmission_pair_index"
16353
16354 | "tar_header_checksum" | "tar_pad_512" | "tar_member_record"
16356 | "zip_local_header" | "zip_central_dir" | "zip_eocd"
16357 | "gzip_member_step" | "gzip_crc32_init" | "gzip_isize"
16358 | "deflate_dynamic_huffman" | "deflate_static_block"
16359 | "lz4_block_step" | "lz4_match_offset"
16360 | "zstd_frame_header" | "brotli_huffman_table"
16361 | "brotli_meta_block" | "lzma_range_step"
16362 | "quoted_printable_encode" | "uuencode_step"
16363 | "modhex_encode" | "percent_encode_full"
16364 | "punycode_encode" | "idn_to_ascii" | "idn_to_unicode"
16365 | "msgpack_pack_int" | "msgpack_pack_str"
16366 | "cbor_encode_uint" | "cbor_encode_str"
16367
16368 | "molecular_weight_compound" | "molarity_dilution"
16370 | "gas_constant_value" | "eyring_rate" | "van_t_hoff_kp"
16371 | "henderson_buffer" | "titration_ph_endpoint"
16372 | "isoelectric_point_protein" | "ka_to_pka" | "pkb_to_kb"
16373 | "amphoteric_check" | "oxidation_number"
16374 | "half_reaction_balance" | "redox_potential_cell"
16375 | "electrolysis_mass" | "spectrophotometer_beer_lambert"
16376 | "epsilon_extinction" | "transmittance_to_a"
16377 | "crystal_field_ligand" | "jahn_teller_check"
16378 | "vsepr_geometry" | "lewis_dot_count"
16379 | "formal_charge" | "resonance_count"
16380 | "ramachandran_phi_psi" | "rg_radius_of_gyration"
16381 | "spectroscopic_factor" | "avogadro_constant"
16382
16383 | "cents_between_freqs" | "note_name_from_midi"
16385 | "interval_quality_size" | "scale_pitches_major"
16386 | "scale_pitches_minor" | "mode_pitches_dorian"
16387 | "mode_pitches_phrygian" | "mode_pitches_lydian"
16388 | "chord_root_inversion" | "chord_quality_classify"
16389 | "chord_voicing_close" | "key_signature_sharps"
16390 | "key_signature_flats" | "tempo_to_ms" | "beat_to_seconds"
16391 | "time_sig_subdivision" | "equal_tempered_freq"
16392 | "just_intonation_freq" | "pythagorean_freq"
16393 | "mean_tone_freq" | "werckmeister_iii" | "kirnberger_iii"
16394 | "dynamics_db_level" | "harmonics_partial"
16395
16396 | "moment_magnitude_mw" | "richter_local_ml"
16398 | "surface_wave_ms" | "body_wave_mb"
16399 | "gutenberg_richter_b" | "omori_aftershock"
16400 | "pga_attenuation" | "arias_intensity" | "shake_map_pga"
16401 | "liquefaction_potential_index" | "spt_n_correction"
16402 | "mineral_mohs_hardness" | "streak_color_index"
16403 | "specific_gravity_water" | "feldspar_classify"
16404 | "silicate_classify" | "igneous_qapf"
16405 | "metamorphic_grade" | "crustal_density_depth"
16406 | "pwave_velocity_depth" | "swave_velocity_depth"
16407 | "gradient_geothermal" | "heat_flow_radiogenic"
16408
16409 | "dgemm" | "sgemm" | "zgemm" | "cgemm"
16411 | "dgemv" | "sgemv" | "dtrsm" | "strsm"
16412 | "dgesv" | "dgetrf" | "dgeqrf" | "dgesvd"
16413 | "dsyevd" | "dpotrf" | "daxpy" | "ddot"
16414 | "dnrm2" | "dscal" | "dasum" | "idamax"
16415 | "dsyrk" | "dgerqf" | "dorgqr" | "dorglq"
16416 | "drot" | "drotg" | "dpbsv" | "dgbsv"
16417 | "dtbsv" | "dtrsv" | "ddrot" | "dgemm3m"
16418 | "dgels" | "dgelsd"
16419
16420 | "cnf_unit_propagate" | "cnf_pure_literal_elim"
16422 | "cnf_dpll_branch" | "dpll_clause_learning"
16423 | "two_watched_literals" | "walksat_step"
16424 | "resolution_step" | "subsumption_check"
16425 | "tableau_branch_close" | "sequent_left_intro"
16426 | "sequent_right_intro" | "nbe_normalize"
16427 | "church_numeral_n" | "encode_pair" | "encode_succ"
16428 | "simply_typed_check" | "hindley_milner_step"
16429 | "unification_robinson" | "bdd_apply" | "bdd_restrict"
16430 | "bdd_quantify" | "aig_simplify_step"
16431 | "smt_qf_lia_solve_step" | "smt_qf_uf_combine"
16432 | "model_checking_ctl" | "model_checking_ltl"
16433 | "bisimulation_step" | "coq_tactic_apply"
16434 | "coq_unify_term" | "refl_check" | "sym_check" | "trans_check"
16435
16436 | "nfa_to_dfa" | "subset_construction"
16438 | "dfa_minimize_hopcroft" | "regex_to_nfa_thompson"
16439 | "glushkov_construction" | "brzozowski_derivative"
16440 | "ll1_first_set" | "ll1_follow_set" | "ll1_predict_table"
16441 | "lr0_items_step" | "lalr_lookahead_compute"
16442 | "lr1_canonical_collection"
16443 | "earley_scan" | "earley_predict" | "earley_complete"
16444 | "packrat_parse_step" | "ascent_parser_step"
16445 | "pratt_parse_step" | "shunting_yard_step"
16446 | "regex_compile_thompson" | "regex_match_dfa"
16447 | "lex_keyword_classify"
16448 | "peg_seq" | "peg_choice" | "peg_repeat" | "peg_lookahead"
16449 | "dfa_simulate_step" | "bytecode_disasm_step"
16450 | "ssa_phi_insert" | "dom_tree_idom" | "dominance_frontier"
16451
16452 | "porter_stem_step" | "snowball_stem_english"
16454 | "snowball_stem_french" | "lemmatize_wordnet"
16455 | "lemmatize_lemmy" | "stem_lancaster"
16456 | "soundex_phonetic" | "metaphone_phonetic"
16457 | "caverphone_2" | "nysiis_phonetic"
16458 | "match_rating_codex" | "daitch_mokotoff"
16459 | "viterbi_pos_tag" | "forward_backward_pos"
16460 | "crf_log_likelihood" | "bigram_perplexity"
16461 | "trigram_perplexity" | "ner_bilou_decode"
16462 | "constituency_cyk" | "dependency_parse_eisner"
16463 | "transition_arc_eager" | "transition_arc_standard"
16464 | "word_alignment_ibm1" | "word_alignment_ibm2"
16465 | "lexicalized_parse" | "coreference_singleton"
16466 | "anaphora_distance" | "head_finding_collins"
16467 | "tree_kernel_collins"
16468
16469 | "btrim" | "translate" | "ascii"
16471 | "regexp_split" | "regexp_matches" | "regexp_replace"
16472 | "json_build_object" | "jsonb_set"
16473 | "json_array_length" | "json_extract_path"
16474 | "json_strip_nulls" | "jsonb_pretty"
16475 | "jsonb_path_query" | "json_each"
16476 | "jsonb_array_length" | "jsonb_object_keys"
16477 | "jsonb_typeof" | "array_to_jsonb"
16478 | "ts_match" | "ts_rank" | "ts_headline"
16479 | "substring_similarity" | "levenshtein_dist"
16480 | "word_similarity" | "strict_word_similarity"
16481 | "hstore_to_array" | "array_to_hstore"
16482 | "string_agg" | "array_agg"
16483 | "corr_agg" | "covar_pop" | "covar_samp"
16484 | "regr_slope" | "regr_intercept" | "regr_r2"
16485 | "percentile_cont" | "percentile_disc" | "mode_agg"
16486 | "array_to_string" | "array_position" | "array_positions"
16487 | "array_remove" | "array_replace"
16488 | "xmlforest" | "xmlagg"
16489
16490 | "zadd" | "zrem" | "zrangebyscore"
16492 | "zrank" | "zrevrank" | "zincrby"
16493 | "zcard" | "zcount" | "zlexcount"
16494 | "lpush" | "rpush" | "lrange" | "lrem"
16495 | "hset" | "hget" | "hgetall" | "hlen"
16496 | "hkeys" | "hvals" | "hmset" | "hincrby"
16497 | "sadd" | "srem" | "smembers"
16498 | "sinter" | "sunion" | "sdiff"
16499 | "scard" | "sismember" | "spop"
16500 | "setex" | "setnx" | "expire"
16501 | "ttl" | "pttl" | "persist"
16502 | "incr" | "decr" | "incrby" | "decrby"
16503 | "getset" | "mset" | "mget" | "renamenx"
16504 | "dbsize" | "type_redis" | "exists_key"
16505 | "strlen" | "getrange" | "setrange" | "append_redis"
16506 | "bitcount" | "bitop" | "bitpos"
16507 | "pfadd" | "pfcount"
16508 | "geoadd" | "geodist" | "geohash"
16509 | "xadd" | "xlen" | "xrange"
16510 | "object_encoding" | "debug_object" | "cluster_slots"
16511
16512 | "argpartition" | "bincount" | "nonzero_count"
16514 | "flatnonzero" | "searchsorted" | "digitize"
16515 | "histogram_bin_edges" | "unique_count"
16516 | "polyfit_rmse"
16517 | "ellipk" | "ellipe"
16518 | "hyp1f1" | "hyp2f1" | "mathieu_b"
16519 | "spherical_jn" | "spherical_yn"
16520 | "jv" | "yn" | "iv" | "kv"
16521 | "airyai" | "airybi"
16522 | "polygamma" | "trigamma" | "loggamma"
16523 | "factorial2" | "factorialk"
16524 | "owens_t" | "marcum_q" | "voigt_profile"
16525 | "chebyt" | "chebyu" | "sph_harm"
16526 | "wofz" | "erfcx" | "erfi" | "dawsn"
16527 | "interp1d"
16528 | "convolve_full" | "convolve_valid" | "correlate_full"
16529 | "kron_product"
16530 | "simpson_rule" | "romberg_quad" | "fixed_quad"
16531 | "ode45_step" | "ode_lsoda" | "solve_ivp_step"
16532 | "root_brentq" | "root_newton" | "root_secant"
16533 | "fmin_powell" | "fmin_cobyla"
16534
16535 | "cobb_douglas" | "ces_production"
16537 | "leontief_input" | "leontief_output"
16538 | "slutsky_decompose"
16539 | "marshallian_demand" | "hicksian_demand"
16540 | "expenditure_function" | "indirect_utility"
16541 | "gale_shapley_step" | "deferred_acceptance"
16542 | "top_trading_cycle" | "vcg_payment" | "myerson_optimal"
16543 | "gini_market" | "hhi_concentration"
16544 | "cournot_eq" | "stackelberg_eq" | "bertrand_eq"
16545 | "monopoly_lerner"
16546 | "consumer_surplus" | "producer_surplus"
16547 | "deadweight_loss" | "tax_incidence"
16548 | "pareto_efficiency" | "edgeworth_box_alloc"
16549 | "social_welfare_utilitarian"
16550 | "social_welfare_rawls" | "social_welfare_nash"
16551 | "arrow_independence"
16552 | "vickrey_auction" | "first_price_seal"
16553 | "english_auction" | "dutch_auction"
16554 | "core_coalition" | "stable_matching_count"
16555 | "gale_optimal" | "pareto_dominance"
16556 | "lerner_index"
16557 | "price_elasticity" | "supply_elasticity"
16558 | "income_elasticity" | "engel_curve" | "cross_elasticity"
16559 | "diff_in_diff" | "did_estimator" | "rdd_estimate"
16560 | "hann_w" | "hamming_w" | "blackman_w" | "barthann_w"
16562 | "nuttall_w" | "flattop_w" | "parzen_window" | "tukey_w"
16563 | "taylor_window" | "dpss_window" | "kaiserord_step"
16564 | "butter_lp_re" | "butter_hp_mag"
16565 | "cheby1_lp" | "cheby2_lp" | "ellip_lp" | "bessel_lp"
16566 | "notch_filter"
16567 | "sosfilt_step" | "lfilter_zi_init" | "filtfilt_pad"
16568 | "freqz_eval" | "freqs_eval" | "group_delay_eval"
16569 | "impulse_response_n"
16570 | "tf2zpk_step" | "zpk2tf_step" | "tf2sos_step"
16571 | "zpk2sos_step" | "sos2tf_step"
16572 | "bilinear_xform" | "bilinear_zpk_xform"
16573 | "firwin_lowpass" | "firwin_highpass"
16574 | "firwin_bandpass" | "firwin_bandstop"
16575 | "firwin2_freq" | "remez_design"
16576 | "stft_step" | "istft_step"
16577 | "cwt_morlet" | "ricker_wavelet" | "mexican_hat_wavelet"
16578 | "coherence_xy" | "csd_xy" | "welch_psd_avg"
16579 | "periodogram_basic" | "lombscargle_freq"
16580 | "hilbert_signal" | "envelope_amplitude"
16581 | "deconvolve_step" | "fftconvolve_step" | "oaconvolve_step"
16582 | "upfirdn_step" | "resample_poly_step" | "decimate_step"
16583 | "savgol_coef" | "detrend_linear"
16584 | "wiener_filter" | "medfilt_1d" | "peak_widths_at"
16585 | "dijkstra_relax" | "bellman_ford_relax"
16587 | "floyd_warshall_step" | "johnson_reweight"
16588 | "astar_search" | "bidirectional_dijkstra"
16589 | "yen_k_shortest" | "ida_star"
16590 | "bfs_count" | "dfs_postorder_done" | "topo_kahn_step"
16591 | "tarjan_scc_step" | "kosaraju_step"
16592 | "kruskal_step" | "prim_step" | "boruvka_step"
16593 | "reverse_delete_step"
16594 | "ford_fulkerson_step" | "edmonds_karp_bfs"
16595 | "dinic_step" | "push_relabel_relabel"
16596 | "stoer_wagner_step" | "karger_step"
16597 | "pagerank_iter" | "hits_authority" | "hits_hub"
16598 | "personalized_pagerank"
16599 | "centrality_degree" | "centrality_closeness"
16600 | "centrality_betweenness" | "centrality_eigenvector"
16601 | "centrality_katz" | "harmonic_centrality" | "load_centrality"
16602 | "clustering_coefficient" | "triangles_count" | "transitivity"
16603 | "modularity_score" | "louvain_gain"
16604 | "label_propagation" | "girvan_newman"
16605 | "articulation_point" | "bridge_edge"
16606 | "edge_connectivity" | "vertex_connectivity"
16607 | "biconnected_components"
16608 | "gx_diameter" | "gx_radius" | "gx_eccentricity"
16609 | "warshall_step"
16610 | "tsp_held_karp" | "tsp_nn_step" | "tsp_christofides"
16611 | "graph_coloring_greedy" | "welsh_powell"
16612 | "vf2_consistent" | "subgraph_isomorphism"
16613 | "hungarian_step" | "hopcroft_karp_step"
16614 | "bron_kerbosch"
16615 | "min_vertex_cover" | "max_independent_set"
16616 | "dominating_set_greedy" | "hamiltonian_path"
16617 | "min_steiner_tree" | "k_shortest_spanning"
16618 | "random_walk_hitting" | "simrank"
16619 | "df_groupby" | "df_aggregate" | "df_apply"
16621 | "df_transform" | "df_pivot" | "df_pivot_table"
16622 | "df_melt" | "df_stack" | "df_unstack"
16623 | "df_explode" | "df_get_dummies" | "df_crosstab"
16624 | "df_merge" | "df_join" | "df_concat"
16625 | "df_resample" | "df_rolling" | "df_expanding"
16626 | "df_ewm" | "df_shift" | "df_diff"
16627 | "df_pct_change" | "df_corr" | "df_cov"
16628 | "df_corrwith" | "df_describe" | "df_kurtosis"
16629 | "df_skew" | "df_sem" | "df_mad"
16630 | "df_dropna" | "df_fillna" | "df_interpolate"
16631 | "df_replace" | "df_isnull" | "df_notnull"
16632 | "df_sort_values" | "df_rank" | "df_quantile"
16633 | "df_value_counts" | "df_sample" | "df_nlargest"
16634 | "df_nsmallest" | "df_idxmax" | "df_idxmin"
16635 | "df_clip" | "df_round" | "df_to_datetime"
16636 | "df_to_timedelta" | "df_to_numeric" | "df_eval"
16637 | "df_query" | "df_filter" | "df_drop_duplicates"
16638 | "df_duplicated" | "df_set_index" | "df_reset_index"
16639 | "image_resize" | "image_grayscale" | "image_threshold"
16641 | "image_blur_gaussian" | "image_blur_box" | "image_sharpen"
16642 | "image_edge_canny" | "image_edge_sobel" | "image_edge_laplacian"
16643 | "image_dilate" | "image_erode" | "image_morphology_open"
16644 | "image_morphology_close" | "image_histogram" | "image_equalize"
16645 | "image_clahe" | "image_contrast" | "image_brightness"
16646 | "image_gamma" | "image_invert" | "image_sepia"
16647 | "image_posterize" | "image_solarize" | "convolve_2d"
16648 | "filter_median" | "filter_bilateral" | "filter_nlmeans"
16649 | "gabor_filter" | "hog_features" | "harris_corners"
16650 | "shi_tomasi_corners" | "sift_keypoints" | "orb_keypoints"
16651 | "surf_keypoints" | "template_match" | "face_detect_haar"
16652 | "watershed_segment" | "slic_superpixels" | "felzenszwalb_segment"
16653 | "graph_cut_segment" | "hough_lines" | "hough_circles"
16654 | "ransac_homography" | "optical_flow_lk" | "optical_flow_farneback"
16655 | "corner_subpix" | "image_rotate" | "image_flip_h"
16656 | "image_flip_v" | "image_emboss" | "image_motion_blur"
16657 | "arima_fit" | "arima_forecast" | "arma_order_select"
16659 | "sarimax_fit" | "garch_fit" | "ewma_smooth"
16660 | "holt_winters_additive" | "holt_winters_multiplicative" | "kalman_filter_step"
16661 | "kalman_smoother_step" | "var_fit" | "vecm_fit"
16662 | "johansen_test" | "phillips_perron" | "adfuller"
16663 | "kpss_test" | "breusch_godfrey" | "ljung_box_q"
16664 | "durbin_watson_d" | "granger_causality" | "cointegration_eg"
16665 | "seasonal_decompose" | "stl_decompose" | "acf_basis"
16666 | "pacf_basis" | "moving_average_filter" | "exp_smooth_simple"
16667 | "exp_smooth_double" | "markov_switching_ar" | "markov_switching_mr"
16668 | "arch_lm" | "state_space_kalman" | "ucm_unobserved_components"
16669 | "spectral_density_estimate" | "bayesian_step" | "pivoted_cholesky_var"
16670 | "sk_logistic_predict" | "sk_logistic_fit" | "sk_random_forest_fit"
16672 | "sk_gbt_fit" | "sk_xgb_fit" | "sk_lightgbm_fit"
16673 | "sk_svm_fit" | "sk_kmeans_fit" | "sk_dbscan_fit"
16674 | "sk_agglomerative_fit" | "sk_pca_fit" | "sk_tsne_fit"
16675 | "sk_umap_fit" | "sk_isolation_forest_fit" | "sk_lof_fit"
16676 | "sk_kfold_split" | "sk_stratified_kfold" | "sk_cross_val_score"
16677 | "sk_grid_search" | "sk_random_search" | "sk_bayes_search"
16678 | "sk_pipeline_fit" | "sk_standard_scaler" | "sk_min_max_scaler"
16679 | "sk_robust_scaler" | "sk_quantile_transform" | "sk_power_transform"
16680 | "sk_one_hot" | "sk_ordinal_encode" | "sk_label_encode"
16681 | "sk_tfidf" | "sk_count_vectorize" | "sk_silhouette"
16682 | "sk_calinski_harabasz" | "sk_davies_bouldin" | "sk_adjusted_rand"
16683 | "sk_mutual_info" | "sk_lda_topic" | "sk_nmf_topic"
16684 | "sk_word2vec_train" | "sk_doc2vec_train" | "sk_naive_bayes_predict"
16685 | "sk_knn_predict" | "sk_decision_tree_split"
16686 | "qubit_x" | "qubit_y" | "qubit_z"
16688 | "qubit_h" | "qubit_s" | "qubit_t"
16689 | "qubit_rx" | "qubit_ry" | "qubit_rz"
16690 | "qubit_u3" | "qubit_u2" | "qubit_u1"
16691 | "qubit_phase" | "qubit_cnot" | "qubit_cz"
16692 | "qubit_swap" | "qubit_ccx" | "qubit_measure"
16693 | "qubit_reset" | "bell_state" | "ghz_state"
16694 | "w_state" | "qft" | "inverse_qft"
16695 | "grover_iter" | "shor_period" | "vqe_step"
16696 | "qaoa_step" | "qpe_iteration" | "pauli_string_expect"
16697 | "circuit_depth" | "circuit_width" | "gate_decompose"
16698 | "ancilla_alloc" | "bloch_sphere_x" | "bloch_sphere_z"
16699 | "density_matrix_purity_q" | "entanglement_entropy" | "quantum_teleportation"
16700 | "superdense_coding" | "noise_model_depolarize"
16701 | "mirr_excel" | "accrint" | "cumipmt"
16703 | "cumprinc" | "dollarde" | "dollarfr"
16704 | "received" | "yieldmat" | "yielddisc"
16705 | "duration_macaulay" | "mduration" | "odddyield"
16706 | "disc_excel" | "effect" | "nominal"
16707 | "intrate" | "price_disc" | "cityhash64"
16708 | "farmhash_64" | "metro_hash_64" | "spookyhash_128"
16709 | "t1ha" | "highway_hash" | "fnv0_32"
16710 | "lose_lose"
16711 | "oat_hash" | "lz4_encode_block" | "snappy_encode"
16712 | "zstd_encode_step" | "brotli_encode_meta" | "lzma_encode_step"
16713 | "bz2_encode_step" | "lzo_encode_step" | "deflate_encode_huffman"
16714 | "lzw_encode" | "gzip_encode_step" | "uri_template_expand"
16715 | "uri_resolve" | "uri_normalize" | "percent_decode_url"
16716 | "url_encode_form" | "url_decode_form" | "punycode_decode_step"
16717 | "idn_normalize" | "url_origin" | "etag_validate"
16718 | "cache_control_parse" | "vary_match" | "content_negotiate"
16719 | "accept_lang_pick" | "range_header_parse" | "if_match_check"
16720 | "if_none_match_check" | "digest_auth_quote" | "www_auth_parse"
16721 | "iso8601_duration_parse" | "iso8601_duration_to_seconds" | "rrule_next_occurrence"
16723 | "cron_next_fire" | "date_round_iso" | "week_number_iso"
16724 | "fiscal_year_us" | "age_at_date" | "easter_western"
16725 | "easter_orthodox_year_2" | "chinese_new_year" | "solstice_winter"
16726 | "equinox_spring" | "rgb_to_oklab" | "oklab_to_rgb"
16727 | "rgb_to_cmyk" | "cmyk_to_rgb" | "rgb_to_xyz"
16728 | "xyz_to_rgb" | "rgb_to_yuv" | "yuv_to_rgb"
16729 | "luminance_relative" | "contrast_ratio" | "wcag_pass"
16730 | "color_temperature_kelvin" | "delta_e76" | "delta_e94"
16731 | "delta_e2000" | "color_blend_alpha" | "isbn10_check"
16732 | "isbn13_check" | "ean13_check" | "upc_check"
16733 | "eth_addr_check" | "btc_addr_check" | "ssn_check"
16734 | "vin_check" | "imei_check" | "iban_check"
16735 | "cusip_check" | "kde_silverman_bw" | "kde_scott_bw"
16736 | "kde_bandwidth_lscv" | "kde_epanechnikov" | "kde_gaussian_2d"
16737 | "kde_uniform" | "kde_triangular" | "kde_biweight"
16738 | "kde_triweight" | "kde_cosine" | "kde_logistic_kernel"
16739 | "mod_exp" | "modexp" | "powmod"
16741 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
16742 | "miller_rabin" | "millerrabin" | "is_probable_prime"
16743 | "derangements" | "stirling2" | "stirling_second"
16745 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
16746 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
16748 | "bs_delta" | "bsdelta" | "option_delta"
16750 | "bs_gamma" | "bsgamma" | "option_gamma"
16751 | "bs_vega" | "bsvega" | "option_vega"
16752 | "bs_theta" | "bstheta" | "option_theta"
16753 | "bs_rho" | "bsrho" | "option_rho"
16754 | "bond_duration" | "mac_duration"
16755 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
16757 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
16759 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
16760 | "pnorm" | "qnorm" | "pbinom" | "dbinom" | "ppois"
16762 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
16763 | "rbind" | "cbind"
16765 | "row_sums" | "rowSums" | "col_sums" | "colSums"
16766 | "row_means" | "rowMeans" | "col_means" | "colMeans"
16767 | "outer" | "crossprod" | "tcrossprod"
16768 | "nrow" | "ncol" | "prop_table" | "proptable"
16769 | "cummax" | "cummin" | "scale_vec" | "scale"
16771 | "which_fn" | "tabulate"
16772 | "duplicated" | "duped" | "rev_vec"
16773 | "seq_fn" | "rep_fn" | "rep"
16774 | "cut_bins" | "cut" | "find_interval" | "findInterval"
16775 | "ecdf_fn" | "ecdf" | "density_est" | "density"
16776 | "embed_ts" | "embed"
16777 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
16779 | "wilcox_test" | "wilcox" | "mann_whitney"
16780 | "prop_test" | "proptest" | "binom_test" | "binomtest"
16781 | "sapply" | "tapply" | "do_call" | "docall"
16783 | "kmeans" | "prcomp" | "pca"
16785 | "rnorm" | "runif" | "rexp" | "rbinom" | "rpois" | "rgeom"
16787 | "rgamma" | "rbeta" | "rchisq" | "rt" | "rf"
16788 | "rweibull" | "rlnorm" | "rcauchy"
16789 | "qunif" | "qexp" | "qweibull" | "qlnorm" | "qcauchy"
16791 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
16793 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
16795 | "lowess" | "loess" | "approx_fn" | "approx"
16797 | "lm_fit" | "lm"
16799 | "qgamma" | "qbeta" | "qchisq" | "qt_fn" | "qt" | "qf_fn" | "qf"
16801 | "qbinom" | "qpois"
16802 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
16804 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
16805 | "predict_lm" | "predict" | "confint_lm" | "confint"
16807 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
16809 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
16810 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
16811 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
16813 | "plot_svg" | "hist_svg" | "histogram_svg"
16814 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
16815 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
16816 | "donut_svg" | "donut" | "area_svg" | "area_chart"
16817 | "hbar_svg" | "hbar" | "radar_svg" | "radar" | "spider"
16818 | "candlestick_svg" | "candlestick" | "ohlc"
16819 | "violin_svg" | "violin" | "cor_heatmap" | "cor_matrix_svg"
16820 | "stacked_bar_svg" | "stacked_bar"
16821 | "wordcloud_svg" | "wordcloud" | "wcloud"
16822 | "treemap_svg" | "treemap"
16823 | "pvw"
16824 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
16826 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
16827 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
16828 | "ai" | "ai_agent" | "prompt" | "stream_prompt" | "stream_prompt_cb"
16830 | "tokens_of"
16831 | "ai_estimate" | "ai_cost" | "ai_history" | "ai_history_clear"
16832 | "ai_cache_clear" | "ai_cache_size"
16833 | "ai_mock_install" | "ai_mock_clear"
16834 | "ai_config_get" | "ai_config_set" | "ai_routing_get" | "ai_routing_set"
16835 | "ai_register_tool" | "ai_unregister_tool" | "ai_clear_tools" | "ai_tools_list"
16836 | "ai_filter" | "ai_map" | "ai_classify" | "ai_match" | "ai_sort" | "ai_dedupe"
16837 | "ai_extract" | "ai_summarize" | "ai_translate" | "ai_template"
16838 | "ai_session_new" | "ai_session_send" | "ai_session_history"
16839 | "ai_session_close" | "ai_session_reset"
16840 | "ai_session_export" | "ai_session_import"
16841 | "ai_memory_save" | "ai_memory_recall" | "ai_memory_forget"
16842 | "ai_memory_count" | "ai_memory_clear"
16843 | "ai_vision" | "ai_pdf" | "ai_grounded" | "ai_citations"
16844 | "ai_transcribe" | "ai_speak" | "ai_image" | "ai_image_edit" | "ai_image_variation"
16845 | "ai_models" | "ai_describe" | "ai_pricing" | "ai_dashboard"
16846 | "ai_moderate" | "ai_chunk" | "ai_warm" | "ai_compare"
16847 | "ai_last_thinking" | "ai_budget" | "ai_batch" | "ai_pmap"
16848 | "ai_file_upload" | "ai_file_list" | "ai_file_get" | "ai_file_delete"
16849 | "ai_file_anthropic_upload" | "ai_file_anthropic_list" | "ai_file_anthropic_delete"
16850 | "vec_cosine" | "vec_search" | "vec_topk"
16851 | "web_search_tool" | "fetch_url_tool" | "read_file_tool" | "run_code_tool"
16853 | "mcp_connect" | "mcp_close" | "mcp_tools" | "mcp_call"
16855 | "mcp_resource" | "mcp_resources" | "mcp_prompt" | "mcp_prompts"
16856 | "mcp_attach_to_ai" | "mcp_detach_from_ai" | "mcp_attached"
16857 | "mcp_server_start" | "mcp_serve_registered_tools"
16858 | "pty_spawn" | "pty_send" | "pty_read" | "pty_expect" | "pty_expect_table"
16860 | "pty_buffer" | "pty_alive" | "pty_eof" | "pty_close" | "pty_interact"
16861 | "pty_strip_ansi" | "pty_after_eof" | "pty_pending_events"
16862 | "stress_fp" | "stress_int" | "stress_cache" | "stress_branch"
16864 | "stress_sort" | "stress_alloc" | "stress_mmap" | "stress_disk"
16865 | "stress_iops" | "stress_net" | "stress_http" | "stress_dns"
16866 | "stress_fork" | "stress_thread" | "stress_aes" | "stress_compress"
16867 | "stress_regex" | "stress_json" | "stress_burst" | "stress_ramp"
16868 | "stress_oscillate" | "stress_all" | "stress_temp" | "stress_thermal_zones"
16869 | "stress_freq" | "stress_throttled" | "stress_load" | "stress_meminfo"
16870 | "stress_cores" | "stress_arm_kill_switch" | "stress_killed"
16871 | "stress_disarm_kill_switch"
16872 | "stress_metrics_record" | "stress_metrics_clear" | "stress_metrics_count"
16873 | "stress_metrics_export" | "stress_metrics_prometheus"
16874 | "stress_metrics_json" | "stress_metrics_csv" | "stress_metrics_watch"
16875 | "audit_log" | "audit_log_path"
16877 | "secrets_encrypt" | "secrets_decrypt" | "secrets_random_key" | "secrets_kdf"
16878 | "web_route" | "web_resources" | "web_root" | "web_routes_table"
16880 | "web_application_config" | "web_boot_application"
16881 | "web_render" | "web_render_partial" | "web_redirect"
16882 | "web_json" | "web_text" | "web_csv" | "web_markdown"
16883 | "web_params" | "web_request" | "web_set_header" | "web_status"
16884 | "web_before_action" | "web_after_action"
16885 | "web_session" | "web_session_set" | "web_session_get" | "web_session_clear"
16886 | "web_signed" | "web_unsigned"
16887 | "web_cookies" | "web_set_cookie"
16888 | "web_flash" | "web_flash_set" | "web_flash_get"
16889 | "web_validate" | "web_permit"
16890 | "web_password_hash" | "web_password_verify"
16891 | "web_token_for" | "web_token_consume" | "web_csrf_meta_tag"
16892 | "web_security_headers" | "web_can"
16893 | "web_h" | "web_truncate" | "web_pluralize" | "web_time_ago_in_words"
16894 | "web_image_tag" | "web_link_to" | "web_button_to"
16895 | "web_form_with" | "web_form_close"
16896 | "web_text_field" | "web_text_area" | "web_check_box"
16897 | "web_stylesheet_link_tag" | "web_javascript_link_tag"
16898 | "web_yield_content" | "web_content_for"
16899 | "web_etag" | "web_cache_get" | "web_cache_set"
16900 | "web_cache_delete" | "web_cache_clear"
16901 | "web_db_connect" | "web_db_execute" | "web_db_query"
16902 | "web_db_begin" | "web_db_commit" | "web_db_rollback"
16903 | "web_create_table" | "web_drop_table"
16904 | "web_add_column" | "web_remove_column"
16905 | "web_migrate" | "web_rollback"
16906 | "web_model_all" | "web_model_find" | "web_model_first" | "web_model_last"
16907 | "web_model_where" | "web_model_create" | "web_model_update"
16908 | "web_model_destroy" | "web_model_count" | "web_model_increment"
16909 | "web_model_paginate" | "web_model_search" | "web_model_soft_destroy"
16910 | "web_model_with"
16911 | "web_jobs_init" | "web_job_enqueue" | "web_job_dequeue"
16912 | "web_job_complete" | "web_job_fail"
16913 | "web_jobs_list" | "web_jobs_stats" | "web_job_purge"
16914 | "web_jsonapi_resource" | "web_jsonapi_collection" | "web_jsonapi_error"
16915 | "web_bearer_token" | "web_jwt_encode" | "web_jwt_decode"
16916 | "web_otp_secret" | "web_otp_generate" | "web_otp_verify"
16917 | "web_uuid" | "web_now" | "web_log" | "web_rate_limit"
16918 | "web_t" | "web_load_locale" | "web_openapi"
16919 | "web_faker_int" | "web_faker_email" | "web_faker_name"
16920 | "web_faker_sentence" | "web_faker_paragraph"
16921 => Some(name),
16922 _ => None,
16923 }
16924 }
16925
16926 fn is_reserved_hash_name(name: &str) -> bool {
16929 matches!(
16930 name,
16931 "b" | "pc"
16932 | "e"
16933 | "a"
16934 | "d"
16935 | "c"
16936 | "p"
16937 | "k"
16938 | "all"
16939 | "stryke::builtins"
16940 | "stryke::perl_compats"
16941 | "stryke::extensions"
16942 | "stryke::aliases"
16943 | "stryke::descriptions"
16944 | "stryke::categories"
16945 | "stryke::primaries"
16946 | "stryke::keywords"
16947 | "stryke::all"
16948 )
16949 }
16950
16951 const RESERVED_FUNCTION_NAMES: &'static [&'static str] = &[
16956 "y",
16957 "tr",
16958 "s",
16959 "m",
16960 "q",
16961 "qq",
16962 "qw",
16963 "qx",
16964 "qr",
16965 "if",
16966 "unless",
16967 "while",
16968 "until",
16969 "for",
16970 "foreach",
16971 "given",
16972 "when",
16973 "else",
16974 "elsif",
16975 "do",
16976 "eval",
16977 "return",
16978 "last",
16979 "next",
16980 "redo",
16981 "goto",
16982 "my",
16983 "our",
16984 "local",
16985 "state",
16986 "sub",
16987 "fn",
16988 "class",
16989 "struct",
16990 "enum",
16991 "trait",
16992 "use",
16993 "no",
16994 "require",
16995 "package",
16996 "BEGIN",
16997 "END",
16998 "CHECK",
16999 "INIT",
17000 "UNITCHECK",
17001 "and",
17002 "or",
17003 "not",
17004 "x",
17005 "eq",
17006 "ne",
17007 "lt",
17008 "gt",
17009 "le",
17010 "ge",
17011 "cmp",
17012 ];
17013
17014 fn check_udf_shadows_builtin(&self, name: &str, line: usize) -> PerlResult<()> {
17015 if !name.contains("::") {
17017 if Self::RESERVED_FUNCTION_NAMES.contains(&name) {
17018 return Err(self.syntax_err(
17019 format!("`{name}` is a reserved word and cannot be used as a function name"),
17020 line,
17021 ));
17022 }
17023 if Self::is_known_bareword(name)
17024 || Self::is_try_builtin_name(name)
17025 || crate::list_builtins::is_list_builtin_name(name)
17026 {
17027 return Err(self.syntax_err(
17028 format!(
17029"`{name}` is a stryke builtin and cannot be redefined (this is not Perl 5; use `fn` not `sub`, or pass --compat)"
17030 ),
17031 line,
17032 ));
17033 }
17034 }
17035 Ok(())
17036 }
17037
17038 fn check_hash_shadows_reserved(&self, name: &str, line: usize) -> PerlResult<()> {
17041 if Self::is_reserved_hash_name(name) {
17042 return Err(self.syntax_err(
17043 format!(
17044"`%{name}` is a stryke reserved hash and cannot be redefined (this is not Perl 5; pass --compat for Perl 5 mode)"
17045 ),
17046 line,
17047 ));
17048 }
17049 Ok(())
17050 }
17051
17052 fn validate_hash_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
17055 match &value.kind {
17056 ExprKind::Integer(_) | ExprKind::Float(_) => {
17057 return Err(self.syntax_err(
17058 "cannot assign scalar to hash — use %h = (key => value) or %h = %{$hashref}",
17059 line,
17060 ));
17061 }
17062 ExprKind::String(_) | ExprKind::InterpolatedString(_) | ExprKind::Bareword(_) => {
17063 return Err(self.syntax_err(
17064 "cannot assign string to hash — use %h = (key => value) or %h = %{$hashref}",
17065 line,
17066 ));
17067 }
17068 ExprKind::ArrayRef(_) => {
17069 return Err(self.syntax_err(
17070 "cannot assign arrayref to hash — use %h = @{$arrayref} for even-length list",
17071 line,
17072 ));
17073 }
17074 ExprKind::ScalarRef(inner) => {
17075 if matches!(inner.kind, ExprKind::ArrayVar(_)) {
17076 return Err(self.syntax_err(
17077 "cannot assign \\@array to hash — use %h = @array for even-length list",
17078 line,
17079 ));
17080 }
17081 if matches!(inner.kind, ExprKind::HashVar(_)) {
17082 return Err(self.syntax_err(
17083 "cannot assign \\%hash to hash — use %h = %other directly",
17084 line,
17085 ));
17086 }
17087 }
17088 ExprKind::HashRef(_) => {
17089 return Err(self.syntax_err(
17090 "cannot assign hashref to hash — use %h = %{$hashref} to dereference",
17091 line,
17092 ));
17093 }
17094 ExprKind::CodeRef { .. } => {
17095 return Err(self.syntax_err("cannot assign coderef to hash", line));
17096 }
17097 ExprKind::Undef => {
17098 return Err(
17099 self.syntax_err("cannot assign undef to hash — use %h = () to empty", line)
17100 );
17101 }
17102 ExprKind::List(items)
17103 if items.len() % 2 != 0
17104 && !items.iter().any(|e| {
17105 matches!(
17106 e.kind,
17107 ExprKind::ArrayVar(_)
17108 | ExprKind::HashVar(_)
17109 | ExprKind::FuncCall { .. }
17110 | ExprKind::Deref { .. }
17111 | ExprKind::ScalarVar(_)
17112 )
17113 }) =>
17114 {
17115 return Err(self.syntax_err(
17116 format!(
17117 "odd-length list ({} elements) in hash assignment — missing value for last key",
17118 items.len()
17119 ),
17120 line,
17121 ));
17122 }
17123 _ => {}
17124 }
17125 Ok(())
17126 }
17127
17128 fn validate_array_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
17133 if let ExprKind::Undef = &value.kind {
17134 return Err(
17135 self.syntax_err("cannot assign undef to array — use @a = () to empty", line)
17136 );
17137 }
17138 Ok(())
17139 }
17140
17141 fn validate_scalar_assignment(&self, value: &Expr, line: usize) -> PerlResult<()> {
17144 if let ExprKind::List(items) = &value.kind {
17145 if items.len() > 1 {
17146 return Err(self.syntax_err(
17147 format!(
17148 "cannot assign {}-element list to scalar — Perl 5 silently takes last element; use ($x) = (list) or $x = $list[-1]",
17149 items.len()
17150 ),
17151 line,
17152 ));
17153 }
17154 }
17155 Ok(())
17156 }
17157
17158 fn validate_assignment(&self, target: &Expr, value: &Expr, line: usize) -> PerlResult<()> {
17160 if crate::compat_mode() {
17161 return Ok(());
17162 }
17163 match &target.kind {
17164 ExprKind::HashVar(_) => self.validate_hash_assignment(value, line),
17165 ExprKind::ArrayVar(_) => self.validate_array_assignment(value, line),
17166 ExprKind::ScalarVar(_) => self.validate_scalar_assignment(value, line),
17167 _ => Ok(()),
17168 }
17169 }
17170
17171 fn parse_block_or_bareword_cmp_block(&mut self) -> PerlResult<Block> {
17175 if matches!(self.peek(), Token::LBrace) {
17176 return self.parse_block();
17177 }
17178 let line = self.peek_line();
17179 if let Token::Ident(ref name) = self.peek().clone() {
17181 if matches!(
17182 self.peek_at(1),
17183 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
17184 ) {
17185 let name = name.clone();
17186 self.advance();
17187 let body = Expr {
17188 kind: ExprKind::FuncCall {
17189 name,
17190 args: vec![
17191 Expr {
17192 kind: ExprKind::ScalarVar("a".to_string()),
17193 line,
17194 },
17195 Expr {
17196 kind: ExprKind::ScalarVar("b".to_string()),
17197 line,
17198 },
17199 ],
17200 },
17201 line,
17202 };
17203 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
17204 }
17205 }
17206 let expr = self.parse_assign_expr_stop_at_pipe()?;
17208 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
17209 }
17210
17211 fn parse_fan_optional_progress(
17213 &mut self,
17214 which: &'static str,
17215 ) -> PerlResult<Option<Box<Expr>>> {
17216 let line = self.peek_line();
17217 if self.eat(&Token::Comma) {
17218 match self.peek() {
17219 Token::Ident(ref kw)
17220 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
17221 {
17222 self.advance();
17223 self.expect(&Token::FatArrow)?;
17224 return Ok(Some(Box::new(self.parse_assign_expr()?)));
17225 }
17226 _ => {
17227 return Err(self.syntax_err(
17228 format!("{which}: expected `progress => EXPR` after comma"),
17229 line,
17230 ));
17231 }
17232 }
17233 }
17234 if let Token::Ident(ref kw) = self.peek().clone() {
17235 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
17236 self.advance();
17237 self.expect(&Token::FatArrow)?;
17238 return Ok(Some(Box::new(self.parse_assign_expr()?)));
17239 }
17240 }
17241 Ok(None)
17242 }
17243
17244 fn parse_assign_expr_list_optional_progress(&mut self) -> PerlResult<(Expr, Option<Expr>)> {
17251 if self.in_pipe_rhs()
17257 && matches!(
17258 self.peek(),
17259 Token::Semicolon
17260 | Token::RBrace
17261 | Token::RParen
17262 | Token::Eof
17263 | Token::PipeForward
17264 | Token::Comma
17265 )
17266 {
17267 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
17268 }
17269 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
17270 loop {
17271 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
17272 break;
17273 }
17274 if matches!(
17275 self.peek(),
17276 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
17277 ) {
17278 break;
17279 }
17280 if self.peek_is_postfix_stmt_modifier_keyword() {
17281 break;
17282 }
17283 if let Token::Ident(ref kw) = self.peek().clone() {
17284 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
17285 self.advance();
17286 self.expect(&Token::FatArrow)?;
17287 let prog = self.parse_assign_expr_stop_at_pipe()?;
17288 return Ok((merge_expr_list(parts), Some(prog)));
17289 }
17290 }
17291 parts.push(self.parse_assign_expr_stop_at_pipe()?);
17292 }
17293 Ok((merge_expr_list(parts), None))
17294 }
17295
17296 fn parse_one_arg(&mut self) -> PerlResult<Expr> {
17297 if matches!(self.peek(), Token::LParen) {
17298 self.advance();
17299 let expr = self.parse_expression()?;
17300 self.expect(&Token::RParen)?;
17301 Ok(expr)
17302 } else {
17303 self.parse_assign_expr_stop_at_pipe()
17304 }
17305 }
17306
17307 fn parse_named_unary_arg(&mut self) -> PerlResult<Expr> {
17316 if matches!(self.peek(), Token::LParen) {
17317 self.advance();
17318 let expr = self.parse_expression()?;
17319 self.expect(&Token::RParen)?;
17320 Ok(expr)
17321 } else {
17322 self.parse_shift()
17323 }
17324 }
17325
17326 fn parse_one_arg_or_default(&mut self) -> PerlResult<Expr> {
17327 let prev = self.prev_line();
17338 if self.peek_line() > prev {
17339 return Ok(Expr {
17340 kind: ExprKind::ScalarVar("_".into()),
17341 line: prev,
17342 });
17343 }
17344 if matches!(
17351 self.peek(),
17352 Token::Semicolon
17354 | Token::RBrace
17355 | Token::RParen
17356 | Token::RBracket
17357 | Token::Eof
17358 | Token::Comma
17359 | Token::FatArrow
17360 | Token::PipeForward
17361 | Token::Question
17363 | Token::Colon
17364 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
17366 | Token::NumLe | Token::NumGe | Token::Spaceship
17367 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
17368 | Token::StrLe | Token::StrGe | Token::StrCmp
17369 | Token::LogAnd | Token::LogOr | Token::LogNot
17371 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
17372 | Token::DefinedOr
17373 | Token::Range | Token::RangeExclusive
17375 | Token::Assign | Token::PlusAssign | Token::MinusAssign
17377 | Token::MulAssign | Token::DivAssign | Token::ModAssign
17378 | Token::PowAssign | Token::DotAssign | Token::AndAssign
17379 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
17380 | Token::ShiftLeftAssign | Token::ShiftRightAssign
17381 | Token::BitAndAssign | Token::BitOrAssign
17382 ) {
17383 return Ok(Expr {
17384 kind: ExprKind::ScalarVar("_".into()),
17385 line: self.peek_line(),
17386 });
17387 }
17388 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
17392 let line = self.peek_line();
17393 self.advance(); self.advance(); return Ok(Expr {
17396 kind: ExprKind::ScalarVar("_".into()),
17397 line,
17398 });
17399 }
17400 self.parse_named_unary_arg()
17405 }
17406
17407 fn parse_one_arg_or_argv(&mut self) -> PerlResult<Expr> {
17409 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
17411 self.advance();
17412 if matches!(self.peek(), Token::RParen) {
17413 self.advance();
17414 return Ok(Expr {
17415 kind: ExprKind::ArrayVar("_".into()),
17416 line: self.peek_line(),
17417 });
17418 }
17419 let expr = self.parse_expression()?;
17420 self.expect(&Token::RParen)?;
17421 return Ok(expr);
17422 }
17423 if matches!(
17425 self.peek(),
17426 Token::Semicolon
17427 | Token::RBrace
17428 | Token::RParen
17429 | Token::Eof
17430 | Token::Comma
17431 | Token::PipeForward
17432 ) || self.peek_line() > line
17433 {
17434 Ok(Expr {
17435 kind: ExprKind::ArrayVar("_".into()),
17436 line,
17437 })
17438 } else {
17439 self.parse_assign_expr()
17440 }
17441 }
17442
17443 fn parse_builtin_args(&mut self) -> PerlResult<Vec<Expr>> {
17444 if matches!(self.peek(), Token::LParen) {
17445 self.advance();
17446 let args = self.parse_arg_list()?;
17447 self.expect(&Token::RParen)?;
17448 Ok(args)
17449 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
17450 Ok(vec![])
17453 } else {
17454 self.parse_list_until_terminator()
17455 }
17456 }
17457
17458 #[inline]
17462 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
17463 if matches!(self.peek(), Token::FatArrow) {
17464 Some(Expr {
17465 kind: ExprKind::String(name.to_string()),
17466 line,
17467 })
17468 } else {
17469 None
17470 }
17471 }
17472
17473 fn parse_hash_subscript_key(&mut self) -> PerlResult<Expr> {
17484 let line = self.peek_line();
17485 if let Token::Ident(ref k) = self.peek().clone() {
17486 if matches!(self.peek_at(1), Token::RBrace) && !Self::is_underscore_topic_slot(k) {
17487 let s = k.clone();
17488 self.advance();
17489 return Ok(Expr {
17490 kind: ExprKind::String(s),
17491 line,
17492 });
17493 }
17494 }
17495 if matches!(self.peek_at(1), Token::RBrace) {
17496 if let Some(s) = Self::operator_keyword_to_ident_str(self.peek()) {
17497 self.advance();
17498 return Ok(Expr {
17499 kind: ExprKind::String(s.to_string()),
17500 line,
17501 });
17502 }
17503 }
17504 self.parse_expression()
17505 }
17506
17507 #[inline]
17509 fn peek_is_glob_par_progress_kw(&self) -> bool {
17510 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
17511 && matches!(self.peek_at(1), Token::FatArrow)
17512 }
17513
17514 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> PerlResult<Vec<Expr>> {
17516 let mut args = Vec::new();
17517 loop {
17518 if matches!(self.peek(), Token::RParen | Token::Eof) {
17519 break;
17520 }
17521 if self.peek_is_glob_par_progress_kw() {
17522 break;
17523 }
17524 args.push(self.parse_assign_expr()?);
17525 match self.peek() {
17526 Token::RParen => break,
17527 Token::Comma => {
17528 self.advance();
17529 if matches!(self.peek(), Token::RParen) {
17530 break;
17531 }
17532 if self.peek_is_glob_par_progress_kw() {
17533 break;
17534 }
17535 }
17536 _ => {
17537 return Err(self.syntax_err(
17538 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
17539 self.peek_line(),
17540 ));
17541 }
17542 }
17543 }
17544 Ok(args)
17545 }
17546
17547 fn parse_pattern_list_glob_par_bare(&mut self) -> PerlResult<Vec<Expr>> {
17549 let mut args = Vec::new();
17550 loop {
17551 if matches!(
17552 self.peek(),
17553 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
17554 ) {
17555 break;
17556 }
17557 if self.peek_is_postfix_stmt_modifier_keyword() {
17558 break;
17559 }
17560 if self.peek_is_glob_par_progress_kw() {
17561 break;
17562 }
17563 args.push(self.parse_assign_expr()?);
17564 if !self.eat(&Token::Comma) {
17565 break;
17566 }
17567 if self.peek_is_glob_par_progress_kw() {
17568 break;
17569 }
17570 }
17571 Ok(args)
17572 }
17573
17574 fn parse_glob_par_or_par_sed_args(&mut self) -> PerlResult<(Vec<Expr>, Option<Box<Expr>>)> {
17576 if matches!(self.peek(), Token::LParen) {
17577 self.advance();
17578 let args = self.parse_pattern_list_until_rparen_or_progress()?;
17579 let progress = if self.peek_is_glob_par_progress_kw() {
17580 self.advance();
17581 self.expect(&Token::FatArrow)?;
17582 Some(Box::new(self.parse_assign_expr()?))
17583 } else {
17584 None
17585 };
17586 self.expect(&Token::RParen)?;
17587 Ok((args, progress))
17588 } else {
17589 let args = self.parse_pattern_list_glob_par_bare()?;
17590 let progress = if self.peek_is_glob_par_progress_kw() {
17592 self.advance();
17593 self.expect(&Token::FatArrow)?;
17594 Some(Box::new(self.parse_assign_expr()?))
17595 } else {
17596 None
17597 };
17598 Ok((args, progress))
17599 }
17600 }
17601
17602 pub(crate) fn parse_arg_list(&mut self) -> PerlResult<Vec<Expr>> {
17603 let mut args = Vec::new();
17604 let saved_no_pf = self.no_pipe_forward_depth;
17608 self.no_pipe_forward_depth = 0;
17609 while !matches!(
17610 self.peek(),
17611 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
17612 ) {
17613 let arg = match self.parse_assign_expr() {
17614 Ok(e) => e,
17615 Err(err) => {
17616 self.no_pipe_forward_depth = saved_no_pf;
17617 return Err(err);
17618 }
17619 };
17620 args.push(arg);
17621 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
17622 break;
17623 }
17624 }
17625 self.no_pipe_forward_depth = saved_no_pf;
17626 Ok(args)
17627 }
17628
17629 pub(crate) fn parse_slice_arg_list(&mut self, is_hash: bool) -> PerlResult<Vec<Expr>> {
17638 let mut args = Vec::new();
17639 let saved_no_pf = self.no_pipe_forward_depth;
17640 self.no_pipe_forward_depth = 0;
17641 while !matches!(
17642 self.peek(),
17643 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
17644 ) {
17645 let arg = match self.parse_slice_arg(is_hash) {
17646 Ok(e) => e,
17647 Err(err) => {
17648 self.no_pipe_forward_depth = saved_no_pf;
17649 return Err(err);
17650 }
17651 };
17652 args.push(arg);
17653 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
17654 break;
17655 }
17656 }
17657 self.no_pipe_forward_depth = saved_no_pf;
17658 Ok(args)
17659 }
17660
17661 fn parse_slice_arg(&mut self, is_hash: bool) -> PerlResult<Expr> {
17663 let line = self.peek_line();
17664
17665 if matches!(self.peek(), Token::Colon) {
17667 self.advance();
17668 return self.finish_slice_range(None, false, is_hash, line);
17669 }
17670 if matches!(self.peek(), Token::PackageSep) {
17671 self.advance();
17672 return self.finish_slice_range(None, true, is_hash, line);
17673 }
17674
17675 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
17678 let result = self.parse_slice_endpoint(is_hash);
17679 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
17680 let from_expr = result?;
17681
17682 if matches!(self.peek(), Token::Colon) {
17684 self.advance();
17685 return self.finish_slice_range(Some(Box::new(from_expr)), false, is_hash, line);
17686 }
17687 if matches!(self.peek(), Token::PackageSep) {
17688 self.advance();
17689 return self.finish_slice_range(Some(Box::new(from_expr)), true, is_hash, line);
17690 }
17691
17692 Ok(from_expr)
17693 }
17694
17695 fn finish_slice_range(
17702 &mut self,
17703 from: Option<Box<Expr>>,
17704 double: bool,
17705 is_hash: bool,
17706 line: usize,
17707 ) -> PerlResult<Expr> {
17708 let (to, step) = if double {
17709 let step_v = self.parse_slice_optional_endpoint(is_hash)?;
17711 (None, step_v)
17712 } else {
17713 let to_v = self.parse_slice_optional_endpoint(is_hash)?;
17715 let step_v = if matches!(self.peek(), Token::Colon) {
17716 self.advance();
17717 self.parse_slice_optional_endpoint(is_hash)?
17718 } else if matches!(self.peek(), Token::PackageSep) {
17719 return Err(
17720 self.syntax_err("Unexpected `::` after slice TO endpoint".to_string(), line)
17721 );
17722 } else {
17723 None
17724 };
17725 (to_v, step_v)
17726 };
17727
17728 if let (Some(f), Some(t)) = (from.as_ref(), to.as_ref()) {
17731 return Ok(Expr {
17732 kind: ExprKind::Range {
17733 from: f.clone(),
17734 to: t.clone(),
17735 exclusive: false,
17736 step,
17737 },
17738 line,
17739 });
17740 }
17741
17742 Ok(Expr {
17743 kind: ExprKind::SliceRange { from, to, step },
17744 line,
17745 })
17746 }
17747
17748 fn parse_slice_optional_endpoint(&mut self, is_hash: bool) -> PerlResult<Option<Box<Expr>>> {
17751 if matches!(
17752 self.peek(),
17753 Token::Colon
17754 | Token::PackageSep
17755 | Token::Comma
17756 | Token::RBracket
17757 | Token::RBrace
17758 | Token::Eof
17759 ) {
17760 return Ok(None);
17761 }
17762 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
17763 let r = self.parse_slice_endpoint(is_hash);
17764 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
17765 Ok(Some(Box::new(r?)))
17766 }
17767
17768 fn parse_slice_endpoint(&mut self, is_hash: bool) -> PerlResult<Expr> {
17772 if is_hash {
17773 if let Token::Ident(name) = self.peek().clone() {
17774 if matches!(
17775 self.peek_at(1),
17776 Token::Colon
17777 | Token::PackageSep
17778 | Token::Comma
17779 | Token::RBracket
17780 | Token::RBrace
17781 ) {
17782 let line = self.peek_line();
17783 self.advance();
17784 return Ok(Expr {
17785 kind: ExprKind::String(name),
17786 line,
17787 });
17788 }
17789 }
17790 }
17791 self.parse_assign_expr()
17792 }
17793
17794 fn parse_method_arg_list_no_paren(&mut self) -> PerlResult<Vec<Expr>> {
17798 let mut args = Vec::new();
17799 let call_line = self.prev_line();
17800 loop {
17801 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
17804 break;
17805 }
17806 if matches!(
17807 self.peek(),
17808 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
17809 ) {
17810 break;
17811 }
17812 if let Token::Ident(ref kw) = self.peek().clone() {
17813 if matches!(
17814 kw.as_str(),
17815 "if" | "unless" | "while" | "until" | "for" | "foreach"
17816 ) {
17817 break;
17818 }
17819 }
17820 if args.is_empty()
17823 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
17824 {
17825 break;
17826 }
17827 if args.is_empty() && self.peek_line() > call_line {
17830 break;
17831 }
17832 args.push(self.parse_assign_expr()?);
17833 if !self.eat(&Token::Comma) {
17834 break;
17835 }
17836 }
17837 Ok(args)
17838 }
17839
17840 fn peek_method_arg_infix_terminator(&self) -> bool {
17843 matches!(
17844 self.peek(),
17845 Token::Plus
17846 | Token::Minus
17847 | Token::Star
17848 | Token::Slash
17849 | Token::Percent
17850 | Token::Power
17851 | Token::Dot
17852 | Token::X
17853 | Token::NumEq
17854 | Token::NumNe
17855 | Token::NumLt
17856 | Token::NumGt
17857 | Token::NumLe
17858 | Token::NumGe
17859 | Token::Spaceship
17860 | Token::StrEq
17861 | Token::StrNe
17862 | Token::StrLt
17863 | Token::StrGt
17864 | Token::StrLe
17865 | Token::StrGe
17866 | Token::StrCmp
17867 | Token::LogAnd
17868 | Token::LogOr
17869 | Token::LogAndWord
17870 | Token::LogOrWord
17871 | Token::DefinedOr
17872 | Token::BitAnd
17873 | Token::BitOr
17874 | Token::BitXor
17875 | Token::ShiftLeft
17876 | Token::ShiftRight
17877 | Token::Range
17878 | Token::RangeExclusive
17879 | Token::BindMatch
17880 | Token::BindNotMatch
17881 | Token::Arrow
17882 | Token::Question
17884 | Token::Colon
17885 | Token::Assign
17887 | Token::PlusAssign
17888 | Token::MinusAssign
17889 | Token::MulAssign
17890 | Token::DivAssign
17891 | Token::ModAssign
17892 | Token::PowAssign
17893 | Token::DotAssign
17894 | Token::AndAssign
17895 | Token::OrAssign
17896 | Token::XorAssign
17897 | Token::DefinedOrAssign
17898 | Token::ShiftLeftAssign
17899 | Token::ShiftRightAssign
17900 | Token::BitAndAssign
17901 | Token::BitOrAssign
17902 )
17903 }
17904
17905 fn parse_list_until_terminator(&mut self) -> PerlResult<Vec<Expr>> {
17906 let mut args = Vec::new();
17907 let call_line = self.prev_line();
17912 loop {
17913 if matches!(
17914 self.peek(),
17915 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
17916 ) {
17917 break;
17918 }
17919 if let Token::Ident(ref kw) = self.peek().clone() {
17921 if matches!(
17922 kw.as_str(),
17923 "if" | "unless" | "while" | "until" | "for" | "foreach"
17924 ) {
17925 break;
17926 }
17927 }
17928 if args.is_empty() && self.peek_line() > call_line {
17935 break;
17936 }
17937 args.push(self.parse_assign_expr_stop_at_pipe()?);
17940 if !self.eat(&Token::Comma) {
17941 break;
17942 }
17943 }
17944 Ok(args)
17945 }
17946
17947 fn parse_forced_hashref_body(&mut self, line: usize) -> PerlResult<Expr> {
17954 let saved = self.pos;
17955 if let Ok(pairs) = self.try_parse_hash_ref() {
17956 return Ok(Expr {
17957 kind: ExprKind::HashRef(pairs),
17958 line,
17959 });
17960 }
17961 self.pos = saved;
17963 if matches!(self.peek(), Token::RBrace) {
17964 self.advance();
17965 return Ok(Expr {
17966 kind: ExprKind::HashRef(vec![]),
17967 line,
17968 });
17969 }
17970 let inner = self.parse_expression()?;
17974 self.expect(&Token::RBrace)?;
17975 let sentinel_key = Expr {
17976 kind: ExprKind::String("__HASH_SPREAD__".into()),
17977 line,
17978 };
17979 Ok(Expr {
17980 kind: ExprKind::HashRef(vec![(sentinel_key, inner)]),
17981 line,
17982 })
17983 }
17984
17985 fn try_parse_hash_ref(&mut self) -> PerlResult<Vec<(Expr, Expr)>> {
17986 let mut pairs = Vec::new();
17987 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
17988 let line = self.peek_line();
17993 let key = if let Token::Ident(ref name) = self.peek().clone() {
17994 if matches!(self.peek_at(1), Token::FatArrow)
17995 && !Self::is_underscore_topic_slot(name)
17996 {
17997 self.advance();
17998 Expr {
17999 kind: ExprKind::String(name.clone()),
18000 line,
18001 }
18002 } else {
18003 self.parse_assign_expr()?
18004 }
18005 } else {
18006 self.parse_assign_expr()?
18007 };
18008 if matches!(self.peek(), Token::RBrace | Token::Comma)
18012 && matches!(
18013 key.kind,
18014 ExprKind::HashVar(_)
18015 | ExprKind::Deref {
18016 kind: Sigil::Hash,
18017 ..
18018 }
18019 )
18020 {
18021 let sentinel_key = Expr {
18025 kind: ExprKind::String("__HASH_SPREAD__".into()),
18026 line,
18027 };
18028 pairs.push((sentinel_key, key));
18029 self.eat(&Token::Comma);
18030 continue;
18031 }
18032 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
18034 let val = self.parse_assign_expr()?;
18035 pairs.push((key, val));
18036 self.eat(&Token::Comma);
18037 } else {
18038 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
18039 }
18040 }
18041 self.expect(&Token::RBrace)?;
18042 Ok(pairs)
18043 }
18044
18045 fn parse_hashref_pairs_until(&mut self, term: &Token) -> PerlResult<Vec<(Expr, Expr)>> {
18050 let mut pairs = Vec::new();
18051 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
18052 && !matches!(self.peek(), Token::Eof)
18053 {
18054 let line = self.peek_line();
18055 let key = if let Token::Ident(ref name) = self.peek().clone() {
18056 if matches!(self.peek_at(1), Token::FatArrow)
18057 && !Self::is_underscore_topic_slot(name)
18058 {
18059 self.advance();
18060 Expr {
18061 kind: ExprKind::String(name.clone()),
18062 line,
18063 }
18064 } else {
18065 self.parse_assign_expr()?
18066 }
18067 } else {
18068 self.parse_assign_expr()?
18069 };
18070 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
18071 let val = self.parse_assign_expr()?;
18072 pairs.push((key, val));
18073 self.eat(&Token::Comma);
18074 } else {
18075 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
18076 }
18077 }
18078 Ok(pairs)
18079 }
18080
18081 fn interp_chain_subscripts(
18087 &self,
18088 chars: &[char],
18089 i: &mut usize,
18090 mut base: Expr,
18091 line: usize,
18092 ) -> Expr {
18093 loop {
18094 let (after, requires_subscript) =
18096 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
18097 (*i + 2, true)
18098 } else {
18099 (*i, false)
18100 };
18101 if after >= chars.len() {
18102 break;
18103 }
18104 match chars[after] {
18105 '[' => {
18106 *i = after + 1;
18107 let mut idx_str = String::new();
18108 while *i < chars.len() && chars[*i] != ']' {
18109 idx_str.push(chars[*i]);
18110 *i += 1;
18111 }
18112 if *i < chars.len() {
18113 *i += 1;
18114 }
18115 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
18116 Expr {
18117 kind: ExprKind::ScalarVar(rest.to_string()),
18118 line,
18119 }
18120 } else if let Ok(n) = idx_str.parse::<i64>() {
18121 Expr {
18122 kind: ExprKind::Integer(n),
18123 line,
18124 }
18125 } else {
18126 Expr {
18127 kind: ExprKind::String(idx_str),
18128 line,
18129 }
18130 };
18131 base = Expr {
18132 kind: ExprKind::ArrowDeref {
18133 expr: Box::new(base),
18134 index: Box::new(idx_expr),
18135 kind: DerefKind::Array,
18136 },
18137 line,
18138 };
18139 }
18140 '{' => {
18141 *i = after + 1;
18142 let mut key = String::new();
18143 let mut depth = 1usize;
18144 while *i < chars.len() && depth > 0 {
18145 if chars[*i] == '{' {
18146 depth += 1;
18147 } else if chars[*i] == '}' {
18148 depth -= 1;
18149 if depth == 0 {
18150 break;
18151 }
18152 }
18153 key.push(chars[*i]);
18154 *i += 1;
18155 }
18156 if *i < chars.len() {
18157 *i += 1;
18158 }
18159 let key_expr = if let Some(rest) = key.strip_prefix('$') {
18160 Expr {
18161 kind: ExprKind::ScalarVar(rest.to_string()),
18162 line,
18163 }
18164 } else {
18165 Expr {
18166 kind: ExprKind::String(key),
18167 line,
18168 }
18169 };
18170 base = Expr {
18171 kind: ExprKind::ArrowDeref {
18172 expr: Box::new(base),
18173 index: Box::new(key_expr),
18174 kind: DerefKind::Hash,
18175 },
18176 line,
18177 };
18178 }
18179 _ => {
18180 if requires_subscript {
18181 }
18183 break;
18184 }
18185 }
18186 }
18187 base
18188 }
18189
18190 fn no_interop_check_scalar_var_name(&self, name: &str, line: usize) -> PerlResult<()> {
18194 if crate::no_interop_mode() && (name == "a" || name == "b") {
18195 return Err(self.syntax_err(
18196 format!(
18197 "stryke uses `$_0` / `$_1` instead of `${}` (--no-interop is active)",
18198 name
18199 ),
18200 line,
18201 ));
18202 }
18203 Ok(())
18204 }
18205
18206 fn parse_interpolated_string(&self, s: &str, line: usize) -> PerlResult<Expr> {
18207 let mut parts = Vec::new();
18209 let mut literal = String::new();
18210 let chars: Vec<char> = s.chars().collect();
18211 let mut i = 0;
18212
18213 'istr: while i < chars.len() {
18214 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
18215 literal.push('$');
18216 i += 1;
18217 continue;
18218 }
18219 if chars[i] == LITERAL_AT_IN_DQUOTE {
18220 literal.push('@');
18221 i += 1;
18222 continue;
18223 }
18224 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
18226 literal.push('\\');
18227 i += 1;
18228 }
18230 if chars[i] == '$' && i + 1 < chars.len() {
18231 if !literal.is_empty() {
18232 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18233 }
18234 i += 1; while i < chars.len() && chars[i].is_whitespace() {
18237 i += 1;
18238 }
18239 if i >= chars.len() {
18240 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
18241 }
18242 if chars[i] == '#' {
18244 i += 1;
18245 let mut sname = String::from("#");
18246 while i < chars.len()
18247 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
18248 {
18249 sname.push(chars[i]);
18250 i += 1;
18251 }
18252 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18253 sname.push_str("::");
18254 i += 2;
18255 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18256 sname.push(chars[i]);
18257 i += 1;
18258 }
18259 }
18260 self.no_interop_check_scalar_var_name(&sname, line)?;
18261 parts.push(StringPart::ScalarVar(sname));
18262 continue;
18263 }
18264 if chars[i] == '$' {
18268 let next_c = chars.get(i + 1).copied();
18269 let is_pid = match next_c {
18270 None => true,
18271 Some(c)
18272 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
18273 {
18274 true
18275 }
18276 _ => false,
18277 };
18278 if is_pid {
18279 parts.push(StringPart::ScalarVar("$$".to_string()));
18280 i += 1; continue;
18282 }
18283 i += 1; }
18285 if chars[i] == '{' {
18286 i += 1;
18292 let mut inner = String::new();
18293 let mut depth = 1usize;
18294 while i < chars.len() && depth > 0 {
18295 match chars[i] {
18296 '{' => depth += 1,
18297 '}' => {
18298 depth -= 1;
18299 if depth == 0 {
18300 break;
18301 }
18302 }
18303 _ => {}
18304 }
18305 inner.push(chars[i]);
18306 i += 1;
18307 }
18308 if i < chars.len() {
18309 i += 1; }
18311
18312 let trimmed = inner.trim();
18316 let is_expr = trimmed.starts_with('$')
18317 || trimmed.starts_with('\\')
18318 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|']);
18321 let mut base: Expr = if is_expr {
18322 match parse_expression_from_str(trimmed, "<interp>") {
18326 Ok(e) => Expr {
18327 kind: ExprKind::Deref {
18328 expr: Box::new(e),
18329 kind: Sigil::Scalar,
18330 },
18331 line,
18332 },
18333 Err(_) => Expr {
18334 kind: ExprKind::ScalarVar(inner.clone()),
18335 line,
18336 },
18337 }
18338 } else {
18339 self.no_interop_check_scalar_var_name(&inner, line)?;
18341 Expr {
18342 kind: ExprKind::ScalarVar(inner),
18343 line,
18344 }
18345 };
18346
18347 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18351 parts.push(StringPart::Expr(base));
18352 } else if chars[i] == '^' {
18353 let mut name = String::from("^");
18355 i += 1;
18356 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18357 name.push(chars[i]);
18358 i += 1;
18359 }
18360 if i < chars.len() && chars[i] == '{' {
18361 i += 1; let mut key = String::new();
18363 let mut depth = 1;
18364 while i < chars.len() && depth > 0 {
18365 if chars[i] == '{' {
18366 depth += 1;
18367 } else if chars[i] == '}' {
18368 depth -= 1;
18369 if depth == 0 {
18370 break;
18371 }
18372 }
18373 key.push(chars[i]);
18374 i += 1;
18375 }
18376 if i < chars.len() {
18377 i += 1;
18378 }
18379 let key_expr = if let Some(rest) = key.strip_prefix('$') {
18380 Expr {
18381 kind: ExprKind::ScalarVar(rest.to_string()),
18382 line,
18383 }
18384 } else {
18385 Expr {
18386 kind: ExprKind::String(key),
18387 line,
18388 }
18389 };
18390 parts.push(StringPart::Expr(Expr {
18391 kind: ExprKind::HashElement {
18392 hash: name,
18393 key: Box::new(key_expr),
18394 },
18395 line,
18396 }));
18397 } else if i < chars.len() && chars[i] == '[' {
18398 i += 1;
18399 let mut idx_str = String::new();
18400 while i < chars.len() && chars[i] != ']' {
18401 idx_str.push(chars[i]);
18402 i += 1;
18403 }
18404 if i < chars.len() {
18405 i += 1;
18406 }
18407 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
18408 Expr {
18409 kind: ExprKind::ScalarVar(rest.to_string()),
18410 line,
18411 }
18412 } else if let Ok(n) = idx_str.parse::<i64>() {
18413 Expr {
18414 kind: ExprKind::Integer(n),
18415 line,
18416 }
18417 } else {
18418 Expr {
18419 kind: ExprKind::String(idx_str),
18420 line,
18421 }
18422 };
18423 parts.push(StringPart::Expr(Expr {
18424 kind: ExprKind::ArrayElement {
18425 array: name,
18426 index: Box::new(idx_expr),
18427 },
18428 line,
18429 }));
18430 } else {
18431 self.no_interop_check_scalar_var_name(&name, line)?;
18432 parts.push(StringPart::ScalarVar(name));
18433 }
18434 } else if chars[i].is_alphabetic() || chars[i] == '_' {
18435 let mut name = String::new();
18436 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18437 name.push(chars[i]);
18438 i += 1;
18439 }
18440 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18445 name.push_str("::");
18446 i += 2;
18447 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18448 name.push(chars[i]);
18449 i += 1;
18450 }
18451 }
18452 let is_topic_slot = name == "_"
18457 || (name.len() > 1
18458 && name.starts_with('_')
18459 && name[1..].bytes().all(|b| b.is_ascii_digit()));
18460 if is_topic_slot {
18461 let try_indexed = chars.get(i) == Some(&'<')
18463 && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit());
18464 let mut handled_indexed = false;
18465 if try_indexed {
18466 let mut j = i + 1;
18467 while j < chars.len() && chars[j].is_ascii_digit() {
18468 j += 1;
18469 }
18470 let digits: String = chars[i + 1..j].iter().collect();
18471 if let Ok(n) = digits.parse::<usize>() {
18472 if n >= 1 {
18473 for _ in 0..n {
18474 name.push('<');
18475 }
18476 i = j;
18477 handled_indexed = true;
18478 }
18479 }
18480 }
18481 if !handled_indexed {
18482 while i < chars.len() && chars[i] == '<' {
18483 name.push('<');
18484 i += 1;
18485 }
18486 }
18487 }
18488 self.no_interop_check_scalar_var_name(&name, line)?;
18493 let mut base = if i < chars.len() && chars[i] == '{' {
18498 i += 1; let mut key = String::new();
18501 let mut depth = 1;
18502 while i < chars.len() && depth > 0 {
18503 if chars[i] == '{' {
18504 depth += 1;
18505 } else if chars[i] == '}' {
18506 depth -= 1;
18507 if depth == 0 {
18508 break;
18509 }
18510 }
18511 key.push(chars[i]);
18512 i += 1;
18513 }
18514 if i < chars.len() {
18515 i += 1;
18516 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
18518 Expr {
18519 kind: ExprKind::ScalarVar(rest.to_string()),
18520 line,
18521 }
18522 } else {
18523 Expr {
18524 kind: ExprKind::String(key),
18525 line,
18526 }
18527 };
18528 Expr {
18529 kind: ExprKind::HashElement {
18530 hash: name,
18531 key: Box::new(key_expr),
18532 },
18533 line,
18534 }
18535 } else if i < chars.len() && chars[i] == '[' {
18536 i += 1;
18538 let mut idx_str = String::new();
18539 while i < chars.len() && chars[i] != ']' {
18540 idx_str.push(chars[i]);
18541 i += 1;
18542 }
18543 if i < chars.len() {
18544 i += 1;
18545 }
18546 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
18547 Expr {
18548 kind: ExprKind::ScalarVar(rest.to_string()),
18549 line,
18550 }
18551 } else if let Ok(n) = idx_str.parse::<i64>() {
18552 Expr {
18553 kind: ExprKind::Integer(n),
18554 line,
18555 }
18556 } else {
18557 Expr {
18558 kind: ExprKind::String(idx_str),
18559 line,
18560 }
18561 };
18562 Expr {
18563 kind: ExprKind::ArrayElement {
18564 array: name,
18565 index: Box::new(idx_expr),
18566 },
18567 line,
18568 }
18569 } else {
18570 Expr {
18572 kind: ExprKind::ScalarVar(name),
18573 line,
18574 }
18575 };
18576
18577 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18581 parts.push(StringPart::Expr(base));
18582 } else if chars[i].is_ascii_digit() {
18583 if chars[i] == '0' {
18585 i += 1;
18586 if i < chars.len() && chars[i].is_ascii_digit() {
18587 return Err(self.syntax_err(
18588 "Numeric variables with more than one digit may not start with '0'",
18589 line,
18590 ));
18591 }
18592 parts.push(StringPart::ScalarVar("0".into()));
18593 } else {
18594 let start = i;
18595 while i < chars.len() && chars[i].is_ascii_digit() {
18596 i += 1;
18597 }
18598 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
18599 }
18600 } else {
18601 let c = chars[i];
18602 let probe = c.to_string();
18603 if VMHelper::is_special_scalar_name_for_get(&probe)
18609 || matches!(c, '\'' | '`' | '&')
18610 {
18611 i += 1;
18612 if i < chars.len() && chars[i] == '{' {
18614 i += 1; let mut key = String::new();
18616 let mut depth = 1;
18617 while i < chars.len() && depth > 0 {
18618 if chars[i] == '{' {
18619 depth += 1;
18620 } else if chars[i] == '}' {
18621 depth -= 1;
18622 if depth == 0 {
18623 break;
18624 }
18625 }
18626 key.push(chars[i]);
18627 i += 1;
18628 }
18629 if i < chars.len() {
18630 i += 1;
18631 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
18633 Expr {
18634 kind: ExprKind::ScalarVar(rest.to_string()),
18635 line,
18636 }
18637 } else {
18638 Expr {
18639 kind: ExprKind::String(key),
18640 line,
18641 }
18642 };
18643 let mut base = Expr {
18644 kind: ExprKind::HashElement {
18645 hash: probe,
18646 key: Box::new(key_expr),
18647 },
18648 line,
18649 };
18650 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18651 parts.push(StringPart::Expr(base));
18652 } else {
18653 let mut base = Expr {
18655 kind: ExprKind::ScalarVar(probe),
18656 line,
18657 };
18658 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
18659 if matches!(base.kind, ExprKind::ScalarVar(_)) {
18660 if let ExprKind::ScalarVar(name) = base.kind {
18662 self.no_interop_check_scalar_var_name(&name, line)?;
18663 parts.push(StringPart::ScalarVar(name));
18664 }
18665 } else {
18666 parts.push(StringPart::Expr(base));
18667 }
18668 }
18669 } else {
18670 literal.push('$');
18671 literal.push(c);
18672 i += 1;
18673 }
18674 }
18675 } else if chars[i] == '@' && i + 1 < chars.len() {
18676 let next = chars[i + 1];
18677 if next == '$' {
18679 if !literal.is_empty() {
18680 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18681 }
18682 i += 1; debug_assert_eq!(chars[i], '$');
18684 i += 1; while i < chars.len() && chars[i].is_whitespace() {
18686 i += 1;
18687 }
18688 if i >= chars.len() {
18689 return Err(self.syntax_err(
18690 "Expected variable or block after `@$` in double-quoted string",
18691 line,
18692 ));
18693 }
18694 let inner_expr = if chars[i] == '{' {
18695 i += 1;
18696 let start = i;
18697 let mut depth = 1usize;
18698 while i < chars.len() && depth > 0 {
18699 match chars[i] {
18700 '{' => depth += 1,
18701 '}' => {
18702 depth -= 1;
18703 if depth == 0 {
18704 break;
18705 }
18706 }
18707 _ => {}
18708 }
18709 i += 1;
18710 }
18711 if depth != 0 {
18712 return Err(self.syntax_err(
18713 "Unterminated `${ ... }` after `@` in double-quoted string",
18714 line,
18715 ));
18716 }
18717 let inner: String = chars[start..i].iter().collect();
18718 i += 1; parse_expression_from_str(inner.trim(), "-e")?
18720 } else {
18721 let mut name = String::new();
18722 if chars[i] == '^' {
18723 name.push('^');
18724 i += 1;
18725 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
18726 {
18727 name.push(chars[i]);
18728 i += 1;
18729 }
18730 } else {
18731 while i < chars.len()
18732 && (chars[i].is_alphanumeric()
18733 || chars[i] == '_'
18734 || chars[i] == ':')
18735 {
18736 name.push(chars[i]);
18737 i += 1;
18738 }
18739 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18740 name.push_str("::");
18741 i += 2;
18742 while i < chars.len()
18743 && (chars[i].is_alphanumeric() || chars[i] == '_')
18744 {
18745 name.push(chars[i]);
18746 i += 1;
18747 }
18748 }
18749 }
18750 if name.is_empty() {
18751 return Err(self.syntax_err(
18752 "Expected identifier after `@$` in double-quoted string",
18753 line,
18754 ));
18755 }
18756 Expr {
18757 kind: ExprKind::ScalarVar(name),
18758 line,
18759 }
18760 };
18761 parts.push(StringPart::Expr(Expr {
18762 kind: ExprKind::Deref {
18763 expr: Box::new(inner_expr),
18764 kind: Sigil::Array,
18765 },
18766 line,
18767 }));
18768 continue 'istr;
18769 }
18770 if next == '{' {
18771 if !literal.is_empty() {
18772 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18773 }
18774 i += 2; let start = i;
18776 let mut depth = 1usize;
18777 while i < chars.len() && depth > 0 {
18778 match chars[i] {
18779 '{' => depth += 1,
18780 '}' => {
18781 depth -= 1;
18782 if depth == 0 {
18783 break;
18784 }
18785 }
18786 _ => {}
18787 }
18788 i += 1;
18789 }
18790 if depth != 0 {
18791 return Err(
18792 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
18793 );
18794 }
18795 let inner: String = chars[start..i].iter().collect();
18796 i += 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
18798 parts.push(StringPart::Expr(Expr {
18799 kind: ExprKind::Deref {
18800 expr: Box::new(inner_expr),
18801 kind: Sigil::Array,
18802 },
18803 line,
18804 }));
18805 continue 'istr;
18806 }
18807 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
18808 literal.push(chars[i]);
18809 i += 1;
18810 } else {
18811 if !literal.is_empty() {
18812 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18813 }
18814 i += 1;
18815 let mut name = String::new();
18816 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
18817 name.push(chars[i]);
18818 i += 1;
18819 } else {
18820 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
18821 name.push(chars[i]);
18822 i += 1;
18823 }
18824 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
18825 name.push_str("::");
18826 i += 2;
18827 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
18828 {
18829 name.push(chars[i]);
18830 i += 1;
18831 }
18832 }
18833 }
18834 if i < chars.len() && chars[i] == '[' {
18835 i += 1;
18836 let start_inner = i;
18837 let mut depth = 1usize;
18838 while i < chars.len() && depth > 0 {
18839 match chars[i] {
18840 '[' => depth += 1,
18841 ']' => depth -= 1,
18842 _ => {}
18843 }
18844 if depth == 0 {
18845 let inner: String = chars[start_inner..i].iter().collect();
18846 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
18848 parts.push(StringPart::Expr(Expr {
18849 kind: ExprKind::ArraySlice {
18850 array: name.clone(),
18851 indices,
18852 },
18853 line,
18854 }));
18855 continue 'istr;
18856 }
18857 i += 1;
18858 }
18859 return Err(self.syntax_err(
18860 "Unterminated [ in array slice inside quoted string",
18861 line,
18862 ));
18863 }
18864 parts.push(StringPart::ArrayVar(name));
18865 }
18866 } else if chars[i] == '#'
18867 && i + 1 < chars.len()
18868 && chars[i + 1] == '{'
18869 && !crate::compat_mode()
18870 {
18871 if !literal.is_empty() {
18873 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
18874 }
18875 i += 2; let mut inner = String::new();
18877 let mut depth = 1usize;
18878 while i < chars.len() && depth > 0 {
18879 match chars[i] {
18880 '{' => depth += 1,
18881 '}' => {
18882 depth -= 1;
18883 if depth == 0 {
18884 break;
18885 }
18886 }
18887 _ => {}
18888 }
18889 inner.push(chars[i]);
18890 i += 1;
18891 }
18892 if i < chars.len() {
18893 i += 1; }
18895 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
18896 parts.push(StringPart::Expr(expr));
18897 } else {
18898 literal.push(chars[i]);
18899 i += 1;
18900 }
18901 }
18902 if !literal.is_empty() {
18903 parts.push(StringPart::Literal(literal));
18904 }
18905
18906 if parts.len() == 1 {
18907 if let StringPart::Literal(s) = &parts[0] {
18908 return Ok(Expr {
18909 kind: ExprKind::String(s.clone()),
18910 line,
18911 });
18912 }
18913 }
18914 if parts.is_empty() {
18915 return Ok(Expr {
18916 kind: ExprKind::String(String::new()),
18917 line,
18918 });
18919 }
18920
18921 Ok(Expr {
18922 kind: ExprKind::InterpolatedString(parts),
18923 line,
18924 })
18925 }
18926
18927 fn expr_to_overload_key(&self, e: &Expr) -> PerlResult<String> {
18928 match &e.kind {
18929 ExprKind::String(s) => Ok(s.clone()),
18930 _ => Err(self.syntax_err(
18931 "overload key must be a string literal (e.g. '\"\"' or '+')",
18932 e.line,
18933 )),
18934 }
18935 }
18936
18937 fn expr_to_overload_sub(&mut self, e: &Expr) -> PerlResult<String> {
18938 match &e.kind {
18939 ExprKind::String(s) => Ok(s.clone()),
18940 ExprKind::Integer(n) => Ok(n.to_string()),
18941 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
18942 ExprKind::CodeRef { params, body } => {
18946 let id = self.next_overload_anon_id;
18947 self.next_overload_anon_id = self.next_overload_anon_id.saturating_add(1);
18948 let name = format!("__overload_anon_{}", id);
18949 self.pending_synthetic_subs.push(Statement {
18950 label: None,
18951 kind: StmtKind::SubDecl {
18952 name: name.clone(),
18953 params: params.clone(),
18954 body: body.clone(),
18955 prototype: None,
18956 },
18957 line: e.line,
18958 });
18959 Ok(name)
18960 }
18961 _ => Err(self.syntax_err(
18962 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
18963 e.line,
18964 )),
18965 }
18966 }
18967}
18968
18969fn merge_expr_list(parts: Vec<Expr>) -> Expr {
18970 if parts.len() == 1 {
18971 parts.into_iter().next().unwrap()
18972 } else {
18973 let line = parts.first().map(|e| e.line).unwrap_or(0);
18974 Expr {
18975 kind: ExprKind::List(parts),
18976 line,
18977 }
18978 }
18979}
18980
18981pub fn parse_expression_from_str(s: &str, file: &str) -> PerlResult<Expr> {
18983 let mut lexer = Lexer::new_with_file(s, file);
18984 let tokens = lexer.tokenize()?;
18985 let mut parser = Parser::new_with_file(tokens, file);
18986 let e = parser.parse_expression()?;
18987 if !parser.at_eof() {
18988 return Err(parser.syntax_err(
18989 "Extra tokens in embedded string expression",
18990 parser.peek_line(),
18991 ));
18992 }
18993 Ok(e)
18994}
18995
18996pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> PerlResult<Expr> {
18998 let mut lexer = Lexer::new_with_file(s, file);
18999 let tokens = lexer.tokenize()?;
19000 let mut parser = Parser::new_with_file(tokens, file);
19001 let stmts = parser.parse_statements()?;
19002 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
19003 let inner = Expr {
19004 kind: ExprKind::CodeRef {
19005 params: vec![],
19006 body: stmts,
19007 },
19008 line: inner_line,
19009 };
19010 Ok(Expr {
19011 kind: ExprKind::Do(Box::new(inner)),
19012 line,
19013 })
19014}
19015
19016pub fn parse_slice_indices_from_str(s: &str, file: &str) -> PerlResult<Vec<Expr>> {
19019 let mut lexer = Lexer::new_with_file(s, file);
19020 let tokens = lexer.tokenize()?;
19021 let mut parser = Parser::new_with_file(tokens, file);
19022 parser.parse_arg_list()
19023}
19024
19025pub fn parse_format_value_line(line: &str) -> PerlResult<Vec<Expr>> {
19026 let trimmed = line.trim();
19027 if trimmed.is_empty() {
19028 return Ok(vec![]);
19029 }
19030 let mut lexer = Lexer::new(trimmed);
19031 let tokens = lexer.tokenize()?;
19032 let mut parser = Parser::new(tokens);
19033 let mut exprs = Vec::new();
19034 loop {
19035 if parser.at_eof() {
19036 break;
19037 }
19038 exprs.push(parser.parse_assign_expr()?);
19040 if parser.eat(&Token::Comma) {
19041 continue;
19042 }
19043 if !parser.at_eof() {
19044 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
19045 }
19046 break;
19047 }
19048 Ok(exprs)
19049}
19050
19051#[cfg(test)]
19052mod tests {
19053 use super::*;
19054
19055 fn parse_ok(code: &str) -> Program {
19056 let mut lexer = Lexer::new(code);
19057 let tokens = lexer.tokenize().expect("tokenize");
19058 let mut parser = Parser::new(tokens);
19059 parser.parse_program().expect("parse")
19060 }
19061
19062 fn parse_err(code: &str) -> String {
19063 let mut lexer = Lexer::new(code);
19064 let tokens = match lexer.tokenize() {
19065 Ok(t) => t,
19066 Err(e) => return e.message,
19067 };
19068 let mut parser = Parser::new(tokens);
19069 parser.parse_program().unwrap_err().message
19070 }
19071
19072 #[test]
19073 fn parse_empty_program() {
19074 let p = parse_ok("");
19075 assert!(p.statements.is_empty());
19076 }
19077
19078 #[test]
19079 fn parse_semicolons_only() {
19080 let p = parse_ok(";;");
19081 assert!(p.statements.len() <= 3);
19082 }
19083
19084 #[test]
19085 fn parse_simple_scalar_assignment() {
19086 let p = parse_ok("$x = 1");
19087 assert_eq!(p.statements.len(), 1);
19088 }
19089
19090 #[test]
19091 fn parse_simple_array_assignment() {
19092 let p = parse_ok("@arr = (1, 2, 3)");
19093 assert_eq!(p.statements.len(), 1);
19094 }
19095
19096 #[test]
19097 fn parse_simple_hash_assignment() {
19098 let p = parse_ok("%h = (a => 1, b => 2)");
19099 assert_eq!(p.statements.len(), 1);
19100 }
19101
19102 #[test]
19103 fn parse_subroutine_decl() {
19104 let p = parse_ok("fn foo { 1 }");
19105 assert_eq!(p.statements.len(), 1);
19106 match &p.statements[0].kind {
19107 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
19108 _ => panic!("expected SubDecl"),
19109 }
19110 }
19111
19112 #[test]
19113 fn parse_subroutine_with_prototype() {
19114 let p = parse_ok("fn foo ($$) { 1 }");
19115 assert_eq!(p.statements.len(), 1);
19116 match &p.statements[0].kind {
19117 StmtKind::SubDecl { prototype, .. } => {
19118 assert!(prototype.is_some());
19119 }
19120 _ => panic!("expected SubDecl"),
19121 }
19122 }
19123
19124 #[test]
19125 fn parse_anonymous_fn() {
19126 let p = parse_ok("my $f = fn { 1 }");
19127 assert_eq!(p.statements.len(), 1);
19128 }
19129
19130 #[test]
19131 fn parse_if_statement() {
19132 let p = parse_ok("if (1) { 2 }");
19133 assert_eq!(p.statements.len(), 1);
19134 matches!(&p.statements[0].kind, StmtKind::If { .. });
19135 }
19136
19137 #[test]
19138 fn parse_if_elsif_else() {
19139 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
19140 assert_eq!(p.statements.len(), 1);
19141 }
19142
19143 #[test]
19144 fn parse_unless_statement() {
19145 let p = parse_ok("unless (0) { 1 }");
19146 assert_eq!(p.statements.len(), 1);
19147 }
19148
19149 #[test]
19150 fn parse_while_loop() {
19151 let p = parse_ok("while ($x) { $x-- }");
19152 assert_eq!(p.statements.len(), 1);
19153 }
19154
19155 #[test]
19156 fn parse_until_loop() {
19157 let p = parse_ok("until ($x) { $x++ }");
19158 assert_eq!(p.statements.len(), 1);
19159 }
19160
19161 #[test]
19162 fn parse_for_c_style() {
19163 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
19164 assert_eq!(p.statements.len(), 1);
19165 }
19166
19167 #[test]
19168 fn parse_foreach_loop() {
19169 let p = parse_ok("foreach my $x (@arr) { 1 }");
19170 assert_eq!(p.statements.len(), 1);
19171 }
19172
19173 #[test]
19174 fn parse_loop_with_label() {
19175 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
19176 assert_eq!(p.statements.len(), 1);
19177 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
19178 }
19179
19180 #[test]
19181 fn parse_begin_block() {
19182 let p = parse_ok("BEGIN { 1 }");
19183 assert_eq!(p.statements.len(), 1);
19184 matches!(&p.statements[0].kind, StmtKind::Begin(_));
19185 }
19186
19187 #[test]
19188 fn parse_end_block() {
19189 let p = parse_ok("END { 1 }");
19190 assert_eq!(p.statements.len(), 1);
19191 matches!(&p.statements[0].kind, StmtKind::End(_));
19192 }
19193
19194 #[test]
19195 fn parse_package_statement() {
19196 let p = parse_ok("package Foo::Bar");
19197 assert_eq!(p.statements.len(), 1);
19198 match &p.statements[0].kind {
19199 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
19200 _ => panic!("expected Package"),
19201 }
19202 }
19203
19204 #[test]
19205 fn parse_use_statement() {
19206 let p = parse_ok("use strict");
19207 assert_eq!(p.statements.len(), 1);
19208 }
19209
19210 #[test]
19211 fn parse_no_statement() {
19212 let p = parse_ok("no warnings");
19213 assert_eq!(p.statements.len(), 1);
19214 }
19215
19216 #[test]
19217 fn parse_require_bareword() {
19218 let p = parse_ok("require Foo::Bar");
19219 assert_eq!(p.statements.len(), 1);
19220 }
19221
19222 #[test]
19223 fn parse_require_string() {
19224 let p = parse_ok(r#"require "foo.pl""#);
19225 assert_eq!(p.statements.len(), 1);
19226 }
19227
19228 #[test]
19229 fn parse_eval_block() {
19230 let p = parse_ok("eval { 1 }");
19231 assert_eq!(p.statements.len(), 1);
19232 }
19233
19234 #[test]
19235 fn parse_eval_string() {
19236 let p = parse_ok(r#"eval "1 + 2""#);
19237 assert_eq!(p.statements.len(), 1);
19238 }
19239
19240 #[test]
19241 fn parse_qw_word_list() {
19242 let p = parse_ok("my @a = qw(foo bar baz)");
19243 assert_eq!(p.statements.len(), 1);
19244 }
19245
19246 #[test]
19247 fn parse_q_string() {
19248 let p = parse_ok("my $s = q{hello}");
19249 assert_eq!(p.statements.len(), 1);
19250 }
19251
19252 #[test]
19253 fn parse_qq_string() {
19254 let p = parse_ok(r#"my $s = qq(hello $x)"#);
19255 assert_eq!(p.statements.len(), 1);
19256 }
19257
19258 #[test]
19259 fn parse_regex_match() {
19260 let p = parse_ok(r#"$x =~ /foo/"#);
19261 assert_eq!(p.statements.len(), 1);
19262 }
19263
19264 #[test]
19265 fn parse_regex_substitution() {
19266 let p = parse_ok(r#"$x =~ s/foo/bar/g"#);
19267 assert_eq!(p.statements.len(), 1);
19268 }
19269
19270 #[test]
19271 fn parse_transliterate() {
19272 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/"#);
19273 assert_eq!(p.statements.len(), 1);
19274 }
19275
19276 #[test]
19277 fn parse_ternary_operator() {
19278 let p = parse_ok("my $x = $a ? 1 : 2");
19279 assert_eq!(p.statements.len(), 1);
19280 }
19281
19282 #[test]
19283 fn parse_arrow_method_call() {
19284 let p = parse_ok("$obj->method()");
19285 assert_eq!(p.statements.len(), 1);
19286 }
19287
19288 #[test]
19289 fn parse_arrow_deref_hash() {
19290 let p = parse_ok("$r->{key}");
19291 assert_eq!(p.statements.len(), 1);
19292 }
19293
19294 #[test]
19295 fn parse_arrow_deref_array() {
19296 let p = parse_ok("$r->[0]");
19297 assert_eq!(p.statements.len(), 1);
19298 }
19299
19300 #[test]
19301 fn parse_chained_arrow_deref() {
19302 let p = parse_ok("$r->{a}[0]{b}");
19303 assert_eq!(p.statements.len(), 1);
19304 }
19305
19306 #[test]
19307 fn parse_my_multiple_vars() {
19308 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3)");
19309 assert_eq!(p.statements.len(), 1);
19310 }
19311
19312 #[test]
19313 fn parse_our_scalar() {
19314 let p = parse_ok("our $VERSION = '1.0'");
19315 assert_eq!(p.statements.len(), 1);
19316 }
19317
19318 #[test]
19319 fn parse_local_scalar() {
19320 let p = parse_ok("local $/ = undef");
19321 assert_eq!(p.statements.len(), 1);
19322 }
19323
19324 #[test]
19325 fn parse_state_variable() {
19326 let p = parse_ok("fn Test::counter { state $n = 0; $n++ }");
19327 assert_eq!(p.statements.len(), 1);
19328 }
19329
19330 #[test]
19331 fn parse_postfix_if() {
19332 let p = parse_ok("print 1 if $x");
19333 assert_eq!(p.statements.len(), 1);
19334 }
19335
19336 #[test]
19337 fn parse_postfix_unless() {
19338 let p = parse_ok("die 'error' unless $ok");
19339 assert_eq!(p.statements.len(), 1);
19340 }
19341
19342 #[test]
19343 fn parse_postfix_while() {
19344 let p = parse_ok("$x++ while $x < 10");
19345 assert_eq!(p.statements.len(), 1);
19346 }
19347
19348 #[test]
19349 fn parse_postfix_for() {
19350 let p = parse_ok("print for @arr");
19351 assert_eq!(p.statements.len(), 1);
19352 }
19353
19354 #[test]
19355 fn parse_last_next_redo() {
19356 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
19357 assert_eq!(p.statements.len(), 1);
19358 }
19359
19360 #[test]
19361 fn parse_return_statement() {
19362 let p = parse_ok("fn foo { return 42 }");
19363 assert_eq!(p.statements.len(), 1);
19364 }
19365
19366 #[test]
19367 fn parse_wantarray() {
19368 let p = parse_ok("fn foo { wantarray ? @a : $a }");
19369 assert_eq!(p.statements.len(), 1);
19370 }
19371
19372 #[test]
19373 fn parse_caller_builtin() {
19374 let p = parse_ok("my @c = caller");
19375 assert_eq!(p.statements.len(), 1);
19376 }
19377
19378 #[test]
19379 fn parse_ref_to_array() {
19380 let p = parse_ok("my $r = \\@arr");
19381 assert_eq!(p.statements.len(), 1);
19382 }
19383
19384 #[test]
19385 fn parse_ref_to_hash() {
19386 let p = parse_ok("my $r = \\%hash");
19387 assert_eq!(p.statements.len(), 1);
19388 }
19389
19390 #[test]
19391 fn parse_ref_to_scalar() {
19392 let p = parse_ok("my $r = \\$x");
19393 assert_eq!(p.statements.len(), 1);
19394 }
19395
19396 #[test]
19397 fn parse_deref_scalar() {
19398 let p = parse_ok("my $v = $$r");
19399 assert_eq!(p.statements.len(), 1);
19400 }
19401
19402 #[test]
19403 fn parse_deref_array() {
19404 let p = parse_ok("my @a = @$r");
19405 assert_eq!(p.statements.len(), 1);
19406 }
19407
19408 #[test]
19409 fn parse_deref_hash() {
19410 let p = parse_ok("my %h = %$r");
19411 assert_eq!(p.statements.len(), 1);
19412 }
19413
19414 #[test]
19415 fn parse_blessed_ref() {
19416 let p = parse_ok("bless $r, 'Foo'");
19417 assert_eq!(p.statements.len(), 1);
19418 }
19419
19420 #[test]
19421 fn parse_heredoc_basic() {
19422 let p = parse_ok("my $s = <<END;\nfoo\nEND");
19423 assert_eq!(p.statements.len(), 1);
19424 }
19425
19426 #[test]
19427 fn parse_heredoc_quoted() {
19428 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
19429 assert_eq!(p.statements.len(), 1);
19430 }
19431
19432 #[test]
19433 fn parse_do_block() {
19434 let p = parse_ok("my $x = do { 1 + 2 }");
19435 assert_eq!(p.statements.len(), 1);
19436 }
19437
19438 #[test]
19439 fn parse_do_file() {
19440 let p = parse_ok(r#"do "foo.pl""#);
19441 assert_eq!(p.statements.len(), 1);
19442 }
19443
19444 #[test]
19445 fn parse_map_expression() {
19446 let p = parse_ok("my @b = map { $_ * 2 } @a");
19447 assert_eq!(p.statements.len(), 1);
19448 }
19449
19450 #[test]
19451 fn parse_grep_expression() {
19452 let p = parse_ok("my @b = grep { $_ > 0 } @a");
19453 assert_eq!(p.statements.len(), 1);
19454 }
19455
19456 #[test]
19457 fn parse_sort_expression() {
19458 let p = parse_ok("my @b = sort { $a <=> $b } @a");
19459 assert_eq!(p.statements.len(), 1);
19460 }
19461
19462 #[test]
19463 fn parse_pipe_forward() {
19464 let p = parse_ok("@a |> map { $_ * 2 }");
19465 assert_eq!(p.statements.len(), 1);
19466 }
19467
19468 #[test]
19469 fn parse_expression_from_str_simple() {
19470 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
19471 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
19472 }
19473
19474 #[test]
19475 fn parse_expression_from_str_extra_tokens_error() {
19476 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
19477 assert!(err.message.contains("Extra tokens"));
19478 }
19479
19480 #[test]
19481 fn parse_slice_indices_from_str_basic() {
19482 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
19483 assert_eq!(indices.len(), 3);
19484 }
19485
19486 #[test]
19487 fn parse_format_value_line_empty() {
19488 let exprs = parse_format_value_line("").unwrap();
19489 assert!(exprs.is_empty());
19490 }
19491
19492 #[test]
19493 fn parse_format_value_line_single() {
19494 let exprs = parse_format_value_line("$x").unwrap();
19495 assert_eq!(exprs.len(), 1);
19496 }
19497
19498 #[test]
19499 fn parse_format_value_line_multiple() {
19500 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
19501 assert_eq!(exprs.len(), 3);
19502 }
19503
19504 #[test]
19505 fn parse_unclosed_brace_error() {
19506 let err = parse_err("fn foo {");
19507 assert!(!err.is_empty());
19508 }
19509
19510 #[test]
19511 fn parse_unclosed_paren_error() {
19512 let err = parse_err("print (1, 2");
19513 assert!(!err.is_empty());
19514 }
19515
19516 #[test]
19517 fn parse_invalid_statement_error() {
19518 let err = parse_err("???");
19519 assert!(!err.is_empty());
19520 }
19521
19522 #[test]
19523 fn merge_expr_list_single() {
19524 let e = Expr {
19525 kind: ExprKind::Integer(1),
19526 line: 1,
19527 };
19528 let merged = merge_expr_list(vec![e.clone()]);
19529 matches!(merged.kind, ExprKind::Integer(1));
19530 }
19531
19532 #[test]
19533 fn merge_expr_list_multiple() {
19534 let e1 = Expr {
19535 kind: ExprKind::Integer(1),
19536 line: 1,
19537 };
19538 let e2 = Expr {
19539 kind: ExprKind::Integer(2),
19540 line: 1,
19541 };
19542 let merged = merge_expr_list(vec![e1, e2]);
19543 matches!(merged.kind, ExprKind::List(_));
19544 }
19545}