1use crate::ast::*;
2use crate::error::{ErrorKind, StrykeError, StrykeResult};
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}
81pub struct Parser {
83 tokens: Vec<(Token, usize)>,
85 pos: usize,
87 next_rate_limit_slot: u32,
89 suppress_indirect_paren_call: u32,
92 pipe_rhs_depth: u32,
98 block_depth: u32,
105 no_pipe_forward_depth: u32,
113 suppress_scalar_hash_brace: u32,
116 next_desugar_tmp: u32,
118 error_file: String,
120 declared_subs: std::collections::HashSet<String>,
122 suppress_parenless_call: u32,
126 pending_thread_input: Option<Expr>,
131 suppress_slash_as_div: u32,
134 pub suppress_m_regex: u32,
137 suppress_colon_range: u32,
141 suppress_tilde_range: u32,
147 thread_last_mode: bool,
150 pub parsing_module: bool,
153 list_construct_close_pos: Option<usize>,
161 pending_synthetic_subs: Vec<Statement>,
166 next_overload_anon_id: u32,
168 pub bare_positional_indices: std::collections::HashSet<usize>,
175 current_package: String,
181}
182
183impl Parser {
184 pub fn new(tokens: Vec<(Token, usize)>) -> Self {
186 Self::new_with_file(tokens, "-e")
187 }
188 pub fn new_with_file(tokens: Vec<(Token, usize)>, file: impl Into<String>) -> Self {
190 Self {
191 tokens,
192 pos: 0,
193 next_rate_limit_slot: 0,
194 suppress_indirect_paren_call: 0,
195 pipe_rhs_depth: 0,
196 no_pipe_forward_depth: 0,
197 suppress_scalar_hash_brace: 0,
198 next_desugar_tmp: 0,
199 error_file: file.into(),
200 declared_subs: std::collections::HashSet::new(),
201 suppress_parenless_call: 0,
202 pending_thread_input: None,
203 suppress_slash_as_div: 0,
204 suppress_m_regex: 0,
205 suppress_colon_range: 0,
206 suppress_tilde_range: 0,
207 thread_last_mode: false,
208 pending_synthetic_subs: Vec::new(),
209 next_overload_anon_id: 0,
210 parsing_module: false,
211 list_construct_close_pos: None,
212 bare_positional_indices: std::collections::HashSet::new(),
213 block_depth: 0,
214 current_package: "main".to_string(),
215 }
216 }
217
218 fn alloc_desugar_tmp(&mut self) -> u32 {
219 let n = self.next_desugar_tmp;
220 self.next_desugar_tmp = self.next_desugar_tmp.saturating_add(1);
221 n
222 }
223
224 #[inline]
228 fn in_pipe_rhs(&self) -> bool {
229 self.pipe_rhs_depth > 0
230 }
231
232 fn pipe_supplies_slurped_list_operand(&self) -> bool {
235 self.in_pipe_rhs()
236 && (matches!(
237 self.peek(),
238 Token::Semicolon
239 | Token::RBrace
240 | Token::RParen
241 | Token::Eof
242 | Token::Comma
243 | Token::PipeForward
244 ) || self.peek_line() > self.prev_line())
245 }
246
247 #[inline]
252 fn pipe_placeholder_list(&self, line: usize) -> Expr {
253 Expr {
254 kind: ExprKind::List(vec![]),
255 line,
256 }
257 }
258
259 fn is_block_then_list_pipe_builtin(name: &str) -> bool {
264 matches!(
265 name,
266 "pfirst"
267 | "pany"
268 | "any"
269 | "all"
270 | "none"
271 | "first"
272 | "find_index"
273 | "firstidx"
274 | "first_index"
275 | "take_while"
276 | "drop_while"
277 | "skip_while"
278 | "reject"
279 | "grepv"
280 | "tap"
281 | "peek"
282 | "group_by"
283 | "chunk_by"
284 | "partition"
285 | "min_by"
286 | "max_by"
287 | "zip_with"
288 | "count_by"
289 )
290 }
291
292 fn lift_bareword_to_topic_call(expr: Expr) -> Expr {
303 let line = expr.line;
304 let topic = || Expr {
305 kind: ExprKind::ScalarVar("_".into()),
306 line,
307 };
308 match expr.kind {
309 ExprKind::Bareword(ref name) => Expr {
310 kind: ExprKind::FuncCall {
311 name: name.clone(),
312 args: vec![topic()],
313 },
314 line,
315 },
316 ExprKind::Unlink(ref args) if args.is_empty() => Expr {
318 kind: ExprKind::Unlink(vec![topic()]),
319 line,
320 },
321 ExprKind::Chmod(ref args) if args.is_empty() => Expr {
322 kind: ExprKind::Chmod(vec![topic()]),
323 line,
324 },
325 ExprKind::Stat(_) => expr,
327 ExprKind::Lstat(_) => expr,
328 ExprKind::Readlink(_) => expr,
329 ExprKind::Rev(ref inner) => {
331 if matches!(inner.kind, ExprKind::List(ref v) if v.is_empty()) {
332 Expr {
333 kind: ExprKind::Rev(Box::new(topic())),
334 line,
335 }
336 } else {
337 expr
338 }
339 }
340 _ => expr,
341 }
342 }
343
344 fn parse_assign_expr_stop_at_pipe(&mut self) -> StrykeResult<Expr> {
352 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_add(1);
353 let r = self.parse_assign_expr();
354 self.no_pipe_forward_depth = self.no_pipe_forward_depth.saturating_sub(1);
355 r
356 }
357
358 fn syntax_err(&self, message: impl Into<String>, line: usize) -> StrykeError {
359 StrykeError::new(ErrorKind::Syntax, message, line, self.error_file.clone())
360 }
361
362 fn try_parse_coderef_listop_args(&mut self, line: usize) -> StrykeResult<Option<Vec<Expr>>> {
369 if !matches!(self.peek(), Token::ScalarVar(_) | Token::Backslash) {
370 return Ok(None);
371 }
372 let f = self.parse_assign_expr_stop_at_pipe()?;
373 let _ = self.eat(&Token::Comma);
374 let list = if self.in_pipe_rhs()
375 && matches!(
376 self.peek(),
377 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
378 ) {
379 self.pipe_placeholder_list(line)
380 } else {
381 self.parse_expression()?
382 };
383 Ok(Some(vec![f, list]))
384 }
385
386 fn alloc_rate_limit_slot(&mut self) -> u32 {
387 let s = self.next_rate_limit_slot;
388 self.next_rate_limit_slot = self.next_rate_limit_slot.saturating_add(1);
389 s
390 }
391
392 fn peek(&self) -> &Token {
393 self.tokens
394 .get(self.pos)
395 .map(|(t, _)| t)
396 .unwrap_or(&Token::Eof)
397 }
398
399 fn peek_line(&self) -> usize {
400 self.tokens.get(self.pos).map(|(_, l)| *l).unwrap_or(0)
401 }
402
403 fn peek_at(&self, offset: usize) -> &Token {
404 self.tokens
405 .get(self.pos + offset)
406 .map(|(t, _)| t)
407 .unwrap_or(&Token::Eof)
408 }
409
410 fn advance(&mut self) -> (Token, usize) {
411 let tok = self
412 .tokens
413 .get(self.pos)
414 .cloned()
415 .unwrap_or((Token::Eof, 0));
416 self.pos += 1;
417 tok
418 }
419
420 fn prev_line(&self) -> usize {
422 if self.pos > 0 {
423 self.tokens.get(self.pos - 1).map(|(_, l)| *l).unwrap_or(0)
424 } else {
425 0
426 }
427 }
428
429 fn looks_like_hashref(&self) -> bool {
438 debug_assert!(matches!(self.peek(), Token::LBrace));
439 let tok1 = self.peek_at(1);
440 let tok2 = self.peek_at(2);
441 match tok1 {
442 Token::RBrace => true,
443 Token::Ident(_)
444 | Token::SingleString(_)
445 | Token::DoubleString(_)
446 | Token::ScalarVar(_)
447 | Token::Integer(_) => matches!(tok2, Token::FatArrow),
448 Token::HashVar(_) => matches!(tok2, Token::RBrace | Token::Comma),
449 _ => false,
450 }
451 }
452
453 fn expect(&mut self, expected: &Token) -> StrykeResult<usize> {
454 let (tok, line) = self.advance();
455 if std::mem::discriminant(&tok) == std::mem::discriminant(expected) {
456 Ok(line)
457 } else {
458 Err(self.syntax_err(format!("Expected {:?}, got {:?}", expected, tok), line))
459 }
460 }
461
462 fn eat(&mut self, expected: &Token) -> bool {
463 if std::mem::discriminant(self.peek()) == std::mem::discriminant(expected) {
464 self.advance();
465 true
466 } else {
467 false
468 }
469 }
470
471 fn at_eof(&self) -> bool {
472 matches!(self.peek(), Token::Eof)
473 }
474
475 fn filetest_allows_implicit_topic(tok: &Token) -> bool {
477 matches!(
478 tok,
479 Token::RParen
480 | Token::Semicolon
481 | Token::Comma
482 | Token::RBrace
483 | Token::Eof
484 | Token::LogAnd
485 | Token::LogOr
486 | Token::LogAndWord
487 | Token::LogOrWord
488 | Token::PipeForward
489 )
490 }
491
492 fn next_is_new_stmt_keyword(&self, stmt_line: usize) -> bool {
496 if crate::compat_mode() {
498 return false;
499 }
500 if self.peek_line() == stmt_line {
501 return false;
502 }
503 matches!(
504 self.peek(),
505 Token::Ident(ref kw) if matches!(kw.as_str(),
506 "use" | "no" | "my" | "our" | "local" | "sub" | "struct" | "enum"
507 | "if" | "unless" | "while" | "until" | "for" | "foreach"
508 | "return" | "last" | "next" | "redo" | "package" | "require"
509 | "BEGIN" | "END" | "UNITCHECK" | "frozen" | "const" | "typed"
510 | "fn" | "class" | "abstract" | "final" | "trait"
515 | "state" | "mysync" | "oursync" | "var" | "val"
516 )
517 )
518 }
519
520 fn next_is_new_statement_start(&self, stmt_line: usize) -> bool {
524 if crate::compat_mode() {
525 return false;
526 }
527 if self.peek_line() == stmt_line {
528 return false;
529 }
530 matches!(
531 self.peek(),
532 Token::ScalarVar(_)
533 | Token::DerefScalarVar(_)
534 | Token::ArrayVar(_)
535 | Token::HashVar(_)
536 | Token::LBrace
537 ) || self.next_is_new_stmt_keyword(stmt_line)
538 }
539
540 pub fn parse_program(&mut self) -> StrykeResult<Program> {
543 let mut statements = self.parse_statements()?;
544 if !self.pending_synthetic_subs.is_empty() {
548 let synthetics = std::mem::take(&mut self.pending_synthetic_subs);
549 let mut combined = Vec::with_capacity(synthetics.len() + statements.len());
550 combined.extend(synthetics);
551 combined.append(&mut statements);
552 statements = combined;
553 }
554 Ok(Program { statements })
555 }
556
557 pub fn parse_statements(&mut self) -> StrykeResult<Vec<Statement>> {
559 let mut statements = Vec::new();
560 while !self.at_eof() {
561 if matches!(self.peek(), Token::Semicolon) {
562 let line = self.peek_line();
563 self.advance();
564 statements.push(Statement {
565 label: None,
566 kind: StmtKind::Empty,
567 line,
568 });
569 continue;
570 }
571 statements.push(self.parse_statement()?);
572 }
573 Ok(statements)
574 }
575
576 fn parse_statement(&mut self) -> StrykeResult<Statement> {
579 let line = self.peek_line();
580
581 let label = match self.peek().clone() {
584 Token::Ident(_) => {
585 if matches!(self.peek_at(1), Token::Colon)
586 && !matches!(self.peek_at(2), Token::Colon)
587 {
588 let (tok, _) = self.advance();
589 let l = match tok {
590 Token::Ident(l) => l,
591 _ => unreachable!(),
592 };
593 self.advance(); Some(l)
595 } else {
596 None
597 }
598 }
599 _ => None,
600 };
601
602 let mut stmt = match self.peek().clone() {
603 Token::FormatDecl { .. } => {
604 let tok_line = self.peek_line();
605 let (tok, _) = self.advance();
606 match tok {
607 Token::FormatDecl { name, lines } => Statement {
608 label: label.clone(),
609 kind: StmtKind::FormatDecl { name, lines },
610 line: tok_line,
611 },
612 _ => unreachable!(),
613 }
614 }
615 Token::Ident(ref kw) => match kw.as_str() {
616 "if" => self.parse_if()?,
617 "unless" => self.parse_unless()?,
618 "while" => {
619 let mut s = self.parse_while()?;
620 if let StmtKind::While {
621 label: ref mut lbl, ..
622 } = s.kind
623 {
624 *lbl = label.clone();
625 }
626 s
627 }
628 "until" => {
629 let mut s = self.parse_until()?;
630 if let StmtKind::Until {
631 label: ref mut lbl, ..
632 } = s.kind
633 {
634 *lbl = label.clone();
635 }
636 s
637 }
638 "for" => {
639 let mut s = self.parse_for_or_foreach()?;
640 match s.kind {
641 StmtKind::For {
642 label: ref mut lbl, ..
643 }
644 | StmtKind::Foreach {
645 label: ref mut lbl, ..
646 } => *lbl = label.clone(),
647 _ => {}
648 }
649 s
650 }
651 "foreach" => {
652 let mut s = self.parse_foreach()?;
653 if let StmtKind::Foreach {
654 label: ref mut lbl, ..
655 } = s.kind
656 {
657 *lbl = label.clone();
658 }
659 s
660 }
661 "sub" => {
662 if crate::no_interop_mode() {
663 return Err(self.syntax_err(
664 "stryke uses `fn` instead of `sub` (--no-interop is active)",
665 self.peek_line(),
666 ));
667 }
668 self.parse_sub_decl(true)?
669 }
670 "fn" => self.parse_sub_decl(false)?,
671 "struct" => {
672 if crate::compat_mode() {
673 return Err(self.syntax_err(
674 "`struct` is a stryke extension (disabled by --compat)",
675 self.peek_line(),
676 ));
677 }
678 self.parse_struct_decl()?
679 }
680 "enum" => {
681 if crate::compat_mode() {
682 return Err(self.syntax_err(
683 "`enum` is a stryke extension (disabled by --compat)",
684 self.peek_line(),
685 ));
686 }
687 self.parse_enum_decl()?
688 }
689 "class" => {
690 if crate::compat_mode() {
691 return Err(self.syntax_err(
693 "Perl 5.38 `class` syntax not yet implemented in --compat mode",
694 self.peek_line(),
695 ));
696 }
697 self.parse_class_decl(false, false)?
698 }
699 "abstract" => {
700 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
702 return Err(self.syntax_err(
703 "`abstract` must be followed by `class`",
704 self.peek_line(),
705 ));
706 }
707 self.parse_class_decl(true, false)?
708 }
709 "final" => {
710 self.advance(); if !matches!(self.peek(), Token::Ident(ref s) if s == "class") {
712 return Err(self
713 .syntax_err("`final` must be followed by `class`", self.peek_line()));
714 }
715 self.parse_class_decl(false, true)?
716 }
717 "trait" => {
718 if crate::compat_mode() {
719 return Err(self.syntax_err(
720 "`trait` is a stryke extension (disabled by --compat)",
721 self.peek_line(),
722 ));
723 }
724 self.parse_trait_decl()?
725 }
726 "my" => self.parse_my_our_local("my", false)?,
727 "var" => {
734 if crate::compat_mode() {
735 return Err(self.syntax_err(
736 "`var` is a stryke extension (disabled by --compat)",
737 self.peek_line(),
738 ));
739 }
740 self.parse_my_our_local("my", false)?
741 }
742 "val" => {
748 if crate::compat_mode() {
749 return Err(self.syntax_err(
750 "`val` is a stryke extension (disabled by --compat)",
751 self.peek_line(),
752 ));
753 }
754 let mut stmt = self.parse_my_our_local("my", true)?;
755 if let StmtKind::My(ref mut decls) = stmt.kind {
756 for decl in decls.iter_mut() {
757 decl.frozen = true;
758 }
759 }
760 stmt
761 }
762 "state" => self.parse_my_our_local("state", false)?,
763 "mysync" => {
764 if crate::compat_mode() {
765 return Err(self.syntax_err(
766 "`mysync` is a stryke extension (disabled by --compat)",
767 self.peek_line(),
768 ));
769 }
770 self.parse_my_our_local("mysync", false)?
771 }
772 "oursync" => {
773 if crate::compat_mode() {
774 return Err(self.syntax_err(
775 "`oursync` is a stryke extension (disabled by --compat)",
776 self.peek_line(),
777 ));
778 }
779 self.parse_my_our_local("oursync", false)?
780 }
781 "frozen" | "const" => {
782 let leading = kw.as_str().to_string();
783 if crate::compat_mode() {
784 return Err(self.syntax_err(
785 format!("`{leading}` is a stryke extension (disabled by --compat)"),
786 self.peek_line(),
787 ));
788 }
789 self.advance(); if let Token::Ident(ref kw) = self.peek().clone() {
795 if kw == "my" {
796 let mut stmt = self.parse_my_our_local("my", true)?;
801 if let StmtKind::My(ref mut decls) = stmt.kind {
802 for decl in decls.iter_mut() {
803 decl.frozen = true;
804 }
805 }
806 stmt
807 } else {
808 return Err(self.syntax_err(
809 format!("Expected 'my' after '{leading}'"),
810 self.peek_line(),
811 ));
812 }
813 } else {
814 return Err(self.syntax_err(
815 format!("Expected 'my' after '{leading}'"),
816 self.peek_line(),
817 ));
818 }
819 }
820 "typed" => {
821 if crate::compat_mode() {
822 return Err(self.syntax_err(
823 "`typed` is a stryke extension (disabled by --compat)",
824 self.peek_line(),
825 ));
826 }
827 self.advance();
828 if let Token::Ident(ref kw) = self.peek().clone() {
829 if kw == "my" {
830 self.parse_my_our_local("my", true)?
831 } else {
832 return Err(
833 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
834 );
835 }
836 } else {
837 return Err(
838 self.syntax_err("Expected 'my' after 'typed'", self.peek_line())
839 );
840 }
841 }
842 "our" => self.parse_my_our_local("our", false)?,
843 "local" => self.parse_my_our_local("local", false)?,
844 "package" => self.parse_package()?,
845 "use" => self.parse_use()?,
846 "no" => self.parse_no()?,
847 "return" => self.parse_return()?,
848 "last" => {
849 self.advance();
850 let lbl = self.try_take_loop_label();
851 let stmt = Statement {
852 label: None,
853 kind: StmtKind::Last(lbl.or(label.clone())),
854 line,
855 };
856 self.parse_stmt_postfix_modifier(stmt)?
857 }
858 "next" => {
859 self.advance();
860 let lbl = self.try_take_loop_label();
861 let stmt = Statement {
862 label: None,
863 kind: StmtKind::Next(lbl.or(label.clone())),
864 line,
865 };
866 self.parse_stmt_postfix_modifier(stmt)?
867 }
868 "redo" => {
869 self.advance();
870 let lbl = self.try_take_loop_label();
871 let stmt = Statement {
872 label: None,
873 kind: StmtKind::Redo(lbl.or(label.clone())),
874 line,
875 };
876 self.parse_stmt_postfix_modifier(stmt)?
877 }
878 "BEGIN" => {
879 self.advance();
880 let block = self.parse_block()?;
881 Statement {
882 label: None,
883 kind: StmtKind::Begin(block),
884 line,
885 }
886 }
887 "END" => {
888 self.advance();
889 let block = self.parse_block()?;
890 Statement {
891 label: None,
892 kind: StmtKind::End(block),
893 line,
894 }
895 }
896 "UNITCHECK" => {
897 self.advance();
898 let block = self.parse_block()?;
899 Statement {
900 label: None,
901 kind: StmtKind::UnitCheck(block),
902 line,
903 }
904 }
905 "CHECK" => {
906 self.advance();
907 let block = self.parse_block()?;
908 Statement {
909 label: None,
910 kind: StmtKind::Check(block),
911 line,
912 }
913 }
914 "INIT" => {
915 self.advance();
916 let block = self.parse_block()?;
917 Statement {
918 label: None,
919 kind: StmtKind::Init(block),
920 line,
921 }
922 }
923 "goto" => {
924 self.advance();
925 let target = self.parse_expression()?;
926 let stmt = Statement {
927 label: None,
928 kind: StmtKind::Goto {
929 target: Box::new(target),
930 },
931 line,
932 };
933 self.parse_stmt_postfix_modifier(stmt)?
935 }
936 "continue" => {
937 self.advance();
938 let block = self.parse_block()?;
939 Statement {
940 label: None,
941 kind: StmtKind::Continue(block),
942 line,
943 }
944 }
945 "before"
946 if matches!(
947 self.peek_at(1),
948 Token::SingleString(_) | Token::DoubleString(_)
949 ) =>
950 {
951 self.parse_advice_decl(crate::ast::AdviceKind::Before)?
952 }
953 "after"
954 if matches!(
955 self.peek_at(1),
956 Token::SingleString(_) | Token::DoubleString(_)
957 ) =>
958 {
959 self.parse_advice_decl(crate::ast::AdviceKind::After)?
960 }
961 "around"
962 if matches!(
963 self.peek_at(1),
964 Token::SingleString(_) | Token::DoubleString(_)
965 ) =>
966 {
967 self.parse_advice_decl(crate::ast::AdviceKind::Around)?
968 }
969 "try" => self.parse_try_catch()?,
970 "defer" => self.parse_defer_stmt()?,
971 "tie" => self.parse_tie_stmt()?,
972 "given" => self.parse_given()?,
973 "when" => self.parse_when_stmt()?,
974 "default" => self.parse_default_stmt()?,
975 "eval_timeout" => self.parse_eval_timeout()?,
976 "do" => {
977 if matches!(self.peek_at(1), Token::LBrace) {
978 self.advance();
979 let body = self.parse_block()?;
980 if let Token::Ident(ref w) = self.peek().clone() {
981 if w == "while" {
982 self.advance();
983 self.expect(&Token::LParen)?;
984 let mut condition = self.parse_expression()?;
985 Self::mark_match_scalar_g_for_boolean_condition(&mut condition);
986 self.expect(&Token::RParen)?;
987 self.eat(&Token::Semicolon);
988 Statement {
989 label: label.clone(),
990 kind: StmtKind::DoWhile { body, condition },
991 line,
992 }
993 } else {
994 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
995 let inner = Expr {
996 kind: ExprKind::CodeRef {
997 params: vec![],
998 body,
999 },
1000 line: inner_line,
1001 };
1002 let expr = Expr {
1003 kind: ExprKind::Do(Box::new(inner)),
1004 line,
1005 };
1006 let stmt = Statement {
1007 label: label.clone(),
1008 kind: StmtKind::Expression(expr),
1009 line,
1010 };
1011 self.parse_stmt_postfix_modifier(stmt)?
1013 }
1014 } else {
1015 let inner_line = body.first().map(|s| s.line).unwrap_or(line);
1016 let inner = Expr {
1017 kind: ExprKind::CodeRef {
1018 params: vec![],
1019 body,
1020 },
1021 line: inner_line,
1022 };
1023 let expr = Expr {
1024 kind: ExprKind::Do(Box::new(inner)),
1025 line,
1026 };
1027 let stmt = Statement {
1028 label: label.clone(),
1029 kind: StmtKind::Expression(expr),
1030 line,
1031 };
1032 self.parse_stmt_postfix_modifier(stmt)?
1033 }
1034 } else {
1035 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1036 let stmt = self.maybe_postfix_modifier(expr)?;
1037 self.parse_stmt_postfix_modifier(stmt)?
1038 } else {
1039 let expr = self.parse_expression()?;
1040 let stmt = self.maybe_postfix_modifier(expr)?;
1041 self.parse_stmt_postfix_modifier(stmt)?
1042 }
1043 }
1044 }
1045 _ => {
1046 if let Some(expr) = self.try_parse_bareword_stmt_call() {
1048 let stmt = self.maybe_postfix_modifier(expr)?;
1049 self.parse_stmt_postfix_modifier(stmt)?
1050 } else {
1051 let expr = self.parse_expression()?;
1052 let stmt = self.maybe_postfix_modifier(expr)?;
1053 self.parse_stmt_postfix_modifier(stmt)?
1054 }
1055 }
1056 },
1057 Token::LBrace => {
1058 if self.looks_like_hashref() {
1061 let expr = self.parse_expression()?;
1062 let stmt = self.maybe_postfix_modifier(expr)?;
1063 self.parse_stmt_postfix_modifier(stmt)?
1064 } else {
1065 let block = self.parse_block()?;
1066 let stmt = Statement {
1067 label: None,
1068 kind: StmtKind::Block(block),
1069 line,
1070 };
1071 self.parse_stmt_postfix_modifier(stmt)?
1073 }
1074 }
1075 _ => {
1076 let expr = self.parse_expression()?;
1077 let stmt = self.maybe_postfix_modifier(expr)?;
1078 self.parse_stmt_postfix_modifier(stmt)?
1079 }
1080 };
1081
1082 stmt.label = label;
1083 Ok(stmt)
1084 }
1085
1086 fn try_take_loop_label(&mut self) -> Option<String> {
1092 let Token::Ident(s) = self.peek() else {
1093 return None;
1094 };
1095 let mut chars = s.chars();
1096 let first = chars.next()?;
1097 if !(first.is_ascii_uppercase() || first == '_') {
1098 return None;
1099 }
1100 let ok = s
1101 .chars()
1102 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_');
1103 if !ok {
1104 return None;
1105 }
1106 let (Token::Ident(l), _) = self.advance() else {
1107 unreachable!()
1108 };
1109 Some(l)
1110 }
1111
1112 fn parse_stmt_postfix_modifier(&mut self, stmt: Statement) -> StrykeResult<Statement> {
1114 let line = stmt.line;
1115 if self.peek_line() > self.prev_line() {
1120 self.eat(&Token::Semicolon);
1121 return Ok(stmt);
1122 }
1123 if let Token::Ident(ref kw) = self.peek().clone() {
1124 match kw.as_str() {
1125 "if" => {
1126 self.advance();
1127 let mut cond = self.parse_expression()?;
1128 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1129 self.eat(&Token::Semicolon);
1130 return Ok(Statement {
1131 label: None,
1132 kind: StmtKind::If {
1133 condition: cond,
1134 body: vec![stmt],
1135 elsifs: vec![],
1136 else_block: None,
1137 },
1138 line,
1139 });
1140 }
1141 "unless" => {
1142 self.advance();
1143 let mut cond = self.parse_expression()?;
1144 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
1145 self.eat(&Token::Semicolon);
1146 return Ok(Statement {
1147 label: None,
1148 kind: StmtKind::Unless {
1149 condition: cond,
1150 body: vec![stmt],
1151 else_block: None,
1152 },
1153 line,
1154 });
1155 }
1156 "while" | "until" | "for" | "foreach" => {
1157 if let Some(expr) = Self::stmt_into_postfix_body_expr(stmt) {
1160 let out = self.maybe_postfix_modifier(expr)?;
1161 self.eat(&Token::Semicolon);
1162 return Ok(out);
1163 }
1164 return Err(self.syntax_err(
1165 format!("postfix `{}` is not supported on this statement form", kw),
1166 self.peek_line(),
1167 ));
1168 }
1169 "pmap" | "pflat_map" | "pgrep" | "pfor" | "preduce" | "pcache" | "par" => {
1171 let line = stmt.line;
1172 let block = self.stmt_into_parallel_block(stmt)?;
1173 let which = kw.as_str();
1174 self.advance();
1175 self.eat(&Token::Comma);
1176 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
1177 self.eat(&Token::Semicolon);
1178 let list = Box::new(list);
1179 let progress = progress.map(Box::new);
1180 let kind = match which {
1181 "pmap" => ExprKind::PMapExpr {
1182 block,
1183 list,
1184 progress,
1185 flat_outputs: false,
1186 on_cluster: None,
1187 stream: false,
1188 },
1189 "pflat_map" => ExprKind::PMapExpr {
1190 block,
1191 list,
1192 progress,
1193 flat_outputs: true,
1194 on_cluster: None,
1195 stream: false,
1196 },
1197 "pgrep" => ExprKind::PGrepExpr {
1198 block,
1199 list,
1200 progress,
1201 stream: false,
1202 },
1203 "pfor" => ExprKind::PForExpr {
1204 block,
1205 list,
1206 progress,
1207 },
1208 "preduce" => ExprKind::PReduceExpr {
1209 block,
1210 list,
1211 progress,
1212 },
1213 "pcache" => ExprKind::PcacheExpr {
1214 block,
1215 list,
1216 progress,
1217 },
1218 "par" => ExprKind::ParExpr { block, list },
1219 _ => unreachable!(),
1220 };
1221 return Ok(Statement {
1222 label: None,
1223 kind: StmtKind::Expression(Expr { kind, line }),
1224 line,
1225 });
1226 }
1227 _ => {}
1228 }
1229 }
1230 self.eat(&Token::Semicolon);
1231 Ok(stmt)
1232 }
1233
1234 fn stmt_into_parallel_block(&self, stmt: Statement) -> StrykeResult<Block> {
1237 let line = stmt.line;
1238 match stmt.kind {
1239 StmtKind::Block(block) => Ok(block),
1240 StmtKind::Expression(expr) => {
1241 if let ExprKind::Do(ref inner) = expr.kind {
1242 if let ExprKind::CodeRef { ref body, .. } = inner.kind {
1243 return Ok(body.clone());
1244 }
1245 }
1246 Ok(vec![Statement {
1247 label: None,
1248 kind: StmtKind::Expression(expr),
1249 line,
1250 }])
1251 }
1252 _ => Err(self.syntax_err(
1253 "postfix parallel op expects `do { }`, a bare `{ }` block, or an expression statement",
1254 line,
1255 )),
1256 }
1257 }
1258
1259 fn stmt_into_postfix_body_expr(stmt: Statement) -> Option<Expr> {
1262 match stmt.kind {
1263 StmtKind::Expression(expr) => Some(expr),
1264 StmtKind::Block(block) => {
1265 let line = stmt.line;
1266 let inner = Expr {
1267 kind: ExprKind::CodeRef {
1268 params: vec![],
1269 body: block,
1270 },
1271 line,
1272 };
1273 Some(Expr {
1274 kind: ExprKind::Do(Box::new(inner)),
1275 line,
1276 })
1277 }
1278 _ => None,
1279 }
1280 }
1281
1282 fn peek_is_postfix_stmt_modifier_keyword(&self) -> bool {
1285 matches!(
1286 self.peek(),
1287 Token::Ident(ref kw)
1288 if matches!(
1289 kw.as_str(),
1290 "if" | "unless" | "while" | "until" | "for" | "foreach"
1291 )
1292 )
1293 }
1294
1295 fn peek_is_named_unary_terminator(&self) -> bool {
1302 matches!(
1303 self.peek(),
1304 Token::Semicolon
1305 | Token::RBrace
1306 | Token::RParen
1307 | Token::RBracket
1308 | Token::Eof
1309 | Token::Comma
1310 | Token::FatArrow
1311 | Token::PipeForward
1312 | Token::Question
1313 | Token::Colon
1314 | Token::NumEq
1315 | Token::NumNe
1316 | Token::NumLt
1317 | Token::NumGt
1318 | Token::NumLe
1319 | Token::NumGe
1320 | Token::Spaceship
1321 | Token::StrEq
1322 | Token::StrNe
1323 | Token::StrLt
1324 | Token::StrGt
1325 | Token::StrLe
1326 | Token::StrGe
1327 | Token::StrCmp
1328 | Token::LogAnd
1329 | Token::LogOr
1330 | Token::LogAndWord
1331 | Token::LogOrWord
1332 | Token::DefinedOr
1333 | Token::Range
1334 | Token::RangeExclusive
1335 | Token::Assign
1336 | Token::PlusAssign
1337 | Token::MinusAssign
1338 | Token::MulAssign
1339 | Token::DivAssign
1340 | Token::ModAssign
1341 | Token::PowAssign
1342 | Token::DotAssign
1343 | Token::AndAssign
1344 | Token::OrAssign
1345 | Token::XorAssign
1346 | Token::DefinedOrAssign
1347 | Token::ShiftLeftAssign
1348 | Token::ShiftRightAssign
1349 | Token::BitAndAssign
1350 | Token::BitOrAssign
1351 )
1352 }
1353
1354 fn maybe_postfix_modifier(&mut self, expr: Expr) -> StrykeResult<Statement> {
1355 let line = expr.line;
1356 if self.peek_line() > self.prev_line() {
1358 return Ok(Statement {
1359 label: None,
1360 kind: StmtKind::Expression(expr),
1361 line,
1362 });
1363 }
1364 match self.peek() {
1365 Token::Ident(ref kw) => match kw.as_str() {
1366 "if" => {
1367 self.advance();
1368 let cond = self.parse_expression()?;
1369 Ok(Statement {
1370 label: None,
1371 kind: StmtKind::Expression(Expr {
1372 kind: ExprKind::PostfixIf {
1373 expr: Box::new(expr),
1374 condition: Box::new(cond),
1375 },
1376 line,
1377 }),
1378 line,
1379 })
1380 }
1381 "unless" => {
1382 self.advance();
1383 let cond = self.parse_expression()?;
1384 Ok(Statement {
1385 label: None,
1386 kind: StmtKind::Expression(Expr {
1387 kind: ExprKind::PostfixUnless {
1388 expr: Box::new(expr),
1389 condition: Box::new(cond),
1390 },
1391 line,
1392 }),
1393 line,
1394 })
1395 }
1396 "while" => {
1397 self.advance();
1398 let cond = self.parse_expression()?;
1399 Ok(Statement {
1400 label: None,
1401 kind: StmtKind::Expression(Expr {
1402 kind: ExprKind::PostfixWhile {
1403 expr: Box::new(expr),
1404 condition: Box::new(cond),
1405 },
1406 line,
1407 }),
1408 line,
1409 })
1410 }
1411 "until" => {
1412 self.advance();
1413 let cond = self.parse_expression()?;
1414 Ok(Statement {
1415 label: None,
1416 kind: StmtKind::Expression(Expr {
1417 kind: ExprKind::PostfixUntil {
1418 expr: Box::new(expr),
1419 condition: Box::new(cond),
1420 },
1421 line,
1422 }),
1423 line,
1424 })
1425 }
1426 "for" | "foreach" => {
1427 self.advance();
1428 let list = self.parse_expression()?;
1429 Ok(Statement {
1430 label: None,
1431 kind: StmtKind::Expression(Expr {
1432 kind: ExprKind::PostfixForeach {
1433 expr: Box::new(expr),
1434 list: Box::new(list),
1435 },
1436 line,
1437 }),
1438 line,
1439 })
1440 }
1441 _ => Ok(Statement {
1442 label: None,
1443 kind: StmtKind::Expression(expr),
1444 line,
1445 }),
1446 },
1447 _ => Ok(Statement {
1448 label: None,
1449 kind: StmtKind::Expression(expr),
1450 line,
1451 }),
1452 }
1453 }
1454
1455 fn try_parse_bareword_stmt_call(&mut self) -> Option<Expr> {
1457 let saved = self.pos;
1458 let line = self.peek_line();
1459 let mut name = match self.peek() {
1460 Token::Ident(n) => n.clone(),
1461 _ => return None,
1462 };
1463 if name.starts_with('\x00') || !Self::bareword_stmt_may_be_sub(&name) {
1465 return None;
1466 }
1467 self.advance();
1468 while self.eat(&Token::PackageSep) {
1469 match self.advance() {
1470 (Token::Ident(part), _) => {
1471 name = format!("{}::{}", name, part);
1472 }
1473 _ => {
1474 self.pos = saved;
1475 return None;
1476 }
1477 }
1478 }
1479 match self.peek() {
1480 Token::Semicolon | Token::RBrace => Some(Expr {
1481 kind: ExprKind::FuncCall { name, args: vec![] },
1482 line,
1483 }),
1484 _ => {
1485 self.pos = saved;
1486 None
1487 }
1488 }
1489 }
1490
1491 pub(crate) fn operator_keyword_to_ident_str(tok: &Token) -> Option<&'static str> {
1495 Some(match tok {
1496 Token::StrEq => "eq",
1497 Token::StrNe => "ne",
1498 Token::StrLt => "lt",
1499 Token::StrGt => "gt",
1500 Token::StrLe => "le",
1501 Token::StrGe => "ge",
1502 Token::StrCmp => "cmp",
1503 Token::LogAndWord => "and",
1504 Token::LogOrWord => "or",
1505 Token::LogNotWord => "not",
1506 Token::X => "x",
1507 _ => return None,
1508 })
1509 }
1510
1511 pub(crate) fn is_underscore_topic_slot(name: &str) -> bool {
1515 if name == "_" {
1516 return true;
1517 }
1518 if !name.starts_with('_') || name.len() < 2 {
1519 return false;
1520 }
1521 let bytes = name.as_bytes();
1522 let mut i = 1;
1523 while i < bytes.len() && bytes[i].is_ascii_digit() {
1525 i += 1;
1526 }
1527 let chevrons_start = i;
1529 while i < bytes.len() && bytes[i] == b'<' {
1530 i += 1;
1531 }
1532 i == bytes.len() && (i > 1 || chevrons_start > 1)
1534 }
1535
1536 pub(crate) fn is_reserved_special_var_name(name: &str) -> bool {
1546 matches!(
1547 name,
1548 "STDIN" | "STDOUT" | "STDERR" | "ARGV" | "ARGVOUT" | "DATA"
1550 | "ENV" | "INC" | "SIG" | "ISA"
1558 | "EXPORT" | "EXPORT_OK" | "EXPORT_TAGS"
1559 | "VERSION"
1560 | "__FILE__" | "__LINE__" | "__PACKAGE__" | "__SUB__"
1562 | "__DATA__" | "__END__"
1563 )
1564 }
1565
1566 fn bareword_stmt_may_be_sub(name: &str) -> bool {
1568 if Self::is_underscore_topic_slot(name) {
1573 return false;
1574 }
1575 !matches!(
1576 name,
1577 "__FILE__"
1578 | "__LINE__"
1579 | "__PACKAGE__"
1580 | "__SUB__"
1581 | "abs"
1582 | "async"
1583 | "spawn"
1584 | "atan2"
1585 | "await"
1586 | "barrier"
1587 | "bless"
1588 | "burp"
1589 | "caller"
1590 | "capture"
1591 | "cat"
1592 | "chdir"
1593 | "chmod"
1594 | "chomp"
1595 | "chop"
1596 | "chr"
1597 | "chown"
1598 | "closedir"
1599 | "close"
1600 | "collect"
1601 | "cos"
1602 | "crypt"
1603 | "defined"
1604 | "dec"
1605 | "delete"
1606 | "die"
1607 | "deque"
1608 | "do"
1609 | "each"
1610 | "eof"
1611 | "fore"
1612 | "eval"
1613 | "exec"
1614 | "exists"
1615 | "exit"
1616 | "exp"
1617 | "fan"
1618 | "fan_cap"
1619 | "fc"
1620 | "fetch_url"
1621 | "d"
1622 | "dirs"
1623 | "dr"
1624 | "f"
1625 | "fi"
1626 | "files"
1627 | "filesf"
1628 | "filter"
1629 | "fr"
1630 | "getcwd"
1631 | "glob_par"
1632 | "par_sed"
1633 | "glob"
1634 | "god"
1635 | "grep"
1636 | "greps"
1637 | "heap"
1638 | "hex"
1639 | "inc"
1640 | "index"
1641 | "int"
1642 | "join"
1643 | "keys"
1644 | "lcfirst"
1645 | "lc"
1646 | "length"
1647 | "link"
1648 | "log"
1649 | "lstat"
1650 | "map"
1651 | "flat_map"
1652 | "maps"
1653 | "flat_maps"
1654 | "flatten"
1655 | "frequencies"
1656 | "freq"
1657 | "pfrequencies"
1658 | "pfreq"
1659 | "interleave"
1660 | "ddump"
1661 | "stringify"
1662 | "str"
1663 | "s"
1664 | "input"
1665 | "lines"
1666 | "words"
1667 | "chars"
1668 | "digits"
1669 | "letters"
1670 | "letters_uc"
1671 | "letters_lc"
1672 | "punctuation"
1673 | "sentences"
1674 | "paragraphs"
1675 | "sections"
1676 | "numbers"
1677 | "graphemes"
1678 | "columns"
1679 | "trim"
1680 | "avg"
1681 | "top"
1682 | "pager"
1683 | "pg"
1684 | "less"
1685 | "count_by"
1686 | "to_file"
1687 | "to_json"
1688 | "to_csv"
1689 | "grep_v"
1690 | "select_keys"
1691 | "pluck"
1692 | "clamp"
1693 | "normalize"
1694 | "stddev"
1695 | "squared"
1696 | "square"
1697 | "cubed"
1698 | "cube"
1699 | "expt"
1700 | "pow"
1701 | "pw"
1702 | "snake_case"
1703 | "camel_case"
1704 | "kebab_case"
1705 | "to_toml"
1706 | "to_yaml"
1707 | "to_xml"
1708 | "to_html"
1709 | "to_markdown"
1710 | "xopen"
1711 | "clip"
1712 | "paste"
1713 | "to_table"
1714 | "sparkline"
1715 | "bar_chart"
1716 | "flame"
1717 | "set"
1718 | "list_count"
1719 | "list_size"
1720 | "count"
1721 | "size"
1722 | "cnt"
1723 | "len"
1724 | "all"
1725 | "any"
1726 | "none"
1727 | "take_while"
1728 | "drop_while"
1729 | "skip_while"
1730 | "skip"
1731 | "first_or"
1732 | "tap"
1733 | "peek"
1734 | "partition"
1735 | "min_by"
1736 | "max_by"
1737 | "zip_with"
1738 | "group_by"
1739 | "chunk_by"
1740 | "with_index"
1741 | "puniq"
1742 | "pfirst"
1743 | "pany"
1744 | "uniq"
1745 | "distinct"
1746 | "shuffle"
1747 | "shuffled"
1748 | "chunked"
1749 | "windowed"
1750 | "match"
1751 | "mkdir"
1752 | "every"
1753 | "gen"
1754 | "oct"
1755 | "open"
1756 | "p"
1757 | "opendir"
1758 | "ord"
1759 | "par"
1760 | "par_lines"
1761 | "par_walk"
1762 | "pipe"
1763 | "pipes"
1764 | "block_devices"
1765 | "char_devices"
1766 | "exe"
1767 | "executables"
1768 | "rate_limit"
1769 | "retry"
1770 | "pcache"
1771 | "pchannel"
1772 | "pfor"
1773 | "pgrep"
1774 | "pgreps"
1775 | "pipeline"
1776 | "pmap_chunked"
1777 | "pmap_reduce"
1778 | "par_reduce"
1779 | "pmap_on"
1780 | "pflat_map_on"
1781 | "pmap"
1782 | "pmaps"
1783 | "pflat_map"
1784 | "pflat_maps"
1785 | "pop"
1786 | "pos"
1787 | "ppool"
1788 | "preduce_init"
1789 | "preduce"
1790 | "pselect"
1791 | "printf"
1792 | "print"
1793 | "pr"
1794 | "psort"
1795 | "push"
1796 | "pwatch"
1797 | "rand"
1798 | "readdir"
1799 | "readlink"
1800 | "reduce"
1801 | "fold"
1802 | "inject"
1803 | "first"
1804 | "detect"
1805 | "find"
1806 | "find_all"
1807 | "find_index"
1808 | "firstidx"
1809 | "first_index"
1810 | "ref"
1811 | "rename"
1812 | "require"
1813 | "rev"
1814 | "reverse"
1815 | "reversed"
1816 | "rewinddir"
1817 | "rindex"
1818 | "rmdir"
1819 | "rm"
1820 | "say"
1821 | "scalar"
1822 | "seekdir"
1823 | "shift"
1824 | "sin"
1825 | "slurp"
1826 | "swallow"
1827 | "ingest"
1828 | "sockets"
1829 | "sort"
1830 | "splice"
1831 | "splice_last"
1832 | "splice1"
1833 | "spl_last"
1834 | "split"
1835 | "sprintf"
1836 | "sqrt"
1837 | "srand"
1838 | "stat"
1839 | "study"
1840 | "substr"
1841 | "symlink"
1842 | "sym_links"
1843 | "system"
1844 | "telldir"
1845 | "timer"
1846 | "trace"
1847 | "ucfirst"
1848 | "uc"
1849 | "undef"
1850 | "umask"
1851 | "unlink"
1852 | "unshift"
1853 | "utime"
1854 | "values"
1855 | "wantarray"
1856 | "warn"
1857 | "watch"
1858 | "yield"
1859 | "sub"
1860 )
1861 }
1862
1863 fn parse_block(&mut self) -> StrykeResult<Block> {
1864 self.expect(&Token::LBrace)?;
1865 let saved_pipe_rhs_depth = self.pipe_rhs_depth;
1868 self.pipe_rhs_depth = 0;
1869 self.block_depth += 1;
1870 let mut stmts = Vec::new();
1871 if let Some(param_stmts) = self.try_parse_block_params()? {
1875 stmts.extend(param_stmts);
1876 }
1877 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
1878 if self.eat(&Token::Semicolon) {
1879 continue;
1880 }
1881 stmts.push(self.parse_statement()?);
1882 }
1883 self.expect(&Token::RBrace)?;
1884 self.pipe_rhs_depth = saved_pipe_rhs_depth;
1885 self.block_depth -= 1;
1886 Self::default_topic_for_sole_bareword(&mut stmts);
1887 Ok(stmts)
1888 }
1889
1890 fn try_parse_block_params(&mut self) -> StrykeResult<Option<Vec<Statement>>> {
1895 if !matches!(self.peek(), Token::BitOr) {
1896 return Ok(None);
1897 }
1898 let mut i = 1; loop {
1901 match self.peek_at(i) {
1902 Token::ScalarVar(_) => i += 1,
1903 _ => return Ok(None), }
1905 match self.peek_at(i) {
1906 Token::BitOr => break, Token::Comma => i += 1, _ => return Ok(None), }
1910 }
1911 let line = self.peek_line();
1913 self.advance(); let mut names = Vec::new();
1915 loop {
1916 if let Token::ScalarVar(ref name) = self.peek().clone() {
1917 names.push(name.clone());
1918 self.advance();
1919 }
1920 if self.eat(&Token::BitOr) {
1921 break;
1922 }
1923 self.expect(&Token::Comma)?;
1924 }
1925 let sources: Vec<&str> = match names.len() {
1930 1 => vec!["_"],
1931 2 => vec!["a", "b"],
1932 n => {
1933 let _ = n;
1935 vec![] }
1937 };
1938 let mut stmts = Vec::with_capacity(names.len());
1939 if !sources.is_empty() {
1940 for (name, src) in names.iter().zip(sources.iter()) {
1941 stmts.push(Statement {
1942 label: None,
1943 kind: StmtKind::My(vec![VarDecl {
1944 sigil: Sigil::Scalar,
1945 name: name.clone(),
1946 initializer: Some(Expr {
1947 kind: ExprKind::ScalarVar(src.to_string()),
1948 line,
1949 }),
1950 frozen: false,
1951 type_annotation: None,
1952 list_context: false,
1953 }]),
1954 line,
1955 });
1956 }
1957 } else {
1958 for (idx, name) in names.iter().enumerate() {
1960 let src = if idx == 0 {
1961 "_".to_string()
1962 } else {
1963 format!("_{idx}")
1964 };
1965 stmts.push(Statement {
1966 label: None,
1967 kind: StmtKind::My(vec![VarDecl {
1968 sigil: Sigil::Scalar,
1969 name: name.clone(),
1970 initializer: Some(Expr {
1971 kind: ExprKind::ScalarVar(src),
1972 line,
1973 }),
1974 frozen: false,
1975 type_annotation: None,
1976 list_context: false,
1977 }]),
1978 line,
1979 });
1980 }
1981 }
1982 Ok(Some(stmts))
1983 }
1984
1985 fn default_topic_for_sole_bareword(stmts: &mut [Statement]) {
2000 let [only] = stmts else { return };
2001 let StmtKind::Expression(ref mut expr) = only.kind else {
2002 return;
2003 };
2004 let topic_line = expr.line;
2005 let topic_arg = || Expr {
2006 kind: ExprKind::ScalarVar("_".to_string()),
2007 line: topic_line,
2008 };
2009 match expr.kind {
2010 ExprKind::FuncCall {
2012 ref name,
2013 ref mut args,
2014 } if args.is_empty()
2015 && (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
2016 {
2017 args.push(topic_arg());
2018 }
2019 ExprKind::Bareword(ref name)
2023 if (Self::is_known_bareword(name) || Self::is_try_builtin_name(name)) =>
2024 {
2025 let n = name.clone();
2026 expr.kind = ExprKind::FuncCall {
2027 name: n,
2028 args: vec![topic_arg()],
2029 };
2030 }
2031 _ => {}
2032 }
2033 }
2034
2035 fn parse_defer_stmt(&mut self) -> StrykeResult<Statement> {
2039 let line = self.peek_line();
2040 self.advance(); let body = self.parse_block()?;
2042 self.eat(&Token::Semicolon);
2043 let coderef = Expr {
2045 kind: ExprKind::CodeRef {
2046 params: vec![],
2047 body,
2048 },
2049 line,
2050 };
2051 Ok(Statement {
2052 label: None,
2053 kind: StmtKind::Expression(Expr {
2054 kind: ExprKind::FuncCall {
2055 name: "defer__internal".to_string(),
2056 args: vec![coderef],
2057 },
2058 line,
2059 }),
2060 line,
2061 })
2062 }
2063
2064 fn parse_try_catch(&mut self) -> StrykeResult<Statement> {
2066 let line = self.peek_line();
2067 self.advance(); let try_block = self.parse_block()?;
2069 match self.peek() {
2070 Token::Ident(ref k) if k == "catch" => {
2071 self.advance();
2072 }
2073 _ => {
2074 return Err(self.syntax_err("expected 'catch' after try block", self.peek_line()));
2075 }
2076 }
2077 self.expect(&Token::LParen)?;
2078 let catch_var = self.parse_scalar_var_name()?;
2079 self.expect(&Token::RParen)?;
2080 let catch_block = self.parse_block()?;
2081 let finally_block = match self.peek() {
2082 Token::Ident(ref k) if k == "finally" => {
2083 self.advance();
2084 Some(self.parse_block()?)
2085 }
2086 _ => None,
2087 };
2088 self.eat(&Token::Semicolon);
2089 Ok(Statement {
2090 label: None,
2091 kind: StmtKind::TryCatch {
2092 try_block,
2093 catch_var,
2094 catch_block,
2095 finally_block,
2096 },
2097 line,
2098 })
2099 }
2100
2101 fn parse_thread_macro(&mut self, _line: usize, thread_last: bool) -> StrykeResult<Expr> {
2115 self.parse_thread_macro_inner(_line, thread_last, None)
2116 }
2117
2118 fn parse_thread_macro_inner(
2127 &mut self,
2128 _line: usize,
2129 thread_last: bool,
2130 mut parallel_collector: Option<&mut Vec<Expr>>,
2131 ) -> StrykeResult<Expr> {
2132 let saved_thread_last = self.thread_last_mode;
2134 self.thread_last_mode = thread_last;
2135
2136 let pipe_rhs_wrap = self.in_pipe_rhs();
2137 let mut result = if let Some(pre) = self.pending_thread_input.take() {
2141 pre
2142 } else if pipe_rhs_wrap {
2143 Expr {
2144 kind: ExprKind::ArrayElement {
2145 array: "_".to_string(),
2146 index: Box::new(Expr {
2147 kind: ExprKind::Integer(0),
2148 line: _line,
2149 }),
2150 },
2151 line: _line,
2152 }
2153 } else {
2154 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
2157 let expr = self.parse_thread_input();
2158 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
2159 expr?
2160 };
2161 let source_for_par = if parallel_collector.is_some() {
2165 let src = std::mem::replace(
2166 &mut result,
2167 Expr {
2168 kind: ExprKind::ScalarVar("_".into()),
2169 line: _line,
2170 },
2171 );
2172 Some(src)
2173 } else {
2174 None
2175 };
2176
2177 let mut last_stage_end_line = self.prev_line();
2179
2180 loop {
2182 if self.peek_line() > last_stage_end_line {
2187 break;
2188 }
2189
2190 match self.peek() {
2194 Token::Semicolon
2195 | Token::RBrace
2196 | Token::RParen
2197 | Token::RBracket
2198 | Token::PipeForward
2199 | Token::Eof
2200 | Token::ScalarVar(_)
2201 | Token::ArrayVar(_)
2202 | Token::HashVar(_)
2203 | Token::Comma => break,
2204 Token::LogOr if matches!(self.peek_at(1), Token::NumGt) => break,
2209 Token::BitOr
2211 if matches!(self.peek_at(1), Token::Ident(ref n) if n == "then")
2212 && matches!(self.peek_at(2), Token::BitOr) =>
2213 {
2214 break
2215 }
2216 Token::Ident(ref kw)
2217 if matches!(
2218 kw.as_str(),
2219 "my" | "var" | "val" | "our"
2220 | "local"
2221 | "state"
2222 | "if"
2223 | "unless"
2224 | "while"
2225 | "until"
2226 | "for"
2227 | "foreach"
2228 | "return"
2229 | "last"
2230 | "next"
2231 | "redo"
2232 ) =>
2233 {
2234 break
2235 }
2236 _ => {}
2237 }
2238
2239 let stage_line = self.peek_line();
2240
2241 match self.peek().clone() {
2243 Token::ArrowBrace => {
2245 self.advance(); let mut stmts = Vec::new();
2247 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
2248 if self.eat(&Token::Semicolon) {
2249 continue;
2250 }
2251 stmts.push(self.parse_statement()?);
2252 }
2253 self.expect(&Token::RBrace)?;
2254 let code_ref = Expr {
2255 kind: ExprKind::CodeRef {
2256 params: vec![],
2257 body: stmts,
2258 },
2259 line: stage_line,
2260 };
2261 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2262 }
2263 Token::Ident(ref name) if name == "sub" => {
2265 if crate::no_interop_mode() {
2266 return Err(self.syntax_err(
2267 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
2268 stage_line,
2269 ));
2270 }
2271 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2273 let body = self.parse_block()?;
2274 let code_ref = Expr {
2275 kind: ExprKind::CodeRef { params, body },
2276 line: stage_line,
2277 };
2278 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2279 }
2280 Token::Ident(ref name) if name == "fn" => {
2282 self.advance(); let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
2284 self.parse_sub_attributes()?;
2285 let body = self.parse_fn_eq_body_or_block(false)?;
2286 let code_ref = Expr {
2287 kind: ExprKind::CodeRef { params, body },
2288 line: stage_line,
2289 };
2290 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2291 }
2292 Token::Ident(ref name) => {
2294 let mut func_name = name.clone();
2295 self.advance();
2296
2297 while matches!(self.peek(), Token::PackageSep) {
2299 self.advance(); if let Token::Ident(ref part) = self.peek().clone() {
2301 func_name.push_str("::");
2302 func_name.push_str(part);
2303 self.advance();
2304 } else {
2305 return Err(self.syntax_err(
2306 format!(
2307 "Expected identifier after `::` in thread stage, got {:?}",
2308 self.peek()
2309 ),
2310 stage_line,
2311 ));
2312 }
2313 }
2314
2315 if func_name.starts_with('\x00') {
2317 let parts: Vec<&str> = func_name.split('\x00').collect();
2318 if parts.len() >= 4 && parts[1] == "s" {
2319 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2320 let stage = Expr {
2321 kind: ExprKind::Substitution {
2322 expr: Box::new(result.clone()),
2323 pattern: parts[2].to_string(),
2324 replacement: parts[3].to_string(),
2325 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2326 delim,
2327 },
2328 line: stage_line,
2329 };
2330 result = stage;
2331 last_stage_end_line = self.prev_line();
2332 continue;
2333 }
2334 if parts.len() >= 4 && parts[1] == "tr" {
2335 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
2336 let stage = Expr {
2337 kind: ExprKind::Transliterate {
2338 expr: Box::new(result.clone()),
2339 from: parts[2].to_string(),
2340 to: parts[3].to_string(),
2341 flags: format!("{}r", parts.get(4).unwrap_or(&"")),
2342 delim,
2343 },
2344 line: stage_line,
2345 };
2346 result = stage;
2347 last_stage_end_line = self.prev_line();
2348 continue;
2349 }
2350 return Err(
2351 self.syntax_err("Unexpected encoded token in thread", stage_line)
2352 );
2353 }
2354
2355 if matches!(self.peek(), Token::Plus)
2360 && matches!(self.peek_at(1), Token::LBrace)
2361 {
2362 self.advance(); self.expect(&Token::LBrace)?;
2364 let pairs = self.try_parse_hash_ref()?;
2366 let hashref_expr = Expr {
2367 kind: ExprKind::HashRef(pairs),
2368 line: stage_line,
2369 };
2370 let flatten_array_refs =
2371 matches!(func_name.as_str(), "flat_map" | "flat_maps");
2372 let stream = matches!(func_name.as_str(), "maps" | "flat_maps");
2373 let placeholder = Expr {
2375 kind: ExprKind::Undef,
2376 line: stage_line,
2377 };
2378 let map_node = Expr {
2379 kind: ExprKind::MapExprComma {
2380 expr: Box::new(hashref_expr),
2381 list: Box::new(placeholder),
2382 flatten_array_refs,
2383 stream,
2384 },
2385 line: stage_line,
2386 };
2387 result = self.pipe_forward_apply(result, map_node, stage_line)?;
2388 } else if func_name == "pmap_chunked" {
2390 let chunk_size = self.parse_assign_expr()?;
2391 let block = self.parse_block_or_bareword_block()?;
2392 let placeholder = self.pipe_placeholder_list(stage_line);
2393 let stage = Expr {
2394 kind: ExprKind::PMapChunkedExpr {
2395 chunk_size: Box::new(chunk_size),
2396 block,
2397 list: Box::new(placeholder),
2398 progress: None,
2399 },
2400 line: stage_line,
2401 };
2402 result = self.pipe_forward_apply(result, stage, stage_line)?;
2403 } else if func_name == "preduce_init" {
2405 let init = self.parse_assign_expr()?;
2406 let block = self.parse_block_or_bareword_block()?;
2407 let placeholder = self.pipe_placeholder_list(stage_line);
2408 let stage = Expr {
2409 kind: ExprKind::PReduceInitExpr {
2410 init: Box::new(init),
2411 block,
2412 list: Box::new(placeholder),
2413 progress: None,
2414 },
2415 line: stage_line,
2416 };
2417 result = self.pipe_forward_apply(result, stage, stage_line)?;
2418 } else if func_name == "pmap_reduce" {
2420 let map_block = self.parse_block_or_bareword_block()?;
2421 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2422 self.parse_block()?
2423 } else {
2424 self.expect(&Token::Comma)?;
2425 self.parse_block_or_bareword_cmp_block()?
2426 };
2427 let placeholder = self.pipe_placeholder_list(stage_line);
2428 let stage = Expr {
2429 kind: ExprKind::PMapReduceExpr {
2430 map_block,
2431 reduce_block,
2432 list: Box::new(placeholder),
2433 progress: None,
2434 },
2435 line: stage_line,
2436 };
2437 result = self.pipe_forward_apply(result, stage, stage_line)?;
2438 } else if func_name == "par_reduce" {
2443 let extract_block = self.parse_block_or_bareword_block()?;
2444 let reduce_block = if matches!(self.peek(), Token::LBrace) {
2445 Some(self.parse_block()?)
2446 } else {
2447 None
2448 };
2449 let placeholder = self.pipe_placeholder_list(stage_line);
2450 let stage = Expr {
2451 kind: ExprKind::ParReduceExpr {
2452 extract_block,
2453 reduce_block,
2454 list: Box::new(placeholder),
2455 },
2456 line: stage_line,
2457 };
2458 result = self.pipe_forward_apply(result, stage, stage_line)?;
2459 } else if func_name == "pmap_on" || func_name == "pflat_map_on" {
2464 self.suppress_scalar_hash_brace =
2467 self.suppress_scalar_hash_brace.saturating_add(1);
2468 let cluster = self.parse_assign_expr();
2469 self.suppress_scalar_hash_brace =
2470 self.suppress_scalar_hash_brace.saturating_sub(1);
2471 let cluster = cluster?;
2472 self.eat(&Token::Comma);
2475 let block = self.parse_block_or_bareword_block()?;
2476 let placeholder = self.pipe_placeholder_list(stage_line);
2477 let stage = Expr {
2478 kind: ExprKind::PMapExpr {
2479 block,
2480 list: Box::new(placeholder),
2481 progress: None,
2482 flat_outputs: func_name == "pflat_map_on",
2483 on_cluster: Some(Box::new(cluster)),
2484 stream: false,
2485 },
2486 line: stage_line,
2487 };
2488 result = self.pipe_forward_apply(result, stage, stage_line)?;
2489 } else if matches!(self.peek(), Token::LBrace) {
2491 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
2493 let stage = self.parse_thread_stage_with_block(&func_name, stage_line)?;
2494 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
2495 result = self.pipe_forward_apply(result, stage, stage_line)?;
2496 } else if matches!(self.peek(), Token::LParen) {
2497 if func_name == "join" {
2500 self.advance(); let separator = self.parse_assign_expr()?;
2502 self.expect(&Token::RParen)?;
2503 let placeholder = self.pipe_placeholder_list(stage_line);
2504 let stage = Expr {
2505 kind: ExprKind::JoinExpr {
2506 separator: Box::new(separator),
2507 list: Box::new(placeholder),
2508 },
2509 line: stage_line,
2510 };
2511 result = self.pipe_forward_apply(result, stage, stage_line)?;
2512 } else if func_name == "split" {
2513 self.advance(); let pattern = self.parse_assign_expr()?;
2515 let limit = if self.eat(&Token::Comma) {
2516 Some(Box::new(self.parse_assign_expr()?))
2517 } else {
2518 None
2519 };
2520 self.expect(&Token::RParen)?;
2521 let placeholder = Expr {
2522 kind: ExprKind::ScalarVar("_".to_string()),
2523 line: stage_line,
2524 };
2525 let stage = Expr {
2526 kind: ExprKind::SplitExpr {
2527 pattern: Box::new(pattern),
2528 string: Box::new(placeholder),
2529 limit,
2530 },
2531 line: stage_line,
2532 };
2533 result = self.pipe_forward_apply(result, stage, stage_line)?;
2534 } else {
2535 self.advance(); let mut call_args = Vec::new();
2546 while !matches!(self.peek(), Token::RParen | Token::Eof) {
2547 call_args.push(self.parse_assign_expr()?);
2548 if !self.eat(&Token::Comma) {
2549 break;
2550 }
2551 }
2552 self.expect(&Token::RParen)?;
2553 if !call_args.iter().any(Self::expr_contains_topic_var) {
2557 let topic = Expr {
2558 kind: ExprKind::ScalarVar("_".to_string()),
2559 line: stage_line,
2560 };
2561 if self.thread_last_mode {
2562 call_args.push(topic);
2563 } else {
2564 call_args.insert(0, topic);
2565 }
2566 }
2567 let call_expr = Expr {
2568 kind: ExprKind::FuncCall {
2569 name: func_name.clone(),
2570 args: call_args,
2571 },
2572 line: stage_line,
2573 };
2574 let code_ref = Expr {
2575 kind: ExprKind::CodeRef {
2576 params: vec![],
2577 body: vec![Statement {
2578 label: None,
2579 kind: StmtKind::Expression(call_expr),
2580 line: stage_line,
2581 }],
2582 },
2583 line: stage_line,
2584 };
2585 result = self.pipe_forward_apply(result, code_ref, stage_line)?;
2586 }
2587 } else {
2588 result = self.thread_apply_bare_func(&func_name, result, stage_line)?;
2590 }
2591 }
2592 Token::Regex(ref pattern, ref flags, delim) => {
2594 let pattern = pattern.clone();
2595 let flags = flags.clone();
2596 self.advance();
2597 result =
2598 self.thread_regex_grep_stage(result, pattern, flags, delim, stage_line);
2599 }
2600 Token::Slash => {
2603 self.advance(); if let Token::Ident(ref ident_s) = self.peek().clone() {
2609 if matches!(ident_s.as_str(), "m" | "s" | "tr" | "y" | "qr")
2610 && matches!(self.peek_at(1), Token::Regex(..))
2611 {
2612 self.advance(); if let Token::Regex(ref misparsed_pattern, ref misparsed_flags, _) =
2619 self.peek().clone()
2620 {
2621 let _ = (misparsed_pattern, misparsed_flags);
2631 }
2632 }
2633 }
2634
2635 let mut pattern = String::new();
2637 loop {
2638 match self.peek().clone() {
2639 Token::Slash => {
2640 self.advance(); break;
2642 }
2643 Token::Eof | Token::Semicolon | Token::Newline => {
2644 return Err(self
2645 .syntax_err("Unterminated regex in thread stage", stage_line));
2646 }
2647 Token::Regex(ref inner_pattern, ref inner_flags, delim) => {
2649 if pattern.is_empty()
2657 || matches!(pattern.as_str(), "m" | "s" | "tr" | "y" | "qr")
2658 {
2659 let _ = (inner_pattern, inner_flags, delim);
2665 }
2666 return Err(self.syntax_err(
2668 "Complex regex in thread stage - use m/pattern/ syntax instead",
2669 stage_line,
2670 ));
2671 }
2672 Token::Ident(ref s) => {
2673 pattern.push_str(s);
2674 self.advance();
2675 }
2676 Token::Integer(n) => {
2677 pattern.push_str(&n.to_string());
2678 self.advance();
2679 }
2680 Token::ScalarVar(ref v) => {
2681 pattern.push('$');
2682 pattern.push_str(v);
2683 self.advance();
2684 }
2685 Token::Dot => {
2686 pattern.push('.');
2687 self.advance();
2688 }
2689 Token::Star => {
2690 pattern.push('*');
2691 self.advance();
2692 }
2693 Token::Plus => {
2694 pattern.push('+');
2695 self.advance();
2696 }
2697 Token::Question => {
2698 pattern.push('?');
2699 self.advance();
2700 }
2701 Token::LParen => {
2702 pattern.push('(');
2703 self.advance();
2704 }
2705 Token::RParen => {
2706 pattern.push(')');
2707 self.advance();
2708 }
2709 Token::LBracket => {
2710 pattern.push('[');
2711 self.advance();
2712 }
2713 Token::RBracket => {
2714 pattern.push(']');
2715 self.advance();
2716 }
2717 Token::Backslash => {
2718 pattern.push('\\');
2719 self.advance();
2720 }
2721 Token::BitOr => {
2722 pattern.push('|');
2723 self.advance();
2724 }
2725 Token::Power => {
2726 pattern.push_str("**");
2727 self.advance();
2728 }
2729 Token::BitXor => {
2730 pattern.push('^');
2731 self.advance();
2732 }
2733 Token::Minus => {
2734 pattern.push('-');
2735 self.advance();
2736 }
2737 _ => {
2738 return Err(self.syntax_err(
2739 format!("Unexpected token in regex pattern: {:?}", self.peek()),
2740 stage_line,
2741 ));
2742 }
2743 }
2744 }
2745 let mut flags = String::new();
2749 if let Token::Ident(ref s) = self.peek().clone() {
2750 let is_flag_only =
2751 s.chars().all(|c| "gimsxecor".contains(c)) && s.len() <= 6;
2752 let followed_by_brace = matches!(self.peek_at(1), Token::LBrace);
2753 if is_flag_only && !followed_by_brace {
2754 flags.push_str(s);
2755 self.advance();
2756 }
2757 }
2758 result = self.thread_regex_grep_stage(result, pattern, flags, '/', stage_line);
2759 }
2760 tok => {
2761 return Err(self.syntax_err(
2762 format!(
2763 "thread: expected stage (ident, fn {{}}, s///, tr///, or /re/), got {:?}",
2764 tok
2765 ),
2766 stage_line,
2767 ));
2768 }
2769 };
2770 last_stage_end_line = self.prev_line();
2771 if let Some(stages) = parallel_collector.as_mut() {
2776 let stage_body = std::mem::replace(
2777 &mut result,
2778 Expr {
2779 kind: ExprKind::ScalarVar("_".into()),
2780 line: stage_line,
2781 },
2782 );
2783 stages.push(stage_body);
2784 }
2785 }
2786
2787 self.thread_last_mode = saved_thread_last;
2789
2790 if let Some(stages) = parallel_collector {
2796 let source_expr = source_for_par.unwrap_or(result);
2797 if stages.is_empty() {
2798 return Err(self.syntax_err(
2799 "~p> / ~p>> require at least one stage after the source",
2800 _line,
2801 ));
2802 }
2803 let stage_closures: Vec<Expr> = stages
2809 .drain(..)
2810 .map(|body| {
2811 let body_line = body.line;
2812 let wrapped = Expr {
2813 kind: ExprKind::ArrayRef(vec![body]),
2814 line: body_line,
2815 };
2816 Expr {
2817 kind: ExprKind::CodeRef {
2818 params: vec![],
2819 body: vec![Statement {
2820 label: None,
2821 kind: StmtKind::Expression(wrapped),
2822 line: body_line,
2823 }],
2824 },
2825 line: body_line,
2826 }
2827 })
2828 .collect();
2829 let stages_arr = Expr {
2830 kind: ExprKind::ArrayRef(stage_closures),
2831 line: _line,
2832 };
2833 let thread_last_flag = Expr {
2834 kind: ExprKind::Integer(if thread_last { 1 } else { 0 }),
2835 line: _line,
2836 };
2837 return Ok(Expr {
2844 kind: ExprKind::FuncCall {
2845 name: "_thread_par_run".into(),
2846 args: vec![stages_arr, thread_last_flag, source_expr],
2847 },
2848 line: _line,
2849 });
2850 }
2851
2852 if pipe_rhs_wrap {
2853 let body_line = result.line;
2856 return Ok(Expr {
2857 kind: ExprKind::CodeRef {
2858 params: vec![],
2859 body: vec![Statement {
2860 label: None,
2861 kind: StmtKind::Expression(result),
2862 line: body_line,
2863 }],
2864 },
2865 line: _line,
2866 });
2867 }
2868 Ok(result)
2869 }
2870
2871 fn thread_regex_grep_stage(
2873 &self,
2874 list: Expr,
2875 pattern: String,
2876 flags: String,
2877 delim: char,
2878 line: usize,
2879 ) -> Expr {
2880 let topic = Expr {
2881 kind: ExprKind::ScalarVar("_".to_string()),
2882 line,
2883 };
2884 let match_expr = Expr {
2885 kind: ExprKind::Match {
2886 expr: Box::new(topic),
2887 pattern,
2888 flags,
2889 scalar_g: false,
2890 delim,
2891 },
2892 line,
2893 };
2894 let block = vec![Statement {
2895 label: None,
2896 kind: StmtKind::Expression(match_expr),
2897 line,
2898 }];
2899 Expr {
2900 kind: ExprKind::GrepExpr {
2901 block,
2902 list: Box::new(list),
2903 keyword: crate::ast::GrepBuiltinKeyword::Grep,
2904 },
2905 line,
2906 }
2907 }
2908
2909 fn expr_contains_topic_var(e: &Expr) -> bool {
2920 format!("{:?}", e).contains("ScalarVar(\"_\")")
2921 }
2922
2923 fn rhs_has_free_bare_topic_slot(&self, rhs_start: usize, rhs_end: usize) -> bool {
2939 let end = rhs_end.min(self.tokens.len());
2940 if rhs_start < end && Self::is_thread_arrow(&self.tokens[rhs_start].0) {
2941 let input = rhs_start + 1;
2945 return input < end && self.bare_positional_indices.contains(&input);
2946 }
2947 let mut brace_depth = 0i32;
2948 for i in rhs_start..end {
2949 if brace_depth == 0 && self.bare_positional_indices.contains(&i) {
2950 return true;
2951 }
2952 match &self.tokens[i].0 {
2953 Token::LBrace | Token::ArrowBrace => brace_depth += 1,
2954 Token::RBrace => brace_depth -= 1,
2955 _ => {}
2956 }
2957 }
2958 false
2959 }
2960
2961 fn is_thread_arrow(tok: &Token) -> bool {
2962 matches!(
2963 tok,
2964 Token::ThreadArrow
2965 | Token::ThreadArrowLast
2966 | Token::ThreadArrowStream
2967 | Token::ThreadArrowStreamLast
2968 | Token::ThreadArrowPar
2969 | Token::ThreadArrowParLast
2970 | Token::ThreadArrowDist
2971 | Token::ThreadArrowDistLast
2972 )
2973 }
2974
2975 fn thread_apply_bare_func(&self, name: &str, arg: Expr, line: usize) -> StrykeResult<Expr> {
2977 let kind = match name {
2978 "uc" => ExprKind::Uc(Box::new(arg)),
2980 "lc" => ExprKind::Lc(Box::new(arg)),
2981 "ucfirst" | "ufc" => ExprKind::Ucfirst(Box::new(arg)),
2982 "lcfirst" | "lfc" => ExprKind::Lcfirst(Box::new(arg)),
2983 "fc" => ExprKind::Fc(Box::new(arg)),
2984 "chomp" => ExprKind::Chomp(Box::new(arg)),
2985 "chop" => ExprKind::Chop(Box::new(arg)),
2986 "length" => ExprKind::Length(Box::new(arg)),
2987 "len" | "cnt" => ExprKind::FuncCall {
2988 name: "count".to_string(),
2989 args: vec![arg],
2990 },
2991 "quotemeta" | "qm" => ExprKind::Quotemeta(Box::new(arg)),
2992 "abs" => ExprKind::Abs(Box::new(arg)),
2994 "int" => ExprKind::Int(Box::new(arg)),
2995 "sqrt" | "sq" => ExprKind::Sqrt(Box::new(arg)),
2996 "sin" => ExprKind::Sin(Box::new(arg)),
2997 "cos" => ExprKind::Cos(Box::new(arg)),
2998 "exp" => ExprKind::Exp(Box::new(arg)),
2999 "log" => ExprKind::Log(Box::new(arg)),
3000 "hex" => ExprKind::Hex(Box::new(arg)),
3001 "oct" => ExprKind::Oct(Box::new(arg)),
3002 "chr" => ExprKind::Chr(Box::new(arg)),
3003 "ord" => ExprKind::Ord(Box::new(arg)),
3004 "rand" => ExprKind::Rand(Some(Box::new(arg))),
3005 "srand" => ExprKind::Srand(Some(Box::new(arg))),
3006 "defined" | "def" => ExprKind::Defined(Box::new(arg)),
3008 "ref" => ExprKind::Ref(Box::new(arg)),
3009 "scalar" => {
3010 if crate::no_interop_mode() {
3011 return Err(self.syntax_err(
3012 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
3013 line,
3014 ));
3015 }
3016 ExprKind::ScalarContext(Box::new(arg))
3017 }
3018 "keys" => ExprKind::Keys(Box::new(arg)),
3020 "values" => ExprKind::Values(Box::new(arg)),
3021 "each" => ExprKind::Each(Box::new(arg)),
3022 "pop" => ExprKind::Pop(Box::new(arg)),
3023 "shift" => ExprKind::Shift(Box::new(arg)),
3024 "reverse" => {
3025 if crate::no_interop_mode() {
3026 return Err(self.syntax_err(
3027 "stryke uses `rev` instead of `reverse` (--no-interop)",
3028 line,
3029 ));
3030 }
3031 ExprKind::ReverseExpr(Box::new(arg))
3032 }
3033 "reversed" | "rv" | "rev" => ExprKind::Rev(Box::new(arg)),
3034 "sort" | "so" => ExprKind::SortExpr {
3035 cmp: None,
3036 list: Box::new(arg),
3037 },
3038 "psort" => ExprKind::PSortExpr {
3039 cmp: None,
3040 list: Box::new(arg),
3041 progress: None,
3042 },
3043 "uniq" | "distinct" | "uq" => ExprKind::FuncCall {
3044 name: "uniq".to_string(),
3045 args: vec![arg],
3046 },
3047 "trim" | "tm" => ExprKind::FuncCall {
3048 name: "trim".to_string(),
3049 args: vec![arg],
3050 },
3051 "flatten" | "fl" => ExprKind::FuncCall {
3052 name: "flatten".to_string(),
3053 args: vec![arg],
3054 },
3055 "compact" | "cpt" => ExprKind::FuncCall {
3056 name: "compact".to_string(),
3057 args: vec![arg],
3058 },
3059 "shuffle" | "shuf" => ExprKind::FuncCall {
3060 name: "shuffle".to_string(),
3061 args: vec![arg],
3062 },
3063 "frequencies" | "freq" | "frq" => ExprKind::FuncCall {
3064 name: "frequencies".to_string(),
3065 args: vec![arg],
3066 },
3067 "pfrequencies" | "pfreq" | "pfrq" => ExprKind::FuncCall {
3068 name: "pfrequencies".to_string(),
3069 args: vec![arg],
3070 },
3071 "dedup" | "dup" => ExprKind::FuncCall {
3072 name: "dedup".to_string(),
3073 args: vec![arg],
3074 },
3075 "enumerate" | "en" => ExprKind::FuncCall {
3076 name: "enumerate".to_string(),
3077 args: vec![arg],
3078 },
3079 "lines" | "ln" => ExprKind::FuncCall {
3080 name: "lines".to_string(),
3081 args: vec![arg],
3082 },
3083 "words" | "wd" => ExprKind::FuncCall {
3084 name: "words".to_string(),
3085 args: vec![arg],
3086 },
3087 "chars" | "ch" => ExprKind::FuncCall {
3088 name: "chars".to_string(),
3089 args: vec![arg],
3090 },
3091 "digits" | "dg" => ExprKind::FuncCall {
3092 name: "digits".to_string(),
3093 args: vec![arg],
3094 },
3095 "letters" | "lts" => ExprKind::FuncCall {
3096 name: "letters".to_string(),
3097 args: vec![arg],
3098 },
3099 "letters_uc" => ExprKind::FuncCall {
3100 name: "letters_uc".to_string(),
3101 args: vec![arg],
3102 },
3103 "letters_lc" => ExprKind::FuncCall {
3104 name: "letters_lc".to_string(),
3105 args: vec![arg],
3106 },
3107 "punctuation" | "punct" => ExprKind::FuncCall {
3108 name: "punctuation".to_string(),
3109 args: vec![arg],
3110 },
3111 "sentences" | "sents" => ExprKind::FuncCall {
3112 name: "sentences".to_string(),
3113 args: vec![arg],
3114 },
3115 "paragraphs" | "paras" => ExprKind::FuncCall {
3116 name: "paragraphs".to_string(),
3117 args: vec![arg],
3118 },
3119 "sections" | "sects" => ExprKind::FuncCall {
3120 name: "sections".to_string(),
3121 args: vec![arg],
3122 },
3123 "numbers" | "nums" => ExprKind::FuncCall {
3124 name: "numbers".to_string(),
3125 args: vec![arg],
3126 },
3127 "graphemes" | "grs" => ExprKind::FuncCall {
3128 name: "graphemes".to_string(),
3129 args: vec![arg],
3130 },
3131 "columns" | "cols" => ExprKind::FuncCall {
3132 name: "columns".to_string(),
3133 args: vec![arg],
3134 },
3135 "slurp" | "sl" => ExprKind::Slurp(Box::new(arg)),
3137 "swallow" | "swa" => ExprKind::Swallow(Box::new(arg)),
3138 "ingest" | "ing" => ExprKind::Ingest(Box::new(arg)),
3139 "burp" => ExprKind::Burp(Box::new(arg)),
3140 "god" => ExprKind::God(Box::new(arg)),
3141 "glob" => ExprKind::Glob(vec![arg]),
3142 "chdir" => ExprKind::Chdir(Box::new(arg)),
3143 "stat" => ExprKind::Stat(Box::new(arg)),
3144 "lstat" => ExprKind::Lstat(Box::new(arg)),
3145 "readlink" => ExprKind::Readlink(Box::new(arg)),
3146 "readdir" => ExprKind::Readdir(Box::new(arg)),
3147 "close" => ExprKind::Close(Box::new(arg)),
3148 "basename" | "bn" => ExprKind::FuncCall {
3149 name: "basename".to_string(),
3150 args: vec![arg],
3151 },
3152 "dirname" | "dn" => ExprKind::FuncCall {
3153 name: "dirname".to_string(),
3154 args: vec![arg],
3155 },
3156 "realpath" | "rp" => ExprKind::FuncCall {
3157 name: "realpath".to_string(),
3158 args: vec![arg],
3159 },
3160 "which" | "wh" => ExprKind::FuncCall {
3161 name: "which".to_string(),
3162 args: vec![arg],
3163 },
3164 "eval" => ExprKind::Eval(Box::new(arg)),
3166 "require" => ExprKind::Require(Box::new(arg)),
3167 "study" => ExprKind::Study(Box::new(arg)),
3168 "snake_case" | "sc" => ExprKind::FuncCall {
3170 name: "snake_case".to_string(),
3171 args: vec![arg],
3172 },
3173 "camel_case" | "cc" => ExprKind::FuncCall {
3174 name: "camel_case".to_string(),
3175 args: vec![arg],
3176 },
3177 "kebab_case" | "kc" => ExprKind::FuncCall {
3178 name: "kebab_case".to_string(),
3179 args: vec![arg],
3180 },
3181 "to_json" | "tj" => ExprKind::FuncCall {
3183 name: "to_json".to_string(),
3184 args: vec![arg],
3185 },
3186 "to_yaml" | "ty" => ExprKind::FuncCall {
3187 name: "to_yaml".to_string(),
3188 args: vec![arg],
3189 },
3190 "to_toml" | "tt" => ExprKind::FuncCall {
3191 name: "to_toml".to_string(),
3192 args: vec![arg],
3193 },
3194 "to_csv" | "tc" => ExprKind::FuncCall {
3195 name: "to_csv".to_string(),
3196 args: vec![arg],
3197 },
3198 "to_xml" | "tx" => ExprKind::FuncCall {
3199 name: "to_xml".to_string(),
3200 args: vec![arg],
3201 },
3202 "to_html" | "th" => ExprKind::FuncCall {
3203 name: "to_html".to_string(),
3204 args: vec![arg],
3205 },
3206 "to_markdown" | "to_md" | "tmd" => ExprKind::FuncCall {
3207 name: "to_markdown".to_string(),
3208 args: vec![arg],
3209 },
3210 "xopen" | "xo" => ExprKind::FuncCall {
3211 name: "xopen".to_string(),
3212 args: vec![arg],
3213 },
3214 "clip" | "clipboard" | "pbcopy" => ExprKind::FuncCall {
3215 name: "clip".to_string(),
3216 args: vec![arg],
3217 },
3218 "to_table" | "table" | "tbl" => ExprKind::FuncCall {
3219 name: "to_table".to_string(),
3220 args: vec![arg],
3221 },
3222 "sparkline" | "spark" => ExprKind::FuncCall {
3223 name: "sparkline".to_string(),
3224 args: vec![arg],
3225 },
3226 "bar_chart" | "bars" => ExprKind::FuncCall {
3227 name: "bar_chart".to_string(),
3228 args: vec![arg],
3229 },
3230 "flame" | "flamechart" => ExprKind::FuncCall {
3231 name: "flame".to_string(),
3232 args: vec![arg],
3233 },
3234 "ddump" | "dd" => ExprKind::FuncCall {
3235 name: "ddump".to_string(),
3236 args: vec![arg],
3237 },
3238 "say" => {
3239 if crate::no_interop_mode() {
3240 return Err(
3241 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
3242 );
3243 }
3244 ExprKind::Say {
3245 handle: None,
3246 args: vec![arg],
3247 }
3248 }
3249 "p" => ExprKind::Say {
3250 handle: None,
3251 args: vec![arg],
3252 },
3253 "print" => ExprKind::Print {
3254 handle: None,
3255 args: vec![arg],
3256 },
3257 "warn" => ExprKind::Warn(vec![arg]),
3258 "die" => ExprKind::Die(vec![arg]),
3259 "stringify" | "str" => ExprKind::FuncCall {
3260 name: "stringify".to_string(),
3261 args: vec![arg],
3262 },
3263 "json_decode" | "jd" => ExprKind::FuncCall {
3264 name: "json_decode".to_string(),
3265 args: vec![arg],
3266 },
3267 "yaml_decode" | "yd" => ExprKind::FuncCall {
3268 name: "yaml_decode".to_string(),
3269 args: vec![arg],
3270 },
3271 "toml_decode" | "td" => ExprKind::FuncCall {
3272 name: "toml_decode".to_string(),
3273 args: vec![arg],
3274 },
3275 "xml_decode" | "xd" => ExprKind::FuncCall {
3276 name: "xml_decode".to_string(),
3277 args: vec![arg],
3278 },
3279 "json_encode" | "je" => ExprKind::FuncCall {
3280 name: "json_encode".to_string(),
3281 args: vec![arg],
3282 },
3283 "yaml_encode" | "ye" => ExprKind::FuncCall {
3284 name: "yaml_encode".to_string(),
3285 args: vec![arg],
3286 },
3287 "toml_encode" | "te" => ExprKind::FuncCall {
3288 name: "toml_encode".to_string(),
3289 args: vec![arg],
3290 },
3291 "xml_encode" | "xe" => ExprKind::FuncCall {
3292 name: "xml_encode".to_string(),
3293 args: vec![arg],
3294 },
3295 "base64_encode" | "b64e" => ExprKind::FuncCall {
3297 name: "base64_encode".to_string(),
3298 args: vec![arg],
3299 },
3300 "base64_decode" | "b64d" => ExprKind::FuncCall {
3301 name: "base64_decode".to_string(),
3302 args: vec![arg],
3303 },
3304 "hex_encode" | "hxe" => ExprKind::FuncCall {
3305 name: "hex_encode".to_string(),
3306 args: vec![arg],
3307 },
3308 "hex_decode" | "hxd" => ExprKind::FuncCall {
3309 name: "hex_decode".to_string(),
3310 args: vec![arg],
3311 },
3312 "url_encode" | "uri_escape" | "ue" => ExprKind::FuncCall {
3313 name: "url_encode".to_string(),
3314 args: vec![arg],
3315 },
3316 "url_decode" | "uri_unescape" | "ud" => ExprKind::FuncCall {
3317 name: "url_decode".to_string(),
3318 args: vec![arg],
3319 },
3320 "gzip" | "gz" => ExprKind::FuncCall {
3321 name: "gzip".to_string(),
3322 args: vec![arg],
3323 },
3324 "gunzip" | "ugz" => ExprKind::FuncCall {
3325 name: "gunzip".to_string(),
3326 args: vec![arg],
3327 },
3328 "zstd" | "zst" => ExprKind::FuncCall {
3329 name: "zstd".to_string(),
3330 args: vec![arg],
3331 },
3332 "zstd_decode" | "uzst" => ExprKind::FuncCall {
3333 name: "zstd_decode".to_string(),
3334 args: vec![arg],
3335 },
3336 "sha256" | "s256" => ExprKind::FuncCall {
3338 name: "sha256".to_string(),
3339 args: vec![arg],
3340 },
3341 "sha1" | "s1" => ExprKind::FuncCall {
3342 name: "sha1".to_string(),
3343 args: vec![arg],
3344 },
3345 "md5" | "m5" => ExprKind::FuncCall {
3346 name: "md5".to_string(),
3347 args: vec![arg],
3348 },
3349 "uuid" | "uid" => ExprKind::FuncCall {
3350 name: "uuid".to_string(),
3351 args: vec![arg],
3352 },
3353 "datetime_utc" | "utc" => ExprKind::FuncCall {
3355 name: "datetime_utc".to_string(),
3356 args: vec![arg],
3357 },
3358 "e" | "fore" | "ep" => ExprKind::ForEachExpr {
3361 block: vec![Statement {
3362 label: None,
3363 kind: StmtKind::Expression(Expr {
3364 kind: ExprKind::Say {
3365 handle: None,
3366 args: vec![Expr {
3367 kind: ExprKind::ScalarVar("_".into()),
3368 line,
3369 }],
3370 },
3371 line,
3372 }),
3373 line,
3374 }],
3375 list: Box::new(arg),
3376 },
3377 _ => ExprKind::FuncCall {
3379 name: name.to_string(),
3380 args: vec![arg],
3381 },
3382 };
3383 Ok(Expr { kind, line })
3384 }
3385
3386 fn parse_thread_stage_with_block(&mut self, name: &str, line: usize) -> StrykeResult<Expr> {
3389 let block = self.parse_block()?;
3390 let placeholder = self.pipe_placeholder_list(line);
3392
3393 match name {
3394 "map" | "flat_map" | "maps" | "flat_maps" => {
3395 let flatten_array_refs = matches!(name, "flat_map" | "flat_maps");
3396 let stream = matches!(name, "maps" | "flat_maps");
3397 Ok(Expr {
3398 kind: ExprKind::MapExpr {
3399 block,
3400 list: Box::new(placeholder),
3401 flatten_array_refs,
3402 stream,
3403 },
3404 line,
3405 })
3406 }
3407 "grep" | "greps" | "filter" | "fi" | "find_all" | "gr" => {
3408 let keyword = match name {
3409 "grep" | "gr" => crate::ast::GrepBuiltinKeyword::Grep,
3410 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
3411 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
3412 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
3413 _ => unreachable!(),
3414 };
3415 Ok(Expr {
3416 kind: ExprKind::GrepExpr {
3417 block,
3418 list: Box::new(placeholder),
3419 keyword,
3420 },
3421 line,
3422 })
3423 }
3424 "sort" | "so" => Ok(Expr {
3425 kind: ExprKind::SortExpr {
3426 cmp: Some(SortComparator::Block(block)),
3427 list: Box::new(placeholder),
3428 },
3429 line,
3430 }),
3431 "reduce" | "rd" => Ok(Expr {
3432 kind: ExprKind::ReduceExpr {
3433 block,
3434 list: Box::new(placeholder),
3435 },
3436 line,
3437 }),
3438 "fore" | "e" | "ep" => Ok(Expr {
3439 kind: ExprKind::ForEachExpr {
3440 block,
3441 list: Box::new(placeholder),
3442 },
3443 line,
3444 }),
3445 "par" => Ok(Expr {
3446 kind: ExprKind::ParExpr {
3447 block,
3448 list: Box::new(placeholder),
3449 },
3450 line,
3451 }),
3452 "pmap" | "pflat_map" | "pmaps" | "pflat_maps" => Ok(Expr {
3453 kind: ExprKind::PMapExpr {
3454 block,
3455 list: Box::new(placeholder),
3456 progress: None,
3457 flat_outputs: name == "pflat_map" || name == "pflat_maps",
3458 on_cluster: None,
3459 stream: name == "pmaps" || name == "pflat_maps",
3460 },
3461 line,
3462 }),
3463 "pgrep" | "pgreps" => Ok(Expr {
3464 kind: ExprKind::PGrepExpr {
3465 block,
3466 list: Box::new(placeholder),
3467 progress: None,
3468 stream: name == "pgreps",
3469 },
3470 line,
3471 }),
3472 "pfor" => Ok(Expr {
3473 kind: ExprKind::PForExpr {
3474 block,
3475 list: Box::new(placeholder),
3476 progress: None,
3477 },
3478 line,
3479 }),
3480 "preduce" => Ok(Expr {
3481 kind: ExprKind::PReduceExpr {
3482 block,
3483 list: Box::new(placeholder),
3484 progress: None,
3485 },
3486 line,
3487 }),
3488 "pcache" => Ok(Expr {
3489 kind: ExprKind::PcacheExpr {
3490 block,
3491 list: Box::new(placeholder),
3492 progress: None,
3493 },
3494 line,
3495 }),
3496 "psort" => Ok(Expr {
3497 kind: ExprKind::PSortExpr {
3498 cmp: Some(block),
3499 list: Box::new(placeholder),
3500 progress: None,
3501 },
3502 line,
3503 }),
3504 _ => {
3505 let code_ref = Expr {
3512 kind: ExprKind::CodeRef {
3513 params: vec![],
3514 body: block,
3515 },
3516 line,
3517 };
3518 let args = if Self::is_block_then_list_pipe_builtin(name) {
3519 vec![code_ref, placeholder]
3520 } else {
3521 vec![code_ref]
3522 };
3523 Ok(Expr {
3524 kind: ExprKind::FuncCall {
3525 name: name.to_string(),
3526 args,
3527 },
3528 line,
3529 })
3530 }
3531 }
3532 }
3533
3534 fn parse_tie_stmt(&mut self) -> StrykeResult<Statement> {
3536 let line = self.peek_line();
3537 self.advance(); let mut implicit_decl: Option<Statement> = None;
3544 if let Token::Ident(kw) = self.peek().clone() {
3545 if matches!(kw.as_str(), "my" | "var" | "val" | "our") {
3546 let kw_line = self.peek_line();
3547 self.advance(); let (decl_sigil, decl_name) = match self.peek().clone() {
3550 Token::ScalarVar(s) => (Sigil::Scalar, s),
3551 Token::ArrayVar(a) => (Sigil::Array, a),
3552 Token::HashVar(h) => (Sigil::Hash, h),
3553 tok => {
3554 return Err(self.syntax_err(
3555 format!("expected variable after `tie {}`, got {:?}", kw, tok),
3556 self.peek_line(),
3557 ));
3558 }
3559 };
3560 let decls = vec![VarDecl {
3561 sigil: decl_sigil,
3562 name: decl_name.clone(),
3563 initializer: None,
3564 frozen: false,
3565 type_annotation: None,
3566 list_context: false,
3567 }];
3568 implicit_decl = Some(Statement {
3569 label: None,
3570 kind: if kw == "my" {
3571 StmtKind::My(decls)
3572 } else {
3573 StmtKind::Our(decls)
3574 },
3575 line: kw_line,
3576 });
3577 }
3582 }
3583 let target = match self.peek().clone() {
3584 Token::HashVar(h) => {
3585 self.advance();
3586 TieTarget::Hash(h)
3587 }
3588 Token::ArrayVar(a) => {
3589 self.advance();
3590 TieTarget::Array(a)
3591 }
3592 Token::ScalarVar(s) => {
3593 self.advance();
3594 TieTarget::Scalar(s)
3595 }
3596 tok => {
3597 return Err(self.syntax_err(
3598 format!("tie expects $scalar, @array, or %hash, got {:?}", tok),
3599 self.peek_line(),
3600 ));
3601 }
3602 };
3603 self.expect(&Token::Comma)?;
3604 let class = self.parse_assign_expr()?;
3605 let mut args = Vec::new();
3606 while self.eat(&Token::Comma) {
3607 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof) {
3608 break;
3609 }
3610 args.push(self.parse_assign_expr()?);
3611 }
3612 self.eat(&Token::Semicolon);
3613 let tie_stmt = Statement {
3614 label: None,
3615 kind: StmtKind::Tie {
3616 target,
3617 class,
3618 args,
3619 },
3620 line,
3621 };
3622 if let Some(decl) = implicit_decl {
3623 Ok(Statement {
3628 label: None,
3629 kind: StmtKind::StmtGroup(vec![decl, tie_stmt]),
3630 line,
3631 })
3632 } else {
3633 Ok(tie_stmt)
3634 }
3635 }
3636
3637 fn parse_given(&mut self) -> StrykeResult<Statement> {
3639 let line = self.peek_line();
3640 self.advance();
3641 self.expect(&Token::LParen)?;
3642 let topic = self.parse_expression()?;
3643 self.expect(&Token::RParen)?;
3644 let body = self.parse_block()?;
3645 self.eat(&Token::Semicolon);
3646 Ok(Statement {
3647 label: None,
3648 kind: StmtKind::Given { topic, body },
3649 line,
3650 })
3651 }
3652
3653 fn parse_when_stmt(&mut self) -> StrykeResult<Statement> {
3655 let line = self.peek_line();
3656 self.advance();
3657 self.expect(&Token::LParen)?;
3658 let cond = self.parse_expression()?;
3659 self.expect(&Token::RParen)?;
3660 let body = self.parse_block()?;
3661 self.eat(&Token::Semicolon);
3662 Ok(Statement {
3663 label: None,
3664 kind: StmtKind::When { cond, body },
3665 line,
3666 })
3667 }
3668
3669 fn parse_default_stmt(&mut self) -> StrykeResult<Statement> {
3671 let line = self.peek_line();
3672 self.advance();
3673 let body = self.parse_block()?;
3674 self.eat(&Token::Semicolon);
3675 Ok(Statement {
3676 label: None,
3677 kind: StmtKind::DefaultCase { body },
3678 line,
3679 })
3680 }
3681
3682 fn parse_cond_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3688 self.expect(&Token::LBrace)?;
3689
3690 let mut arms: Vec<(Expr, Block)> = Vec::new();
3691 let mut else_block: Option<Block> = None;
3692
3693 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3694 let arm_line = self.peek_line();
3695
3696 let is_default = matches!(self.peek(), Token::Ident(ref s) if s == "default")
3698 && matches!(self.peek_at(1), Token::FatArrow);
3699
3700 if is_default {
3701 self.advance(); self.advance(); let body = if matches!(self.peek(), Token::LBrace) {
3704 self.parse_block()?
3705 } else {
3706 let expr = self.parse_assign_expr()?;
3707 vec![Statement {
3708 label: None,
3709 kind: StmtKind::Expression(expr),
3710 line: arm_line,
3711 }]
3712 };
3713 else_block = Some(body);
3714 self.eat(&Token::Comma);
3715 break; }
3717
3718 let condition = self.parse_assign_expr()?;
3720 self.expect(&Token::FatArrow)?;
3721
3722 let body = if matches!(self.peek(), Token::LBrace) {
3723 self.parse_block()?
3724 } else {
3725 let expr = self.parse_assign_expr()?;
3726 vec![Statement {
3727 label: None,
3728 kind: StmtKind::Expression(expr),
3729 line: arm_line,
3730 }]
3731 };
3732
3733 arms.push((condition, body));
3734 self.eat(&Token::Comma);
3735 }
3736
3737 self.expect(&Token::RBrace)?;
3738
3739 if arms.is_empty() {
3740 return Err(self.syntax_err("cond requires at least one condition arm", line));
3741 }
3742
3743 let (first_cond, first_body) = arms.remove(0);
3745 let elsifs: Vec<(Expr, Block)> = arms;
3746
3747 let if_stmt = Statement {
3749 label: None,
3750 kind: StmtKind::If {
3751 condition: first_cond,
3752 body: first_body,
3753 elsifs,
3754 else_block,
3755 },
3756 line,
3757 };
3758 let inner = Expr {
3759 kind: ExprKind::CodeRef {
3760 params: vec![],
3761 body: vec![if_stmt],
3762 },
3763 line,
3764 };
3765 Ok(Expr {
3766 kind: ExprKind::Do(Box::new(inner)),
3767 line,
3768 })
3769 }
3770
3771 fn parse_algebraic_match_expr(&mut self, line: usize) -> StrykeResult<Expr> {
3773 self.expect(&Token::LParen)?;
3774 let subject = self.parse_expression()?;
3775 self.expect(&Token::RParen)?;
3776 self.expect(&Token::LBrace)?;
3777 let mut arms = Vec::new();
3778 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3779 if self.eat(&Token::Semicolon) {
3780 continue;
3781 }
3782 let pattern = self.parse_match_pattern()?;
3783 let guard = if matches!(self.peek(), Token::Ident(ref s) if s == "if") {
3784 self.advance();
3785 Some(Box::new(self.parse_assign_expr()?))
3788 } else {
3789 None
3790 };
3791 self.expect(&Token::FatArrow)?;
3792 let body = self.parse_assign_expr()?;
3794 arms.push(MatchArm {
3795 pattern,
3796 guard,
3797 body,
3798 });
3799 self.eat(&Token::Comma);
3800 }
3801 self.expect(&Token::RBrace)?;
3802 Ok(Expr {
3803 kind: ExprKind::AlgebraicMatch {
3804 subject: Box::new(subject),
3805 arms,
3806 },
3807 line,
3808 })
3809 }
3810
3811 fn parse_match_pattern(&mut self) -> StrykeResult<MatchPattern> {
3812 match self.peek().clone() {
3813 Token::Regex(pattern, flags, _delim) => {
3814 self.advance();
3815 Ok(MatchPattern::Regex { pattern, flags })
3816 }
3817 Token::Ident(ref s) if s == "_" => {
3818 self.advance();
3819 Ok(MatchPattern::Any)
3820 }
3821 Token::Ident(ref s) if s == "Some" => {
3822 self.advance();
3823 self.expect(&Token::LParen)?;
3824 let name = self.parse_scalar_var_name()?;
3825 self.expect(&Token::RParen)?;
3826 Ok(MatchPattern::OptionSome(name))
3827 }
3828 Token::LBracket => self.parse_match_array_pattern(),
3829 Token::LBrace => self.parse_match_hash_pattern(),
3830 Token::LParen => {
3831 self.advance();
3832 let e = self.parse_expression()?;
3833 self.expect(&Token::RParen)?;
3834 Ok(MatchPattern::Value(Box::new(e)))
3835 }
3836 _ => {
3837 let e = self.parse_assign_expr()?;
3838 Ok(MatchPattern::Value(Box::new(e)))
3839 }
3840 }
3841 }
3842
3843 fn parse_match_array_elems_until_rbracket(&mut self) -> StrykeResult<Vec<MatchArrayElem>> {
3845 let mut elems = Vec::new();
3846 if self.eat(&Token::RBracket) {
3847 return Ok(vec![]);
3848 }
3849 loop {
3850 if matches!(self.peek(), Token::Star) {
3851 self.advance();
3852 elems.push(MatchArrayElem::Rest);
3853 self.eat(&Token::Comma);
3854 if !matches!(self.peek(), Token::RBracket) {
3855 return Err(self.syntax_err(
3856 "`*` must be the last element in an array match pattern",
3857 self.peek_line(),
3858 ));
3859 }
3860 self.expect(&Token::RBracket)?;
3861 return Ok(elems);
3862 }
3863 if let Token::ArrayVar(name) = self.peek().clone() {
3864 self.advance();
3865 elems.push(MatchArrayElem::RestBind(name));
3866 self.eat(&Token::Comma);
3867 if !matches!(self.peek(), Token::RBracket) {
3868 return Err(self.syntax_err(
3869 "`@name` rest bind must be the last element in an array match pattern",
3870 self.peek_line(),
3871 ));
3872 }
3873 self.expect(&Token::RBracket)?;
3874 return Ok(elems);
3875 }
3876 if let Token::ScalarVar(name) = self.peek().clone() {
3877 self.advance();
3878 elems.push(MatchArrayElem::CaptureScalar(name));
3879 if self.eat(&Token::Comma) {
3880 if matches!(self.peek(), Token::RBracket) {
3881 break;
3882 }
3883 continue;
3884 }
3885 break;
3886 }
3887 let e = self.parse_assign_expr()?;
3888 elems.push(MatchArrayElem::Expr(e));
3889 if self.eat(&Token::Comma) {
3890 if matches!(self.peek(), Token::RBracket) {
3891 break;
3892 }
3893 continue;
3894 }
3895 break;
3896 }
3897 self.expect(&Token::RBracket)?;
3898 Ok(elems)
3899 }
3900
3901 fn parse_match_array_pattern(&mut self) -> StrykeResult<MatchPattern> {
3902 self.expect(&Token::LBracket)?;
3903 let elems = self.parse_match_array_elems_until_rbracket()?;
3904 Ok(MatchPattern::Array(elems))
3905 }
3906
3907 fn parse_match_hash_pattern(&mut self) -> StrykeResult<MatchPattern> {
3908 self.expect(&Token::LBrace)?;
3909 let mut pairs = Vec::new();
3910 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
3911 if self.eat(&Token::Semicolon) {
3912 continue;
3913 }
3914 let key = self.parse_assign_expr()?;
3915 self.expect(&Token::FatArrow)?;
3916 match self.advance().0 {
3917 Token::Ident(ref s) if s == "_" => {
3918 pairs.push(MatchHashPair::KeyOnly { key });
3919 }
3920 Token::ScalarVar(name) => {
3921 pairs.push(MatchHashPair::Capture { key, name });
3922 }
3923 tok => {
3924 return Err(self.syntax_err(
3925 format!(
3926 "hash match pattern must bind with `=> $name` or `=> _`, got {:?}",
3927 tok
3928 ),
3929 self.peek_line(),
3930 ));
3931 }
3932 }
3933 self.eat(&Token::Comma);
3934 }
3935 self.expect(&Token::RBrace)?;
3936 Ok(MatchPattern::Hash(pairs))
3937 }
3938
3939 fn parse_eval_timeout(&mut self) -> StrykeResult<Statement> {
3941 let line = self.peek_line();
3942 self.advance();
3943 let timeout = self.parse_postfix()?;
3944 let body = self.parse_block_or_bareword_block_no_args()?;
3945 self.eat(&Token::Semicolon);
3946 Ok(Statement {
3947 label: None,
3948 kind: StmtKind::EvalTimeout { timeout, body },
3949 line,
3950 })
3951 }
3952
3953 fn mark_match_scalar_g_for_boolean_condition(cond: &mut Expr) {
3954 match &mut cond.kind {
3955 ExprKind::Match {
3956 flags, scalar_g, ..
3957 } if flags.contains('g') => {
3958 *scalar_g = true;
3959 }
3960 ExprKind::UnaryOp {
3961 op: UnaryOp::LogNot,
3962 expr,
3963 } => {
3964 if let ExprKind::Match {
3965 flags, scalar_g, ..
3966 } = &mut expr.kind
3967 {
3968 if flags.contains('g') {
3969 *scalar_g = true;
3970 }
3971 }
3972 }
3973 _ => {}
3974 }
3975 }
3976
3977 fn parse_if(&mut self) -> StrykeResult<Statement> {
3978 let line = self.peek_line();
3979 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
3981 if crate::compat_mode() {
3982 return Err(self.syntax_err(
3983 "`if let` is a stryke extension (disabled by --compat)",
3984 line,
3985 ));
3986 }
3987 return self.parse_if_let(line);
3988 }
3989 self.expect(&Token::LParen)?;
3990 let mut cond = self.parse_expression()?;
3991 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
3992 self.expect(&Token::RParen)?;
3993 let body = self.parse_block()?;
3994
3995 let mut elsifs = Vec::new();
3996 let mut else_block = None;
3997
3998 loop {
3999 if let Token::Ident(ref kw) = self.peek().clone() {
4000 if kw == "elsif" {
4001 self.advance();
4002 self.expect(&Token::LParen)?;
4003 let mut c = self.parse_expression()?;
4004 Self::mark_match_scalar_g_for_boolean_condition(&mut c);
4005 self.expect(&Token::RParen)?;
4006 let b = self.parse_block()?;
4007 elsifs.push((c, b));
4008 continue;
4009 }
4010 if kw == "else" {
4011 self.advance();
4012 else_block = Some(self.parse_block()?);
4013 }
4014 }
4015 break;
4016 }
4017
4018 Ok(Statement {
4019 label: None,
4020 kind: StmtKind::If {
4021 condition: cond,
4022 body,
4023 elsifs,
4024 else_block,
4025 },
4026 line,
4027 })
4028 }
4029
4030 fn parse_if_let(&mut self, line: usize) -> StrykeResult<Statement> {
4032 self.advance(); let pattern = self.parse_match_pattern()?;
4034 self.expect(&Token::Assign)?;
4035 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4037 let rhs = self.parse_assign_expr();
4038 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4039 let rhs = rhs?;
4040 let then_block = self.parse_block()?;
4041 let else_block_opt = match self.peek().clone() {
4042 Token::Ident(ref kw) if kw == "else" => {
4043 self.advance();
4044 Some(self.parse_block()?)
4045 }
4046 Token::Ident(ref kw) if kw == "elsif" => {
4047 return Err(self.syntax_err(
4048 "`if let` does not support `elsif`; use `else { }` or a full `match`",
4049 self.peek_line(),
4050 ));
4051 }
4052 _ => None,
4053 };
4054 let then_expr = Self::expr_do_anon_block(then_block, line);
4055 let else_expr = if let Some(eb) = else_block_opt {
4056 Self::expr_do_anon_block(eb, line)
4057 } else {
4058 Expr {
4059 kind: ExprKind::Undef,
4060 line,
4061 }
4062 };
4063 let arms = vec![
4064 MatchArm {
4065 pattern,
4066 guard: None,
4067 body: then_expr,
4068 },
4069 MatchArm {
4070 pattern: MatchPattern::Any,
4071 guard: None,
4072 body: else_expr,
4073 },
4074 ];
4075 Ok(Statement {
4076 label: None,
4077 kind: StmtKind::Expression(Expr {
4078 kind: ExprKind::AlgebraicMatch {
4079 subject: Box::new(rhs),
4080 arms,
4081 },
4082 line,
4083 }),
4084 line,
4085 })
4086 }
4087
4088 fn expr_do_anon_block(block: Block, outer_line: usize) -> Expr {
4089 let inner_line = block.first().map(|s| s.line).unwrap_or(outer_line);
4090 Expr {
4091 kind: ExprKind::Do(Box::new(Expr {
4092 kind: ExprKind::CodeRef {
4093 params: vec![],
4094 body: block,
4095 },
4096 line: inner_line,
4097 })),
4098 line: outer_line,
4099 }
4100 }
4101
4102 fn parse_unless(&mut self) -> StrykeResult<Statement> {
4103 let line = self.peek_line();
4104 self.advance(); self.expect(&Token::LParen)?;
4106 let mut cond = self.parse_expression()?;
4107 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4108 self.expect(&Token::RParen)?;
4109 let body = self.parse_block()?;
4110 let else_block = if let Token::Ident(ref kw) = self.peek().clone() {
4111 if kw == "else" {
4112 self.advance();
4113 Some(self.parse_block()?)
4114 } else {
4115 None
4116 }
4117 } else {
4118 None
4119 };
4120 Ok(Statement {
4121 label: None,
4122 kind: StmtKind::Unless {
4123 condition: cond,
4124 body,
4125 else_block,
4126 },
4127 line,
4128 })
4129 }
4130
4131 fn parse_while(&mut self) -> StrykeResult<Statement> {
4132 let line = self.peek_line();
4133 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "let") {
4135 if crate::compat_mode() {
4136 return Err(self.syntax_err(
4137 "`while let` is a stryke extension (disabled by --compat)",
4138 line,
4139 ));
4140 }
4141 return self.parse_while_let(line);
4142 }
4143 self.expect(&Token::LParen)?;
4144 let mut cond = self.parse_expression()?;
4145 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4146 self.expect(&Token::RParen)?;
4147 let body = self.parse_block()?;
4148 let continue_block = self.parse_optional_continue_block()?;
4149 Ok(Statement {
4150 label: None,
4151 kind: StmtKind::While {
4152 condition: cond,
4153 body,
4154 label: None,
4155 continue_block,
4156 },
4157 line,
4158 })
4159 }
4160
4161 fn parse_while_let(&mut self, line: usize) -> StrykeResult<Statement> {
4164 self.advance(); let pattern = self.parse_match_pattern()?;
4166 self.expect(&Token::Assign)?;
4167 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
4168 let rhs = self.parse_assign_expr();
4169 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
4170 let rhs = rhs?;
4171 let mut user_body = self.parse_block()?;
4172 let continue_block = self.parse_optional_continue_block()?;
4173 user_body.push(Statement::new(
4174 StmtKind::Expression(Expr {
4175 kind: ExprKind::Integer(1),
4176 line,
4177 }),
4178 line,
4179 ));
4180 let tmp = format!("__while_let_{}", self.alloc_desugar_tmp());
4181 let match_expr = Expr {
4182 kind: ExprKind::AlgebraicMatch {
4183 subject: Box::new(rhs),
4184 arms: vec![
4185 MatchArm {
4186 pattern,
4187 guard: None,
4188 body: Self::expr_do_anon_block(user_body, line),
4189 },
4190 MatchArm {
4191 pattern: MatchPattern::Any,
4192 guard: None,
4193 body: Expr {
4194 kind: ExprKind::Integer(0),
4195 line,
4196 },
4197 },
4198 ],
4199 },
4200 line,
4201 };
4202 let my_stmt = Statement::new(
4203 StmtKind::My(vec![VarDecl {
4204 sigil: Sigil::Scalar,
4205 name: tmp.clone(),
4206 initializer: Some(match_expr),
4207 frozen: false,
4208 type_annotation: None,
4209 list_context: false,
4210 }]),
4211 line,
4212 );
4213 let unless_last = Statement::new(
4214 StmtKind::Unless {
4215 condition: Expr {
4216 kind: ExprKind::ScalarVar(tmp),
4217 line,
4218 },
4219 body: vec![Statement::new(StmtKind::Last(None), line)],
4220 else_block: None,
4221 },
4222 line,
4223 );
4224 Ok(Statement::new(
4225 StmtKind::While {
4226 condition: Expr {
4227 kind: ExprKind::Integer(1),
4228 line,
4229 },
4230 body: vec![my_stmt, unless_last],
4231 label: None,
4232 continue_block,
4233 },
4234 line,
4235 ))
4236 }
4237
4238 fn parse_until(&mut self) -> StrykeResult<Statement> {
4239 let line = self.peek_line();
4240 self.advance(); self.expect(&Token::LParen)?;
4242 let mut cond = self.parse_expression()?;
4243 Self::mark_match_scalar_g_for_boolean_condition(&mut cond);
4244 self.expect(&Token::RParen)?;
4245 let body = self.parse_block()?;
4246 let continue_block = self.parse_optional_continue_block()?;
4247 Ok(Statement {
4248 label: None,
4249 kind: StmtKind::Until {
4250 condition: cond,
4251 body,
4252 label: None,
4253 continue_block,
4254 },
4255 line,
4256 })
4257 }
4258
4259 fn parse_optional_continue_block(&mut self) -> StrykeResult<Option<Block>> {
4261 if let Token::Ident(ref kw) = self.peek().clone() {
4262 if kw == "continue" {
4263 self.advance();
4264 return Ok(Some(self.parse_block()?));
4265 }
4266 }
4267 Ok(None)
4268 }
4269
4270 fn parse_for_or_foreach(&mut self) -> StrykeResult<Statement> {
4271 let line = self.peek_line();
4272 self.advance(); match self.peek() {
4278 Token::LParen => {
4279 let saved = self.pos;
4284 self.advance(); let mut depth = 1;
4287 let mut has_semi = false;
4288 let mut scan = self.pos;
4289 while scan < self.tokens.len() {
4290 match &self.tokens[scan].0 {
4291 Token::LParen => depth += 1,
4292 Token::RParen => {
4293 depth -= 1;
4294 if depth == 0 {
4295 break;
4296 }
4297 }
4298 Token::Semicolon if depth == 1 => {
4299 has_semi = true;
4300 break;
4301 }
4302 _ => {}
4303 }
4304 scan += 1;
4305 }
4306 self.pos = saved;
4307
4308 if has_semi {
4309 self.parse_c_style_for(line)
4310 } else {
4311 self.expect(&Token::LParen)?;
4313 let list = self.parse_expression()?;
4314 self.expect(&Token::RParen)?;
4315 let body = self.parse_block()?;
4316 let continue_block = self.parse_optional_continue_block()?;
4317 Ok(Statement {
4318 label: None,
4319 kind: StmtKind::Foreach {
4320 var: "_".to_string(),
4321 list,
4322 body,
4323 label: None,
4324 continue_block,
4325 },
4326 line,
4327 })
4328 }
4329 }
4330 Token::Ident(ref kw) if kw == "my" => {
4331 self.advance(); let var = self.parse_scalar_var_name()?;
4333 self.expect(&Token::LParen)?;
4334 let list = self.parse_expression()?;
4335 self.expect(&Token::RParen)?;
4336 let body = self.parse_block()?;
4337 let continue_block = self.parse_optional_continue_block()?;
4338 Ok(Statement {
4339 label: None,
4340 kind: StmtKind::Foreach {
4341 var,
4342 list,
4343 body,
4344 label: None,
4345 continue_block,
4346 },
4347 line,
4348 })
4349 }
4350 Token::ScalarVar(_) => {
4351 let var = self.parse_scalar_var_name()?;
4352 self.expect(&Token::LParen)?;
4353 let list = self.parse_expression()?;
4354 self.expect(&Token::RParen)?;
4355 let body = self.parse_block()?;
4356 let continue_block = self.parse_optional_continue_block()?;
4357 Ok(Statement {
4358 label: None,
4359 kind: StmtKind::Foreach {
4360 var,
4361 list,
4362 body,
4363 label: None,
4364 continue_block,
4365 },
4366 line,
4367 })
4368 }
4369 _ => self.parse_c_style_for(line),
4370 }
4371 }
4372
4373 fn parse_c_style_for(&mut self, line: usize) -> StrykeResult<Statement> {
4374 self.expect(&Token::LParen)?;
4375 let init = if self.eat(&Token::Semicolon) {
4376 None
4377 } else {
4378 let s = self.parse_statement()?;
4379 self.eat(&Token::Semicolon);
4380 Some(Box::new(s))
4381 };
4382 let mut condition = if matches!(self.peek(), Token::Semicolon) {
4383 None
4384 } else {
4385 Some(self.parse_expression()?)
4386 };
4387 if let Some(ref mut c) = condition {
4388 Self::mark_match_scalar_g_for_boolean_condition(c);
4389 }
4390 self.expect(&Token::Semicolon)?;
4391 let step = if matches!(self.peek(), Token::RParen) {
4392 None
4393 } else {
4394 Some(self.parse_expression()?)
4395 };
4396 self.expect(&Token::RParen)?;
4397 let body = self.parse_block()?;
4398 let continue_block = self.parse_optional_continue_block()?;
4399 Ok(Statement {
4400 label: None,
4401 kind: StmtKind::For {
4402 init,
4403 condition,
4404 step,
4405 body,
4406 label: None,
4407 continue_block,
4408 },
4409 line,
4410 })
4411 }
4412
4413 fn parse_foreach(&mut self) -> StrykeResult<Statement> {
4414 let line = self.peek_line();
4415 self.advance(); let var = match self.peek() {
4417 Token::Ident(ref kw) if kw == "my" => {
4418 self.advance();
4419 self.parse_scalar_var_name()?
4420 }
4421 Token::ScalarVar(_) => self.parse_scalar_var_name()?,
4422 _ => "_".to_string(),
4423 };
4424 self.expect(&Token::LParen)?;
4425 let list = self.parse_expression()?;
4426 self.expect(&Token::RParen)?;
4427 let body = self.parse_block()?;
4428 let continue_block = self.parse_optional_continue_block()?;
4429 Ok(Statement {
4430 label: None,
4431 kind: StmtKind::Foreach {
4432 var,
4433 list,
4434 body,
4435 label: None,
4436 continue_block,
4437 },
4438 line,
4439 })
4440 }
4441
4442 fn parse_scalar_var_name(&mut self) -> StrykeResult<String> {
4443 match self.advance() {
4444 (Token::ScalarVar(name), _) => Ok(name),
4445 (tok, line) => {
4446 Err(self.syntax_err(format!("Expected scalar variable, got {:?}", tok), line))
4447 }
4448 }
4449 }
4450
4451 fn parse_legacy_sub_prototype_tail(&mut self) -> StrykeResult<String> {
4453 let mut s = String::new();
4454 loop {
4455 match self.peek().clone() {
4456 Token::RParen => {
4457 self.advance();
4458 break;
4459 }
4460 Token::Eof => {
4461 return Err(self.syntax_err(
4462 "Unterminated sub prototype (expected ')' before end of input)",
4463 self.peek_line(),
4464 ));
4465 }
4466 Token::ScalarVar(v) if v == ")" => {
4467 self.advance();
4470 s.push('$');
4471 if matches!(self.peek(), Token::LBrace) {
4472 break;
4473 }
4474 }
4475 Token::Ident(i) => {
4476 let i = i.clone();
4477 self.advance();
4478 s.push_str(&i);
4479 }
4480 Token::Semicolon => {
4481 self.advance();
4482 s.push(';');
4483 }
4484 Token::LParen => {
4485 self.advance();
4486 s.push('(');
4487 }
4488 Token::LBracket => {
4489 self.advance();
4490 s.push('[');
4491 }
4492 Token::RBracket => {
4493 self.advance();
4494 s.push(']');
4495 }
4496 Token::Backslash => {
4497 self.advance();
4498 s.push('\\');
4499 }
4500 Token::Comma => {
4501 self.advance();
4502 s.push(',');
4503 }
4504 Token::ScalarVar(v) => {
4505 let v = v.clone();
4506 self.advance();
4507 s.push('$');
4508 s.push_str(&v);
4509 }
4510 Token::ArrayVar(v) => {
4511 let v = v.clone();
4512 self.advance();
4513 s.push('@');
4514 s.push_str(&v);
4515 }
4516 Token::ArrayAt => {
4518 self.advance();
4519 s.push('@');
4520 }
4521 Token::HashVar(v) => {
4522 let v = v.clone();
4523 self.advance();
4524 s.push('%');
4525 s.push_str(&v);
4526 }
4527 Token::HashPercent => {
4528 self.advance();
4529 s.push('%');
4530 }
4531 Token::Plus => {
4532 self.advance();
4533 s.push('+');
4534 }
4535 Token::Minus => {
4536 self.advance();
4537 s.push('-');
4538 }
4539 Token::BitAnd => {
4540 self.advance();
4541 s.push('&');
4542 }
4543 tok => {
4544 return Err(self.syntax_err(
4545 format!("Unexpected token in sub prototype: {:?}", tok),
4546 self.peek_line(),
4547 ));
4548 }
4549 }
4550 }
4551 Ok(s)
4552 }
4553
4554 fn sub_signature_list_starts_here(&self) -> bool {
4555 match self.peek() {
4556 Token::LBrace | Token::LBracket => true,
4557 Token::ScalarVar(name) if name != "$$" && name != ")" => true,
4558 Token::ArrayVar(_) | Token::HashVar(_) => true,
4559 _ => false,
4560 }
4561 }
4562
4563 fn parse_sub_signature_hash_key(&mut self) -> StrykeResult<String> {
4564 let (tok, line) = self.advance();
4565 match tok {
4566 Token::Ident(i) => Ok(i),
4567 Token::SingleString(s) | Token::DoubleString(s) => Ok(s),
4568 tok => Err(self.syntax_err(
4569 format!(
4570 "sub signature: expected hash key (identifier or string), got {:?}",
4571 tok
4572 ),
4573 line,
4574 )),
4575 }
4576 }
4577
4578 fn parse_sub_signature_param_list(&mut self) -> StrykeResult<Vec<SubSigParam>> {
4579 let mut params = Vec::new();
4580 loop {
4581 if matches!(self.peek(), Token::RParen) {
4582 break;
4583 }
4584 match self.peek().clone() {
4585 Token::ScalarVar(name) => {
4586 if name == "$$" || name == ")" {
4587 return Err(self.syntax_err(
4588 format!(
4589 "`{name}` cannot start a stryke sub signature (use legacy prototype `($$)` etc.)"
4590 ),
4591 self.peek_line(),
4592 ));
4593 }
4594 self.advance();
4595 let ty = if self.eat(&Token::Colon) {
4596 match self.peek() {
4597 Token::Ident(ref tname) => {
4598 let tname = tname.clone();
4599 self.advance();
4600 Some(match tname.as_str() {
4601 "Int" => PerlTypeName::Int,
4602 "Str" => PerlTypeName::Str,
4603 "Float" => PerlTypeName::Float,
4604 "Bool" => PerlTypeName::Bool,
4605 "Array" => PerlTypeName::Array,
4606 "Hash" => PerlTypeName::Hash,
4607 "Ref" => PerlTypeName::Ref,
4608 "Any" => PerlTypeName::Any,
4609 _ => PerlTypeName::Struct(tname),
4610 })
4611 }
4612 _ => {
4613 return Err(self.syntax_err(
4614 "expected type name after `:` in sub signature",
4615 self.peek_line(),
4616 ));
4617 }
4618 }
4619 } else {
4620 None
4621 };
4622 let default = if self.eat(&Token::Assign) {
4624 Some(Box::new(self.parse_ternary()?))
4625 } else {
4626 None
4627 };
4628 params.push(SubSigParam::Scalar(name, ty, default));
4629 }
4630 Token::ArrayVar(name) => {
4631 self.advance();
4632 let default = if self.eat(&Token::Assign) {
4633 Some(Box::new(self.parse_ternary()?))
4634 } else {
4635 None
4636 };
4637 params.push(SubSigParam::Array(name, default));
4638 }
4639 Token::HashVar(name) => {
4640 self.advance();
4641 let default = if self.eat(&Token::Assign) {
4642 Some(Box::new(self.parse_ternary()?))
4643 } else {
4644 None
4645 };
4646 params.push(SubSigParam::Hash(name, default));
4647 }
4648 Token::LBracket => {
4649 self.advance();
4650 let elems = self.parse_match_array_elems_until_rbracket()?;
4651 params.push(SubSigParam::ArrayDestruct(elems));
4652 }
4653 Token::LBrace => {
4654 self.advance();
4655 let mut pairs = Vec::new();
4656 loop {
4657 if matches!(self.peek(), Token::RBrace | Token::Eof) {
4658 break;
4659 }
4660 if self.eat(&Token::Comma) {
4661 continue;
4662 }
4663 let key = self.parse_sub_signature_hash_key()?;
4664 self.expect(&Token::FatArrow)?;
4665 let bind = self.parse_scalar_var_name()?;
4666 pairs.push((key, bind));
4667 self.eat(&Token::Comma);
4668 }
4669 self.expect(&Token::RBrace)?;
4670 params.push(SubSigParam::HashDestruct(pairs));
4671 }
4672 tok => {
4673 return Err(self.syntax_err(
4674 format!(
4675 "expected `$name`, `[ ... ]`, or `{{ ... }}` in sub signature, got {:?}",
4676 tok
4677 ),
4678 self.peek_line(),
4679 ));
4680 }
4681 }
4682 match self.peek() {
4683 Token::Comma => {
4684 self.advance();
4685 if matches!(self.peek(), Token::RParen) {
4686 return Err(self.syntax_err(
4687 "trailing `,` before `)` in sub signature",
4688 self.peek_line(),
4689 ));
4690 }
4691 }
4692 Token::RParen => break,
4693 _ => {
4694 return Err(self.syntax_err(
4695 format!(
4696 "expected `,` or `)` after sub signature parameter, got {:?}",
4697 self.peek()
4698 ),
4699 self.peek_line(),
4700 ));
4701 }
4702 }
4703 }
4704 Ok(params)
4705 }
4706
4707 fn parse_sub_sig_or_prototype_opt(
4709 &mut self,
4710 ) -> StrykeResult<(Vec<SubSigParam>, Option<String>)> {
4711 if !matches!(self.peek(), Token::LParen) {
4712 return Ok((vec![], None));
4713 }
4714 self.advance();
4715 if matches!(self.peek(), Token::RParen) {
4716 self.advance();
4717 return Ok((vec![], Some(String::new())));
4718 }
4719 if self.sub_signature_list_starts_here() {
4720 let params = self.parse_sub_signature_param_list()?;
4721 self.expect(&Token::RParen)?;
4722 return Ok((params, None));
4723 }
4724 let proto = self.parse_legacy_sub_prototype_tail()?;
4725 Ok((vec![], Some(proto)))
4726 }
4727
4728 fn parse_sub_attributes(&mut self) -> StrykeResult<()> {
4730 while self.eat(&Token::Colon) {
4731 match self.advance() {
4732 (Token::Ident(_), _) => {}
4733 (tok, line) => {
4734 return Err(self.syntax_err(
4735 format!("Expected attribute name after `:`, got {:?}", tok),
4736 line,
4737 ));
4738 }
4739 }
4740 if self.eat(&Token::LParen) {
4741 let mut depth = 1usize;
4742 while depth > 0 {
4743 match self.advance().0 {
4744 Token::LParen => depth += 1,
4745 Token::RParen => {
4746 depth -= 1;
4747 }
4748 Token::Eof => {
4749 return Err(self.syntax_err(
4750 "Unterminated sub attribute argument list",
4751 self.peek_line(),
4752 ));
4753 }
4754 _ => {}
4755 }
4756 }
4757 }
4758 }
4759 Ok(())
4760 }
4761
4762 fn try_parse_fn_assign_shorthand_body(&mut self) -> StrykeResult<Option<Block>> {
4765 if !self.eat(&Token::Assign) {
4766 return Ok(None);
4767 }
4768 let expr = self.parse_assign_expr()?;
4769 if matches!(self.peek(), Token::Comma) {
4770 return Err(self.syntax_err(
4771 "`fn ... =` allows only a single expression; use `fn ... { ... }` for multiple statements",
4772 self.peek_line(),
4773 ));
4774 }
4775 let eline = expr.line;
4776 self.eat(&Token::Semicolon);
4777 let mut body = vec![Statement {
4778 label: None,
4779 kind: StmtKind::Expression(expr),
4780 line: eline,
4781 }];
4782 Self::default_topic_for_sole_bareword(&mut body);
4783 Ok(Some(body))
4784 }
4785
4786 fn parse_fn_eq_body_or_block(&mut self, is_sub_keyword: bool) -> StrykeResult<Block> {
4789 if !is_sub_keyword {
4790 if let Some(block) = self.try_parse_fn_assign_shorthand_body()? {
4791 return Ok(block);
4792 }
4793 }
4794 self.parse_block()
4795 }
4796
4797 fn parse_sub_decl(&mut self, is_sub_keyword: bool) -> StrykeResult<Statement> {
4798 let line = self.peek_line();
4799 self.advance(); match self.peek().clone() {
4801 Token::Ident(_) => {
4802 let name = self.parse_package_qualified_identifier()?;
4803 let bare = name.rsplit("::").next().unwrap_or(&name);
4812 if Self::is_underscore_topic_slot(bare) {
4813 return Err(self.syntax_err(
4814 format!(
4815 "`fn {}` would shadow the topic-slot scalar; pick a different name",
4816 name
4817 ),
4818 line,
4819 ));
4820 }
4821 if Self::is_reserved_special_var_name(bare) {
4822 return Err(self.syntax_err(
4823 format!(
4824 "`fn {}` would shadow a Perl special variable / filehandle / compile-time token; pick a different name",
4825 name
4826 ),
4827 line,
4828 ));
4829 }
4830 let allow_shadow =
4837 crate::compat_mode() || (self.parsing_module && !crate::no_interop_mode());
4838 if !allow_shadow {
4839 self.check_udf_shadows_builtin(&name, line)?;
4840 }
4841 self.declared_subs.insert(name.clone());
4842 let (params, prototype) = self.parse_sub_sig_or_prototype_opt()?;
4843 self.parse_sub_attributes()?;
4844 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4845 Ok(Statement {
4846 label: None,
4847 kind: StmtKind::SubDecl {
4848 name,
4849 params,
4850 body,
4851 prototype,
4852 },
4853 line,
4854 })
4855 }
4856 Token::LParen | Token::LBrace | Token::Colon => {
4857 if is_sub_keyword && crate::no_interop_mode() {
4859 return Err(self.syntax_err(
4860 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
4861 line,
4862 ));
4863 }
4864 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
4866 self.parse_sub_attributes()?;
4867 let body = self.parse_fn_eq_body_or_block(is_sub_keyword)?;
4868 Ok(Statement {
4869 label: None,
4870 kind: StmtKind::Expression(Expr {
4871 kind: ExprKind::CodeRef { params, body },
4872 line,
4873 }),
4874 line,
4875 })
4876 }
4877 tok => {
4878 let topic_name = match &tok {
4883 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n)
4884 if Self::is_underscore_topic_slot(n) =>
4885 {
4886 Some((
4887 match &tok {
4888 Token::ScalarVar(_) => '$',
4889 Token::ArrayVar(_) => '@',
4890 Token::HashVar(_) => '%',
4891 _ => unreachable!(),
4892 },
4893 n.clone(),
4894 ))
4895 }
4896 _ => None,
4897 };
4898 if let Some((sigil, n)) = topic_name {
4899 return Err(self.syntax_err(
4900 format!(
4901 "`fn {}{}` would shadow the topic-slot scalar; pick a different name",
4902 sigil, n
4903 ),
4904 self.peek_line(),
4905 ));
4906 }
4907 let special_var = match &tok {
4912 Token::ScalarVar(n) | Token::ArrayVar(n) | Token::HashVar(n) => Some((
4913 match &tok {
4914 Token::ScalarVar(_) => '$',
4915 Token::ArrayVar(_) => '@',
4916 Token::HashVar(_) => '%',
4917 _ => unreachable!(),
4918 },
4919 n.clone(),
4920 )),
4921 _ => None,
4922 };
4923 if let Some((sigil, n)) = special_var {
4924 return Err(self.syntax_err(
4925 format!(
4926 "`fn {}{}` would shadow a Perl special variable / global; pick a different name",
4927 sigil, n
4928 ),
4929 self.peek_line(),
4930 ));
4931 }
4932 if matches!(tok, Token::Percent) {
4937 return Err(self.syntax_err(
4938 "`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 { ... }`",
4939 self.peek_line(),
4940 ));
4941 }
4942 Err(self.syntax_err(
4943 format!("Expected sub name, `(`, `{{`, or `:`, got {:?}", tok),
4944 self.peek_line(),
4945 ))
4946 }
4947 }
4948 }
4949
4950 fn parse_advice_decl(&mut self, kind: crate::ast::AdviceKind) -> StrykeResult<Statement> {
4953 let line = self.peek_line();
4954 self.advance(); let pattern = match self.advance() {
4956 (Token::SingleString(s), _) | (Token::DoubleString(s), _) => s,
4957 (tok, err_line) => {
4958 return Err(self.syntax_err(
4959 format!(
4960 "Expected string-literal pattern after `{}`, got {:?}",
4961 match kind {
4962 crate::ast::AdviceKind::Before => "before",
4963 crate::ast::AdviceKind::After => "after",
4964 crate::ast::AdviceKind::Around => "around",
4965 },
4966 tok
4967 ),
4968 err_line,
4969 ));
4970 }
4971 };
4972 let body = self.parse_block()?;
4973 Ok(Statement {
4974 label: None,
4975 kind: StmtKind::AdviceDecl {
4976 kind,
4977 pattern,
4978 body,
4979 },
4980 line,
4981 })
4982 }
4983
4984 fn parse_struct_decl(&mut self) -> StrykeResult<Statement> {
4986 let line = self.peek_line();
4987 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
4989 self.syntax_err(
4990 format!("Expected struct name, got {:?}", self.peek()),
4991 self.peek_line(),
4992 )
4993 })?;
4994 let name = if raw_name.contains("::") || self.current_package == "main" {
4995 raw_name
4996 } else {
4997 format!("{}::{}", self.current_package, raw_name)
4998 };
4999 self.expect(&Token::LBrace)?;
5000 let mut fields = Vec::new();
5001 let mut methods = Vec::new();
5002 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5003 let is_method = match self.peek() {
5005 Token::Ident(s) => s == "fn" || s == "sub",
5006 _ => false,
5007 };
5008 if is_method {
5009 let is_sub_keyword = matches!(self.peek(), Token::Ident(ref s) if s == "sub");
5010 self.advance(); let method_name = match self.advance() {
5012 (Token::Ident(n), _) => n,
5013 (tok, err_line) => {
5014 return Err(self
5015 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5016 }
5017 };
5018 let params = if self.eat(&Token::LParen) {
5020 let p = self.parse_sub_signature_param_list()?;
5021 self.expect(&Token::RParen)?;
5022 p
5023 } else {
5024 Vec::new()
5025 };
5026 let body = if is_sub_keyword {
5027 self.parse_block()?
5028 } else {
5029 self.parse_fn_eq_body_or_block(false)?
5030 };
5031 methods.push(crate::ast::StructMethod {
5032 name: method_name,
5033 params,
5034 body,
5035 });
5036 self.eat(&Token::Comma);
5038 self.eat(&Token::Semicolon);
5039 continue;
5040 }
5041
5042 let field_name = match self.advance() {
5043 (Token::Ident(n), _) => n,
5044 (tok, err_line) => {
5045 return Err(
5046 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5047 )
5048 }
5049 };
5050 let ty = if self.eat(&Token::FatArrow) || self.eat(&Token::Colon) {
5055 self.parse_type_name()?
5056 } else {
5057 crate::ast::PerlTypeName::Any
5058 };
5059 let default = if self.eat(&Token::Assign) {
5060 Some(self.parse_ternary()?)
5062 } else {
5063 None
5064 };
5065 fields.push(StructField {
5066 name: field_name,
5067 ty,
5068 default,
5069 });
5070 if !self.eat(&Token::Comma) {
5071 self.eat(&Token::Semicolon);
5073 }
5074 }
5075 self.expect(&Token::RBrace)?;
5076 self.eat(&Token::Semicolon);
5077 Ok(Statement {
5078 label: None,
5079 kind: StmtKind::StructDecl {
5080 def: StructDef {
5081 name,
5082 fields,
5083 methods,
5084 },
5085 },
5086 line,
5087 })
5088 }
5089
5090 fn parse_enum_decl(&mut self) -> StrykeResult<Statement> {
5092 let line = self.peek_line();
5093 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5095 self.syntax_err(
5096 format!("Expected enum name, got {:?}", self.peek()),
5097 self.peek_line(),
5098 )
5099 })?;
5100 let name = if raw_name.contains("::") || self.current_package == "main" {
5101 raw_name
5102 } else {
5103 format!("{}::{}", self.current_package, raw_name)
5104 };
5105 self.expect(&Token::LBrace)?;
5106 let mut variants = Vec::new();
5107 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5108 let variant_name = match self.advance() {
5109 (Token::Ident(n), _) => n,
5110 (tok, err_line) => {
5111 return Err(
5112 self.syntax_err(format!("Expected variant name, got {:?}", tok), err_line)
5113 )
5114 }
5115 };
5116 let ty = if self.eat(&Token::FatArrow) {
5117 Some(self.parse_type_name()?)
5118 } else {
5119 None
5120 };
5121 variants.push(EnumVariant {
5122 name: variant_name,
5123 ty,
5124 });
5125 if !self.eat(&Token::Comma) {
5126 self.eat(&Token::Semicolon);
5127 }
5128 }
5129 self.expect(&Token::RBrace)?;
5130 self.eat(&Token::Semicolon);
5131 Ok(Statement {
5132 label: None,
5133 kind: StmtKind::EnumDecl {
5134 def: EnumDef { name, variants },
5135 },
5136 line,
5137 })
5138 }
5139
5140 fn parse_class_decl(&mut self, is_abstract: bool, is_final: bool) -> StrykeResult<Statement> {
5142 use crate::ast::{ClassDef, ClassField, ClassMethod, ClassStaticField, Visibility};
5143 let line = self.peek_line();
5144 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5146 self.syntax_err(
5147 format!("Expected class name, got {:?}", self.peek()),
5148 self.peek_line(),
5149 )
5150 })?;
5151 let name = if raw_name.contains("::") || self.current_package == "main" {
5156 raw_name
5157 } else {
5158 format!("{}::{}", self.current_package, raw_name)
5159 };
5160
5161 let mut extends = Vec::new();
5163 if matches!(self.peek(), Token::Ident(ref s) if s == "extends") {
5164 self.advance(); loop {
5166 let parent = self.parse_package_qualified_identifier().map_err(|_| {
5167 self.syntax_err(
5168 format!(
5169 "Expected parent class name after `extends`, got {:?}",
5170 self.peek()
5171 ),
5172 self.peek_line(),
5173 )
5174 })?;
5175 extends.push(parent);
5176 if !self.eat(&Token::Comma) {
5177 break;
5178 }
5179 }
5180 }
5181
5182 let mut implements = Vec::new();
5184 if matches!(self.peek(), Token::Ident(ref s) if s == "impl") {
5185 self.advance(); loop {
5187 let trait_name = self.parse_package_qualified_identifier().map_err(|_| {
5188 self.syntax_err(
5189 format!("Expected trait name after `impl`, got {:?}", self.peek()),
5190 self.peek_line(),
5191 )
5192 })?;
5193 implements.push(trait_name);
5194 if !self.eat(&Token::Comma) {
5195 break;
5196 }
5197 }
5198 }
5199
5200 self.expect(&Token::LBrace)?;
5201 let mut fields = Vec::new();
5202 let mut methods = Vec::new();
5203 let mut static_fields = Vec::new();
5204
5205 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5206 let visibility = match self.peek() {
5208 Token::Ident(ref s) if s == "pub" => {
5209 self.advance();
5210 Visibility::Public
5211 }
5212 Token::Ident(ref s) if s == "priv" => {
5213 self.advance();
5214 Visibility::Private
5215 }
5216 Token::Ident(ref s) if s == "prot" => {
5217 self.advance();
5218 Visibility::Protected
5219 }
5220 _ => Visibility::Public, };
5222
5223 if matches!(self.peek(), Token::Ident(ref s) if s == "static") {
5225 self.advance(); if matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5229 return Err(self.syntax_err(
5231 "use `fn Self.name` for static methods, not `static fn`",
5232 self.peek_line(),
5233 ));
5234 }
5235
5236 let field_name = match self.advance() {
5237 (Token::Ident(n), _) => n,
5238 (tok, err_line) => {
5239 return Err(self.syntax_err(
5240 format!("Expected static field name, got {:?}", tok),
5241 err_line,
5242 ))
5243 }
5244 };
5245
5246 let ty = if self.eat(&Token::Colon) {
5247 self.parse_type_name()?
5248 } else {
5249 crate::ast::PerlTypeName::Any
5250 };
5251
5252 let default = if self.eat(&Token::Assign) {
5253 Some(self.parse_ternary()?)
5254 } else {
5255 None
5256 };
5257
5258 static_fields.push(ClassStaticField {
5259 name: field_name,
5260 ty,
5261 visibility,
5262 default,
5263 });
5264
5265 if !self.eat(&Token::Comma) {
5266 self.eat(&Token::Semicolon);
5267 }
5268 continue;
5269 }
5270
5271 let method_is_final = matches!(self.peek(), Token::Ident(ref s) if s == "final");
5273 if method_is_final {
5274 self.advance(); }
5276
5277 let is_method = matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub");
5279 if is_method {
5280 self.advance(); let is_static = matches!(self.peek(), Token::Ident(ref s) if s == "Self");
5284 if is_static {
5285 self.advance(); self.expect(&Token::Dot)?;
5287 }
5288
5289 let method_name = match self.advance() {
5290 (Token::Ident(n), _) => n,
5291 (tok, err_line) => {
5292 return Err(self
5293 .syntax_err(format!("Expected method name, got {:?}", tok), err_line))
5294 }
5295 };
5296
5297 let params = if self.eat(&Token::LParen) {
5299 let p = self.parse_sub_signature_param_list()?;
5300 self.expect(&Token::RParen)?;
5301 p
5302 } else {
5303 Vec::new()
5304 };
5305
5306 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5308 Some(b)
5309 } else if matches!(self.peek(), Token::LBrace) {
5310 Some(self.parse_block()?)
5311 } else {
5312 None
5313 };
5314
5315 methods.push(ClassMethod {
5316 name: method_name,
5317 params,
5318 body,
5319 visibility,
5320 is_static,
5321 is_final: method_is_final,
5322 });
5323 self.eat(&Token::Comma);
5324 self.eat(&Token::Semicolon);
5325 continue;
5326 } else if method_is_final {
5327 return Err(self.syntax_err("`final` must be followed by `fn`", self.peek_line()));
5328 }
5329
5330 let field_name = match self.advance() {
5332 (Token::Ident(n), _) => n,
5333 (tok, err_line) => {
5334 return Err(
5335 self.syntax_err(format!("Expected field name, got {:?}", tok), err_line)
5336 )
5337 }
5338 };
5339
5340 let ty = if self.eat(&Token::Colon) || self.eat(&Token::FatArrow) {
5344 self.parse_type_name()?
5345 } else {
5346 crate::ast::PerlTypeName::Any
5347 };
5348
5349 let default = if self.eat(&Token::Assign) {
5351 Some(self.parse_ternary()?)
5352 } else {
5353 None
5354 };
5355
5356 fields.push(ClassField {
5357 name: field_name,
5358 ty,
5359 visibility,
5360 default,
5361 });
5362
5363 if !self.eat(&Token::Comma) {
5364 self.eat(&Token::Semicolon);
5365 }
5366 }
5367
5368 self.expect(&Token::RBrace)?;
5369 self.eat(&Token::Semicolon);
5370
5371 Ok(Statement {
5372 label: None,
5373 kind: StmtKind::ClassDecl {
5374 def: ClassDef {
5375 name,
5376 is_abstract,
5377 is_final,
5378 extends,
5379 implements,
5380 fields,
5381 methods,
5382 static_fields,
5383 },
5384 },
5385 line,
5386 })
5387 }
5388
5389 fn parse_trait_decl(&mut self) -> StrykeResult<Statement> {
5391 use crate::ast::{ClassMethod, TraitDef, Visibility};
5392 let line = self.peek_line();
5393 self.advance(); let raw_name = self.parse_package_qualified_identifier().map_err(|_| {
5395 self.syntax_err(
5396 format!("Expected trait name, got {:?}", self.peek()),
5397 self.peek_line(),
5398 )
5399 })?;
5400 let name = if raw_name.contains("::") || self.current_package == "main" {
5401 raw_name
5402 } else {
5403 format!("{}::{}", self.current_package, raw_name)
5404 };
5405
5406 self.expect(&Token::LBrace)?;
5407 let mut methods = Vec::new();
5408
5409 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
5410 let visibility = match self.peek() {
5412 Token::Ident(ref s) if s == "pub" => {
5413 self.advance();
5414 Visibility::Public
5415 }
5416 Token::Ident(ref s) if s == "priv" => {
5417 self.advance();
5418 Visibility::Private
5419 }
5420 Token::Ident(ref s) if s == "prot" => {
5421 self.advance();
5422 Visibility::Protected
5423 }
5424 _ => Visibility::Public,
5425 };
5426
5427 if !matches!(self.peek(), Token::Ident(ref s) if s == "fn" || s == "sub") {
5429 return Err(self.syntax_err("Expected `fn` in trait definition", self.peek_line()));
5430 }
5431 self.advance(); let method_name = match self.advance() {
5434 (Token::Ident(n), _) => n,
5435 (tok, err_line) => {
5436 return Err(
5437 self.syntax_err(format!("Expected method name, got {:?}", tok), err_line)
5438 )
5439 }
5440 };
5441
5442 let params = if self.eat(&Token::LParen) {
5444 let p = self.parse_sub_signature_param_list()?;
5445 self.expect(&Token::RParen)?;
5446 p
5447 } else {
5448 Vec::new()
5449 };
5450
5451 let body = if let Some(b) = self.try_parse_fn_assign_shorthand_body()? {
5453 Some(b)
5454 } else if matches!(self.peek(), Token::LBrace) {
5455 Some(self.parse_block()?)
5456 } else {
5457 None
5458 };
5459
5460 methods.push(ClassMethod {
5461 name: method_name,
5462 params,
5463 body,
5464 visibility,
5465 is_static: false,
5466 is_final: false,
5467 });
5468
5469 self.eat(&Token::Comma);
5470 self.eat(&Token::Semicolon);
5471 }
5472
5473 self.expect(&Token::RBrace)?;
5474 self.eat(&Token::Semicolon);
5475
5476 Ok(Statement {
5477 label: None,
5478 kind: StmtKind::TraitDecl {
5479 def: TraitDef { name, methods },
5480 },
5481 line,
5482 })
5483 }
5484
5485 fn local_simple_target_to_var_decl(target: &Expr) -> Option<VarDecl> {
5486 match &target.kind {
5487 ExprKind::ScalarVar(name) => Some(VarDecl {
5488 sigil: Sigil::Scalar,
5489 name: name.clone(),
5490 initializer: None,
5491 frozen: false,
5492 type_annotation: None,
5493 list_context: false,
5494 }),
5495 ExprKind::ArrayVar(name) => Some(VarDecl {
5496 sigil: Sigil::Array,
5497 name: name.clone(),
5498 initializer: None,
5499 frozen: false,
5500 type_annotation: None,
5501 list_context: false,
5502 }),
5503 ExprKind::HashVar(name) => Some(VarDecl {
5504 sigil: Sigil::Hash,
5505 name: name.clone(),
5506 initializer: None,
5507 frozen: false,
5508 type_annotation: None,
5509 list_context: false,
5510 }),
5511 ExprKind::Typeglob(name) => Some(VarDecl {
5512 sigil: Sigil::Typeglob,
5513 name: name.clone(),
5514 initializer: None,
5515 frozen: false,
5516 type_annotation: None,
5517 list_context: false,
5518 }),
5519 _ => None,
5520 }
5521 }
5522
5523 fn parse_decl_array_destructure(
5524 &mut self,
5525 keyword: &str,
5526 line: usize,
5527 ) -> StrykeResult<Statement> {
5528 self.expect(&Token::LBracket)?;
5529 let elems = self.parse_match_array_elems_until_rbracket()?;
5530 self.expect(&Token::Assign)?;
5531 self.suppress_scalar_hash_brace += 1;
5532 let rhs = self.parse_expression()?;
5533 self.suppress_scalar_hash_brace -= 1;
5534 let stmt = self.desugar_array_destructure(keyword, line, elems, rhs)?;
5535 self.parse_stmt_postfix_modifier(stmt)
5536 }
5537
5538 fn parse_decl_hash_destructure(
5539 &mut self,
5540 keyword: &str,
5541 line: usize,
5542 ) -> StrykeResult<Statement> {
5543 let MatchPattern::Hash(pairs) = self.parse_match_hash_pattern()? else {
5544 unreachable!("parse_match_hash_pattern returns Hash");
5545 };
5546 self.expect(&Token::Assign)?;
5547 self.suppress_scalar_hash_brace += 1;
5548 let rhs = self.parse_expression()?;
5549 self.suppress_scalar_hash_brace -= 1;
5550 let stmt = self.desugar_hash_destructure(keyword, line, pairs, rhs)?;
5551 self.parse_stmt_postfix_modifier(stmt)
5552 }
5553
5554 fn desugar_array_destructure(
5555 &mut self,
5556 keyword: &str,
5557 line: usize,
5558 elems: Vec<MatchArrayElem>,
5559 rhs: Expr,
5560 ) -> StrykeResult<Statement> {
5561 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5562 let mut stmts: Vec<Statement> = Vec::new();
5563 stmts.push(destructure_stmt_from_var_decls(
5564 keyword,
5565 vec![VarDecl {
5566 sigil: Sigil::Scalar,
5567 name: tmp.clone(),
5568 initializer: Some(rhs),
5569 frozen: false,
5570 type_annotation: None,
5571 list_context: false,
5572 }],
5573 line,
5574 ));
5575
5576 let has_rest = elems
5577 .iter()
5578 .any(|e| matches!(e, MatchArrayElem::Rest | MatchArrayElem::RestBind(_)));
5579 let fixed_slots = elems
5580 .iter()
5581 .filter(|e| {
5582 matches!(
5583 e,
5584 MatchArrayElem::CaptureScalar(_) | MatchArrayElem::Expr(_)
5585 )
5586 })
5587 .count();
5588 if !has_rest {
5589 let cond = Expr {
5590 kind: ExprKind::BinOp {
5591 left: Box::new(destructure_expr_array_len(&tmp, line)),
5592 op: BinOp::NumEq,
5593 right: Box::new(Expr {
5594 kind: ExprKind::Integer(fixed_slots as i64),
5595 line,
5596 }),
5597 },
5598 line,
5599 };
5600 stmts.push(destructure_stmt_unless_die(
5601 line,
5602 cond,
5603 "array destructure: length mismatch",
5604 ));
5605 }
5606
5607 let mut idx: i64 = 0;
5608 for elem in elems {
5609 match elem {
5610 MatchArrayElem::Rest => break,
5611 MatchArrayElem::RestBind(name) => {
5612 let list_source = Expr {
5613 kind: ExprKind::Deref {
5614 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5615 kind: Sigil::Array,
5616 },
5617 line,
5618 };
5619 let last_ix = Expr {
5620 kind: ExprKind::BinOp {
5621 left: Box::new(destructure_expr_array_len(&tmp, line)),
5622 op: BinOp::Sub,
5623 right: Box::new(Expr {
5624 kind: ExprKind::Integer(1),
5625 line,
5626 }),
5627 },
5628 line,
5629 };
5630 let range = Expr {
5631 kind: ExprKind::Range {
5632 from: Box::new(Expr {
5633 kind: ExprKind::Integer(idx),
5634 line,
5635 }),
5636 to: Box::new(last_ix),
5637 exclusive: false,
5638 step: None,
5639 },
5640 line,
5641 };
5642 let slice = Expr {
5643 kind: ExprKind::AnonymousListSlice {
5644 source: Box::new(list_source),
5645 indices: vec![range],
5646 },
5647 line,
5648 };
5649 stmts.push(destructure_stmt_from_var_decls(
5650 keyword,
5651 vec![VarDecl {
5652 sigil: Sigil::Array,
5653 name,
5654 initializer: Some(slice),
5655 frozen: false,
5656 type_annotation: None,
5657 list_context: false,
5658 }],
5659 line,
5660 ));
5661 break;
5662 }
5663 MatchArrayElem::CaptureScalar(name) => {
5664 let arrow = Expr {
5665 kind: ExprKind::ArrowDeref {
5666 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5667 index: Box::new(Expr {
5668 kind: ExprKind::Integer(idx),
5669 line,
5670 }),
5671 kind: DerefKind::Array,
5672 },
5673 line,
5674 };
5675 stmts.push(destructure_stmt_from_var_decls(
5676 keyword,
5677 vec![VarDecl {
5678 sigil: Sigil::Scalar,
5679 name,
5680 initializer: Some(arrow),
5681 frozen: false,
5682 list_context: false,
5683 type_annotation: None,
5684 }],
5685 line,
5686 ));
5687 idx += 1;
5688 }
5689 MatchArrayElem::Expr(e) => {
5690 let elem_subj = Expr {
5691 kind: ExprKind::ArrowDeref {
5692 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5693 index: Box::new(Expr {
5694 kind: ExprKind::Integer(idx),
5695 line,
5696 }),
5697 kind: DerefKind::Array,
5698 },
5699 line,
5700 };
5701 let match_expr = Expr {
5702 kind: ExprKind::AlgebraicMatch {
5703 subject: Box::new(elem_subj),
5704 arms: vec![
5705 MatchArm {
5706 pattern: MatchPattern::Value(Box::new(e.clone())),
5707 guard: None,
5708 body: Expr {
5709 kind: ExprKind::Integer(0),
5710 line,
5711 },
5712 },
5713 MatchArm {
5714 pattern: MatchPattern::Any,
5715 guard: None,
5716 body: Expr {
5717 kind: ExprKind::Die(vec![Expr {
5718 kind: ExprKind::String(
5719 "array destructure: element pattern mismatch"
5720 .to_string(),
5721 ),
5722 line,
5723 }]),
5724 line,
5725 },
5726 },
5727 ],
5728 },
5729 line,
5730 };
5731 stmts.push(Statement {
5732 label: None,
5733 kind: StmtKind::Expression(match_expr),
5734 line,
5735 });
5736 idx += 1;
5737 }
5738 }
5739 }
5740
5741 Ok(Statement {
5742 label: None,
5743 kind: StmtKind::StmtGroup(stmts),
5744 line,
5745 })
5746 }
5747
5748 fn desugar_hash_destructure(
5749 &mut self,
5750 keyword: &str,
5751 line: usize,
5752 pairs: Vec<MatchHashPair>,
5753 rhs: Expr,
5754 ) -> StrykeResult<Statement> {
5755 let tmp = format!("__stryke_ds_{}", self.alloc_desugar_tmp());
5756 let mut stmts: Vec<Statement> = Vec::new();
5757 stmts.push(destructure_stmt_from_var_decls(
5758 keyword,
5759 vec![VarDecl {
5760 sigil: Sigil::Scalar,
5761 name: tmp.clone(),
5762 initializer: Some(rhs),
5763 frozen: false,
5764 type_annotation: None,
5765 list_context: false,
5766 }],
5767 line,
5768 ));
5769
5770 for pair in pairs {
5771 match pair {
5772 MatchHashPair::KeyOnly { key } => {
5773 let exists_op = Expr {
5774 kind: ExprKind::Exists(Box::new(Expr {
5775 kind: ExprKind::ArrowDeref {
5776 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5777 index: Box::new(key),
5778 kind: DerefKind::Hash,
5779 },
5780 line,
5781 })),
5782 line,
5783 };
5784 stmts.push(destructure_stmt_unless_die(
5785 line,
5786 exists_op,
5787 "hash destructure: missing required key",
5788 ));
5789 }
5790 MatchHashPair::Capture { key, name } => {
5791 let init = Expr {
5792 kind: ExprKind::ArrowDeref {
5793 expr: Box::new(destructure_expr_scalar_tmp(&tmp, line)),
5794 index: Box::new(key),
5795 kind: DerefKind::Hash,
5796 },
5797 line,
5798 };
5799 stmts.push(destructure_stmt_from_var_decls(
5800 keyword,
5801 vec![VarDecl {
5802 sigil: Sigil::Scalar,
5803 name,
5804 initializer: Some(init),
5805 frozen: false,
5806 type_annotation: None,
5807 list_context: false,
5808 }],
5809 line,
5810 ));
5811 }
5812 }
5813 }
5814
5815 Ok(Statement {
5816 label: None,
5817 kind: StmtKind::StmtGroup(stmts),
5818 line,
5819 })
5820 }
5821
5822 fn parse_my_our_local(
5823 &mut self,
5824 keyword: &str,
5825 allow_type_annotation: bool,
5826 ) -> StrykeResult<Statement> {
5827 let line = self.peek_line();
5828 self.advance(); if keyword == "local"
5831 && !matches!(self.peek(), Token::LParen | Token::LBracket | Token::LBrace)
5832 {
5833 let target = self.parse_postfix()?;
5834 let mut initializer: Option<Expr> = None;
5835 if self.eat(&Token::Assign) {
5836 initializer = Some(self.parse_expression()?);
5837 } else if matches!(
5838 self.peek(),
5839 Token::OrAssign | Token::DefinedOrAssign | Token::AndAssign
5840 ) {
5841 if matches!(&target.kind, ExprKind::Typeglob(_)) {
5842 return Err(self.syntax_err(
5843 "compound assignment on typeglob declaration is not supported",
5844 self.peek_line(),
5845 ));
5846 }
5847 let op = match self.peek().clone() {
5848 Token::OrAssign => BinOp::LogOr,
5849 Token::DefinedOrAssign => BinOp::DefinedOr,
5850 Token::AndAssign => BinOp::LogAnd,
5851 _ => unreachable!(),
5852 };
5853 self.advance();
5854 let rhs = self.parse_assign_expr()?;
5855 let tgt_line = target.line;
5856 initializer = Some(Expr {
5857 kind: ExprKind::CompoundAssign {
5858 target: Box::new(target.clone()),
5859 op,
5860 value: Box::new(rhs),
5861 },
5862 line: tgt_line,
5863 });
5864 }
5865
5866 let kind = if let Some(mut decl) = Self::local_simple_target_to_var_decl(&target) {
5867 decl.initializer = initializer;
5868 StmtKind::Local(vec![decl])
5869 } else {
5870 StmtKind::LocalExpr {
5871 target,
5872 initializer,
5873 }
5874 };
5875 let stmt = Statement {
5876 label: None,
5877 kind,
5878 line,
5879 };
5880 return self.parse_stmt_postfix_modifier(stmt);
5881 }
5882
5883 if matches!(self.peek(), Token::LBracket) {
5884 return self.parse_decl_array_destructure(keyword, line);
5885 }
5886 if matches!(self.peek(), Token::LBrace) {
5887 return self.parse_decl_hash_destructure(keyword, line);
5888 }
5889
5890 let mut decls = Vec::new();
5891 let used_parens = self.eat(&Token::LParen);
5892
5893 if used_parens {
5894 while !matches!(self.peek(), Token::RParen | Token::Eof) {
5896 let decl = self.parse_var_decl(allow_type_annotation)?;
5897 decls.push(decl);
5898 if !self.eat(&Token::Comma) {
5899 break;
5900 }
5901 }
5902 self.expect(&Token::RParen)?;
5903 } else {
5904 decls.push(self.parse_var_decl(allow_type_annotation)?);
5905 }
5906 if used_parens {
5908 for decl in &mut decls {
5909 decl.list_context = true;
5910 }
5911 }
5912
5913 if self.eat(&Token::Assign) {
5915 if keyword == "our" && decls.len() == 1 {
5916 while matches!(self.peek(), Token::Ident(ref i) if i == "our") {
5917 self.advance();
5918 decls.push(self.parse_var_decl(allow_type_annotation)?);
5919 if !self.eat(&Token::Assign) {
5920 return Err(self.syntax_err(
5921 "expected `=` after `our` in chained our-declaration",
5922 self.peek_line(),
5923 ));
5924 }
5925 }
5926 }
5927 let rhs_start_pos = self.pos;
5928 let mut val = self.parse_expression()?;
5929 let rhs_end_pos = self.pos;
5930 if !crate::compat_mode()
5951 && self.block_depth == 0
5952 && decls.len() == 1
5953 && matches!(decls[0].sigil, Sigil::Scalar)
5954 && !matches!(
5955 val.kind,
5956 ExprKind::CodeRef { .. }
5957 | ExprKind::SubroutineRef(_)
5958 | ExprKind::SubroutineCodeRef(_)
5959 | ExprKind::DynamicSubCodeRef(_)
5960 )
5961 && self.rhs_has_free_bare_topic_slot(rhs_start_pos, rhs_end_pos)
5962 {
5963 let val_line = val.line;
5964 val = Expr {
5965 kind: ExprKind::CodeRef {
5966 params: Vec::new(),
5967 body: vec![Statement {
5968 label: None,
5969 kind: StmtKind::Expression(val),
5970 line: val_line,
5971 }],
5972 },
5973 line: val_line,
5974 };
5975 }
5976 if !crate::compat_mode() && decls.len() == 1 {
5979 let decl = &decls[0];
5980 let target_kind = match decl.sigil {
5981 Sigil::Scalar => ExprKind::ScalarVar(decl.name.clone()),
5982 Sigil::Array => ExprKind::ArrayVar(decl.name.clone()),
5983 Sigil::Hash => ExprKind::HashVar(decl.name.clone()),
5984 Sigil::Typeglob => {
5985 if decls.len() == 1 {
5987 decls[0].initializer = Some(val);
5988 } else {
5989 for d in &mut decls {
5990 d.initializer = Some(val.clone());
5991 }
5992 }
5993 return Ok(Statement {
5994 label: None,
5995 kind: match keyword {
5996 "my" => StmtKind::My(decls),
5997 "mysync" => StmtKind::MySync(decls),
5998 "our" => StmtKind::Our(decls),
5999 "oursync" => StmtKind::OurSync(decls),
6000 "local" => StmtKind::Local(decls),
6001 "state" => StmtKind::State(decls),
6002 _ => unreachable!(),
6003 },
6004 line,
6005 });
6006 }
6007 };
6008 let target = Expr {
6009 kind: target_kind,
6010 line,
6011 };
6012 self.validate_assignment(&target, &val, line)?;
6013 }
6014 if decls.len() == 1 {
6015 decls[0].initializer = Some(val);
6016 } else {
6017 for decl in &mut decls {
6018 decl.initializer = Some(val.clone());
6019 }
6020 }
6021 } else if decls.len() == 1 {
6022 let op = match self.peek().clone() {
6024 Token::OrAssign => Some(BinOp::LogOr),
6025 Token::DefinedOrAssign => Some(BinOp::DefinedOr),
6026 Token::AndAssign => Some(BinOp::LogAnd),
6027 _ => None,
6028 };
6029 if let Some(op) = op {
6030 let d = &decls[0];
6031 if matches!(d.sigil, Sigil::Typeglob) {
6032 return Err(self.syntax_err(
6033 "compound assignment on typeglob declaration is not supported",
6034 self.peek_line(),
6035 ));
6036 }
6037 self.advance();
6038 let rhs = self.parse_assign_expr()?;
6039 let target = Expr {
6040 kind: match d.sigil {
6041 Sigil::Scalar => ExprKind::ScalarVar(d.name.clone()),
6042 Sigil::Array => ExprKind::ArrayVar(d.name.clone()),
6043 Sigil::Hash => ExprKind::HashVar(d.name.clone()),
6044 Sigil::Typeglob => unreachable!(),
6045 },
6046 line,
6047 };
6048 decls[0].initializer = Some(Expr {
6049 kind: ExprKind::CompoundAssign {
6050 target: Box::new(target),
6051 op,
6052 value: Box::new(rhs),
6053 },
6054 line,
6055 });
6056 }
6057 }
6058
6059 let kind = match keyword {
6060 "my" => StmtKind::My(decls),
6061 "mysync" => StmtKind::MySync(decls),
6062 "our" => StmtKind::Our(decls),
6063 "oursync" => StmtKind::OurSync(decls),
6064 "local" => StmtKind::Local(decls),
6065 "state" => StmtKind::State(decls),
6066 _ => unreachable!(),
6067 };
6068 let stmt = Statement {
6069 label: None,
6070 kind,
6071 line,
6072 };
6073 self.parse_stmt_postfix_modifier(stmt)
6075 }
6076
6077 fn parse_var_decl(&mut self, allow_type_annotation: bool) -> StrykeResult<VarDecl> {
6078 let mut decl = match self.advance() {
6079 (Token::ScalarVar(name), _) => VarDecl {
6080 sigil: Sigil::Scalar,
6081 name,
6082 initializer: None,
6083 frozen: false,
6084 type_annotation: None,
6085 list_context: false,
6086 },
6087 (Token::ArrayVar(name), _) => VarDecl {
6088 sigil: Sigil::Array,
6089 name,
6090 initializer: None,
6091 frozen: false,
6092 type_annotation: None,
6093 list_context: false,
6094 },
6095 (Token::HashVar(name), line) => {
6096 if !crate::compat_mode() {
6097 self.check_hash_shadows_reserved(&name, line)?;
6098 }
6099 VarDecl {
6100 sigil: Sigil::Hash,
6101 name,
6102 initializer: None,
6103 frozen: false,
6104 type_annotation: None,
6105 list_context: false,
6106 }
6107 }
6108 (Token::Star, _line) => {
6109 let name = match self.advance() {
6110 (Token::Ident(n), _) => n,
6111 (tok, l) => {
6112 return Err(self
6113 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
6114 }
6115 };
6116 VarDecl {
6117 sigil: Sigil::Typeglob,
6118 name,
6119 initializer: None,
6120 frozen: false,
6121 type_annotation: None,
6122 list_context: false,
6123 }
6124 }
6125 (Token::Ident(ref kw), _) if kw == "undef" => VarDecl {
6130 sigil: Sigil::Scalar,
6131 name: format!("__undef_sink_{}", self.pos),
6135 initializer: None,
6136 frozen: false,
6137 type_annotation: None,
6138 list_context: false,
6139 },
6140 (tok, line) => {
6141 return Err(self.syntax_err(
6142 format!("Expected variable in declaration, got {:?}", tok),
6143 line,
6144 ));
6145 }
6146 };
6147 if allow_type_annotation && self.eat(&Token::Colon) {
6148 let ty = self.parse_type_name()?;
6149 if decl.sigil != Sigil::Scalar {
6150 return Err(self.syntax_err(
6151 "`: Type` is only valid for scalar declarations (typed my $name : Int)",
6152 self.peek_line(),
6153 ));
6154 }
6155 decl.type_annotation = Some(ty);
6156 }
6157 Ok(decl)
6158 }
6159
6160 fn parse_type_name(&mut self) -> StrykeResult<PerlTypeName> {
6161 match self.advance() {
6162 (Token::Ident(name), _) => match name.as_str() {
6163 "Int" => Ok(PerlTypeName::Int),
6164 "Str" => Ok(PerlTypeName::Str),
6165 "Float" => Ok(PerlTypeName::Float),
6166 "Bool" => Ok(PerlTypeName::Bool),
6167 "Array" => Ok(PerlTypeName::Array),
6168 "Hash" => Ok(PerlTypeName::Hash),
6169 "Ref" => Ok(PerlTypeName::Ref),
6170 "Any" => Ok(PerlTypeName::Any),
6171 _ => Ok(PerlTypeName::Struct(name)),
6172 },
6173 (tok, err_line) => Err(self.syntax_err(
6174 format!("Expected type name after `:`, got {:?}", tok),
6175 err_line,
6176 )),
6177 }
6178 }
6179
6180 fn parse_package(&mut self) -> StrykeResult<Statement> {
6181 let line = self.peek_line();
6182 self.advance(); let name = match self.advance() {
6184 (Token::Ident(n), _) => n,
6185 (tok, line) => {
6186 return Err(self.syntax_err(format!("Expected package name, got {:?}", tok), line))
6187 }
6188 };
6189 let mut full_name = name;
6191 while self.eat(&Token::PackageSep) {
6192 if let (Token::Ident(part), _) = self.advance() {
6193 full_name = format!("{}::{}", full_name, part);
6194 }
6195 }
6196 self.eat(&Token::Semicolon);
6197 self.current_package = full_name.clone();
6200 Ok(Statement {
6201 label: None,
6202 kind: StmtKind::Package { name: full_name },
6203 line,
6204 })
6205 }
6206
6207 fn parse_use(&mut self) -> StrykeResult<Statement> {
6208 let line = self.peek_line();
6209 self.advance(); let (tok, tok_line) = self.advance();
6211 match tok {
6212 Token::Float(v) => {
6213 self.eat(&Token::Semicolon);
6214 Ok(Statement {
6215 label: None,
6216 kind: StmtKind::UsePerlVersion { version: v },
6217 line,
6218 })
6219 }
6220 Token::Integer(n) => {
6221 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6222 self.eat(&Token::Semicolon);
6223 Ok(Statement {
6224 label: None,
6225 kind: StmtKind::UsePerlVersion { version: n as f64 },
6226 line,
6227 })
6228 } else {
6229 Err(self.syntax_err(
6230 format!("Expected ';' after use VERSION (got {:?})", self.peek()),
6231 line,
6232 ))
6233 }
6234 }
6235 Token::Ident(n) => {
6236 let mut full_name = n;
6237 while self.eat(&Token::PackageSep) {
6238 if let (Token::Ident(part), _) = self.advance() {
6239 full_name = format!("{}::{}", full_name, part);
6240 }
6241 }
6242 if full_name == "overload" {
6243 let mut pairs = Vec::new();
6244 let mut parse_overload_pairs = |this: &mut Self| -> StrykeResult<()> {
6245 loop {
6246 if matches!(this.peek(), Token::RParen | Token::Semicolon | Token::Eof)
6247 {
6248 break;
6249 }
6250 let key_e = this.parse_assign_expr()?;
6251 this.expect(&Token::FatArrow)?;
6252 let val_e = this.parse_assign_expr()?;
6253 let key = this.expr_to_overload_key(&key_e)?;
6254 let val = this.expr_to_overload_sub(&val_e)?;
6255 pairs.push((key, val));
6256 if !this.eat(&Token::Comma) {
6257 break;
6258 }
6259 }
6260 Ok(())
6261 };
6262 if self.eat(&Token::LParen) {
6263 parse_overload_pairs(self)?;
6265 self.expect(&Token::RParen)?;
6266 } else if !matches!(self.peek(), Token::Semicolon | Token::Eof) {
6267 parse_overload_pairs(self)?;
6268 }
6269 self.eat(&Token::Semicolon);
6270 return Ok(Statement {
6271 label: None,
6272 kind: StmtKind::UseOverload { pairs },
6273 line,
6274 });
6275 }
6276 let mut imports = Vec::new();
6277 let on_same_line = self.peek_line() == tok_line;
6285 if on_same_line
6286 && !matches!(self.peek(), Token::Semicolon | Token::Eof)
6287 && !self.next_is_new_statement_start(tok_line)
6288 {
6289 loop {
6290 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6291 break;
6292 }
6293 imports.push(self.parse_expression()?);
6294 if !self.eat(&Token::Comma) {
6295 break;
6296 }
6297 }
6298 }
6299 self.eat(&Token::Semicolon);
6300 Ok(Statement {
6301 label: None,
6302 kind: StmtKind::Use {
6303 module: full_name,
6304 imports,
6305 },
6306 line,
6307 })
6308 }
6309 other => Err(self.syntax_err(
6310 format!("Expected module name or version after use, got {:?}", other),
6311 tok_line,
6312 )),
6313 }
6314 }
6315
6316 fn parse_no(&mut self) -> StrykeResult<Statement> {
6317 let line = self.peek_line();
6318 self.advance(); let module = match self.advance() {
6320 (Token::Ident(n), tok_line) => (n, tok_line),
6321 (tok, line) => {
6322 return Err(self.syntax_err(
6323 format!("Expected module name after no, got {:?}", tok),
6324 line,
6325 ))
6326 }
6327 };
6328 let (module_name, tok_line) = module;
6329 let mut full_name = module_name;
6330 while self.eat(&Token::PackageSep) {
6331 if let (Token::Ident(part), _) = self.advance() {
6332 full_name = format!("{}::{}", full_name, part);
6333 }
6334 }
6335 let mut imports = Vec::new();
6336 if !matches!(self.peek(), Token::Semicolon | Token::Eof)
6337 && !self.next_is_new_statement_start(tok_line)
6338 {
6339 loop {
6340 if matches!(self.peek(), Token::Semicolon | Token::Eof) {
6341 break;
6342 }
6343 imports.push(self.parse_expression()?);
6344 if !self.eat(&Token::Comma) {
6345 break;
6346 }
6347 }
6348 }
6349 self.eat(&Token::Semicolon);
6350 Ok(Statement {
6351 label: None,
6352 kind: StmtKind::No {
6353 module: full_name,
6354 imports,
6355 },
6356 line,
6357 })
6358 }
6359
6360 fn parse_return(&mut self) -> StrykeResult<Statement> {
6361 let line = self.peek_line();
6362 self.advance(); let val = if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6369 || self.peek_is_postfix_stmt_modifier_keyword()
6370 {
6371 None
6372 } else {
6373 let first = self.parse_assign_expr()?;
6378 if matches!(self.peek(), Token::Comma | Token::FatArrow) {
6379 let mut items = vec![first];
6380 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6381 if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
6382 || self.peek_is_postfix_stmt_modifier_keyword()
6383 {
6384 break;
6385 }
6386 items.push(self.parse_assign_expr()?);
6387 }
6388 let line = items.first().map(|e| e.line).unwrap_or(line);
6389 Some(Expr {
6390 kind: ExprKind::List(items),
6391 line,
6392 })
6393 } else {
6394 Some(first)
6395 }
6396 };
6397 let stmt = Statement {
6399 label: None,
6400 kind: StmtKind::Return(val),
6401 line,
6402 };
6403 if let Token::Ident(ref kw) = self.peek().clone() {
6404 match kw.as_str() {
6405 "if" => {
6406 self.advance();
6407 let cond = self.parse_expression()?;
6408 self.eat(&Token::Semicolon);
6409 return Ok(Statement {
6410 label: None,
6411 kind: StmtKind::If {
6412 condition: cond,
6413 body: vec![stmt],
6414 elsifs: vec![],
6415 else_block: None,
6416 },
6417 line,
6418 });
6419 }
6420 "unless" => {
6421 self.advance();
6422 let cond = self.parse_expression()?;
6423 self.eat(&Token::Semicolon);
6424 return Ok(Statement {
6425 label: None,
6426 kind: StmtKind::Unless {
6427 condition: cond,
6428 body: vec![stmt],
6429 else_block: None,
6430 },
6431 line,
6432 });
6433 }
6434 _ => {}
6435 }
6436 }
6437 self.eat(&Token::Semicolon);
6438 Ok(stmt)
6439 }
6440
6441 fn parse_expression(&mut self) -> StrykeResult<Expr> {
6444 self.parse_comma_expr()
6445 }
6446
6447 fn parse_comma_expr(&mut self) -> StrykeResult<Expr> {
6448 let expr = self.parse_or_word()?;
6456 let mut exprs = vec![expr];
6457 while self.eat(&Token::Comma) || self.eat(&Token::FatArrow) {
6458 if matches!(
6459 self.peek(),
6460 Token::RParen | Token::RBracket | Token::RBrace | Token::Semicolon | Token::Eof
6461 ) {
6462 break; }
6464 exprs.push(self.parse_or_word()?);
6465 }
6466 if exprs.len() == 1 {
6467 return Ok(exprs.pop().unwrap());
6468 }
6469 let line = exprs[0].line;
6470 Ok(Expr {
6471 kind: ExprKind::List(exprs),
6472 line,
6473 })
6474 }
6475
6476 fn parse_assign_expr(&mut self) -> StrykeResult<Expr> {
6477 let expr = self.parse_ternary()?;
6478 let line = expr.line;
6479
6480 match self.peek().clone() {
6481 Token::Assign => {
6482 self.advance();
6483 let right = self.parse_assign_expr()?;
6484 if let ExprKind::MethodCall { ref args, .. } = expr.kind {
6486 if args.is_empty() {
6487 let ExprKind::MethodCall {
6489 object,
6490 method,
6491 super_call,
6492 ..
6493 } = expr.kind
6494 else {
6495 unreachable!()
6496 };
6497 return Ok(Expr {
6498 kind: ExprKind::MethodCall {
6499 object,
6500 method,
6501 args: vec![right],
6502 super_call,
6503 },
6504 line,
6505 });
6506 }
6507 }
6508 self.validate_assignment(&expr, &right, line)?;
6509 Ok(Expr {
6510 kind: ExprKind::Assign {
6511 target: Box::new(expr),
6512 value: Box::new(right),
6513 },
6514 line,
6515 })
6516 }
6517 Token::PlusAssign => {
6518 self.advance();
6519 let r = self.parse_assign_expr()?;
6520 Ok(Expr {
6521 kind: ExprKind::CompoundAssign {
6522 target: Box::new(expr),
6523 op: BinOp::Add,
6524 value: Box::new(r),
6525 },
6526 line,
6527 })
6528 }
6529 Token::MinusAssign => {
6530 self.advance();
6531 let r = self.parse_assign_expr()?;
6532 Ok(Expr {
6533 kind: ExprKind::CompoundAssign {
6534 target: Box::new(expr),
6535 op: BinOp::Sub,
6536 value: Box::new(r),
6537 },
6538 line,
6539 })
6540 }
6541 Token::MulAssign => {
6542 self.advance();
6543 let r = self.parse_assign_expr()?;
6544 Ok(Expr {
6545 kind: ExprKind::CompoundAssign {
6546 target: Box::new(expr),
6547 op: BinOp::Mul,
6548 value: Box::new(r),
6549 },
6550 line,
6551 })
6552 }
6553 Token::DivAssign => {
6554 self.advance();
6555 let r = self.parse_assign_expr()?;
6556 Ok(Expr {
6557 kind: ExprKind::CompoundAssign {
6558 target: Box::new(expr),
6559 op: BinOp::Div,
6560 value: Box::new(r),
6561 },
6562 line,
6563 })
6564 }
6565 Token::ModAssign => {
6566 self.advance();
6567 let r = self.parse_assign_expr()?;
6568 Ok(Expr {
6569 kind: ExprKind::CompoundAssign {
6570 target: Box::new(expr),
6571 op: BinOp::Mod,
6572 value: Box::new(r),
6573 },
6574 line,
6575 })
6576 }
6577 Token::PowAssign => {
6578 self.advance();
6579 let r = self.parse_assign_expr()?;
6580 Ok(Expr {
6581 kind: ExprKind::CompoundAssign {
6582 target: Box::new(expr),
6583 op: BinOp::Pow,
6584 value: Box::new(r),
6585 },
6586 line,
6587 })
6588 }
6589 Token::XAssign => {
6590 self.advance();
6595 let r = self.parse_assign_expr()?;
6596 let lhs_for_repeat = expr.clone();
6597 Ok(Expr {
6598 kind: ExprKind::Assign {
6599 target: Box::new(expr),
6600 value: Box::new(Expr {
6601 kind: ExprKind::Repeat {
6602 expr: Box::new(lhs_for_repeat),
6603 count: Box::new(r),
6604 list_repeat: false,
6605 },
6606 line,
6607 }),
6608 },
6609 line,
6610 })
6611 }
6612 Token::DotAssign => {
6613 self.advance();
6614 let r = self.parse_assign_expr()?;
6615 Ok(Expr {
6616 kind: ExprKind::CompoundAssign {
6617 target: Box::new(expr),
6618 op: BinOp::Concat,
6619 value: Box::new(r),
6620 },
6621 line,
6622 })
6623 }
6624 Token::BitAndAssign => {
6625 self.advance();
6626 let r = self.parse_assign_expr()?;
6627 Ok(Expr {
6628 kind: ExprKind::CompoundAssign {
6629 target: Box::new(expr),
6630 op: BinOp::BitAnd,
6631 value: Box::new(r),
6632 },
6633 line,
6634 })
6635 }
6636 Token::BitOrAssign => {
6637 self.advance();
6638 let r = self.parse_assign_expr()?;
6639 Ok(Expr {
6640 kind: ExprKind::CompoundAssign {
6641 target: Box::new(expr),
6642 op: BinOp::BitOr,
6643 value: Box::new(r),
6644 },
6645 line,
6646 })
6647 }
6648 Token::XorAssign => {
6649 self.advance();
6650 let r = self.parse_assign_expr()?;
6651 Ok(Expr {
6652 kind: ExprKind::CompoundAssign {
6653 target: Box::new(expr),
6654 op: BinOp::BitXor,
6655 value: Box::new(r),
6656 },
6657 line,
6658 })
6659 }
6660 Token::ShiftLeftAssign => {
6661 self.advance();
6662 let r = self.parse_assign_expr()?;
6663 Ok(Expr {
6664 kind: ExprKind::CompoundAssign {
6665 target: Box::new(expr),
6666 op: BinOp::ShiftLeft,
6667 value: Box::new(r),
6668 },
6669 line,
6670 })
6671 }
6672 Token::ShiftRightAssign => {
6673 self.advance();
6674 let r = self.parse_assign_expr()?;
6675 Ok(Expr {
6676 kind: ExprKind::CompoundAssign {
6677 target: Box::new(expr),
6678 op: BinOp::ShiftRight,
6679 value: Box::new(r),
6680 },
6681 line,
6682 })
6683 }
6684 Token::OrAssign => {
6685 self.advance();
6686 let r = self.parse_assign_expr()?;
6687 Ok(Expr {
6688 kind: ExprKind::CompoundAssign {
6689 target: Box::new(expr),
6690 op: BinOp::LogOr,
6691 value: Box::new(r),
6692 },
6693 line,
6694 })
6695 }
6696 Token::DefinedOrAssign => {
6697 self.advance();
6698 let r = self.parse_assign_expr()?;
6699 Ok(Expr {
6700 kind: ExprKind::CompoundAssign {
6701 target: Box::new(expr),
6702 op: BinOp::DefinedOr,
6703 value: Box::new(r),
6704 },
6705 line,
6706 })
6707 }
6708 Token::AndAssign => {
6709 self.advance();
6710 let r = self.parse_assign_expr()?;
6711 Ok(Expr {
6712 kind: ExprKind::CompoundAssign {
6713 target: Box::new(expr),
6714 op: BinOp::LogAnd,
6715 value: Box::new(r),
6716 },
6717 line,
6718 })
6719 }
6720 _ => Ok(expr),
6721 }
6722 }
6723
6724 fn parse_ternary(&mut self) -> StrykeResult<Expr> {
6725 let expr = self.parse_pipe_forward()?;
6726 if self.eat(&Token::Question) {
6727 let line = expr.line;
6728 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
6729 let then_expr = self.parse_assign_expr();
6730 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
6731 let then_expr = then_expr?;
6732 self.expect(&Token::Colon)?;
6733 let else_expr = self.parse_assign_expr()?;
6734 return Ok(Expr {
6735 kind: ExprKind::Ternary {
6736 condition: Box::new(expr),
6737 then_expr: Box::new(then_expr),
6738 else_expr: Box::new(else_expr),
6739 },
6740 line,
6741 });
6742 }
6743 Ok(expr)
6744 }
6745
6746 fn parse_pipe_forward(&mut self) -> StrykeResult<Expr> {
6752 let mut left = self.parse_range()?;
6759 if self.no_pipe_forward_depth > 0 {
6765 return Ok(left);
6766 }
6767 while matches!(self.peek(), Token::PipeForward) {
6768 if crate::compat_mode() {
6769 return Err(self.syntax_err(
6770 "pipe-forward operator `|>` is a stryke extension (disabled by --compat)",
6771 left.line,
6772 ));
6773 }
6774 let line = left.line;
6775 self.advance();
6776 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_add(1);
6779 let right_result = self.parse_range();
6783 self.pipe_rhs_depth = self.pipe_rhs_depth.saturating_sub(1);
6784 let right = right_result?;
6785 left = self.pipe_forward_apply(left, right, line)?;
6786 }
6787 Ok(left)
6788 }
6789
6790 fn pipe_forward_apply(&self, lhs: Expr, rhs: Expr, line: usize) -> StrykeResult<Expr> {
6812 let Expr { kind, line: rline } = rhs;
6813 let new_kind = match kind {
6814 ExprKind::FuncCall { name, mut args } => {
6816 let dispatch_name: &str = name.strip_prefix("CORE::").unwrap_or(name.as_str());
6819 match dispatch_name {
6820 "puniq" | "uniq" | "distinct" | "flatten" | "set" | "list_count"
6821 | "list_size" | "count" | "size" | "cnt" | "len" | "with_index" | "shuffle"
6822 | "shuffled" | "frequencies" | "freq" | "pfrequencies" | "pfreq"
6823 | "interleave" | "ddump" | "stringify" | "str" | "lines" | "words"
6824 | "chars" | "digits" | "letters" | "letters_uc" | "letters_lc"
6825 | "punctuation" | "numbers" | "graphemes" | "columns" | "sentences"
6826 | "paragraphs" | "sections" | "trim" | "avg" | "to_json" | "to_csv"
6827 | "to_toml" | "to_yaml" | "to_xml" | "to_html" | "from_json" | "from_csv"
6828 | "from_toml" | "from_yaml" | "from_xml" | "to_markdown" | "to_table"
6829 | "xopen" | "clip" | "sparkline" | "bar_chart" | "flame" | "stddev"
6830 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "normalize"
6831 | "snake_case" | "camel_case" | "kebab_case" => {
6832 if args.is_empty() {
6833 args.push(lhs);
6834 } else {
6835 args[0] = lhs;
6836 }
6837 }
6838 "chunked" | "windowed" => {
6839 if args.is_empty() {
6840 return Err(self.syntax_err(
6841 "|>: chunked(N) / windowed(N) needs size — e.g. `@a |> windowed(2)`",
6842 line,
6843 ));
6844 }
6845 args.insert(0, lhs);
6846 }
6847 "reduce" | "fold" => {
6848 args.push(lhs);
6849 }
6850 "grep_v" | "pluck" | "tee" | "nth" | "chunk" => {
6851 args.push(lhs);
6857 }
6858 "enumerate" | "dedup" => {
6859 args.insert(0, lhs);
6862 }
6863 "clamp" => {
6864 args.push(lhs);
6866 }
6867 n if Self::is_block_then_list_pipe_builtin(n) => {
6868 if args.len() < 2 {
6869 return Err(self.syntax_err(
6870 format!(
6871 "|>: `{name}` needs {{ BLOCK }}, LIST so the list can receive the pipe"
6872 ),
6873 line,
6874 ));
6875 }
6876 args[1] = lhs;
6877 }
6878 "take" | "head" | "tail" | "drop" => {
6879 if args.is_empty() {
6880 return Err(self.syntax_err(
6881 "|>: `{name}` needs N last — e.g. `@a |> take(3)` for `take(@a, 3)`",
6882 line,
6883 ));
6884 }
6885 args.insert(0, lhs);
6887 }
6888 _ => {
6889 if self.thread_last_mode {
6890 args.push(lhs);
6891 } else {
6892 args.insert(0, lhs);
6893 }
6894 }
6895 }
6896 ExprKind::FuncCall { name, args }
6897 }
6898 ExprKind::MethodCall {
6899 object,
6900 method,
6901 mut args,
6902 super_call,
6903 } => {
6904 if self.thread_last_mode {
6905 args.push(lhs);
6906 } else {
6907 args.insert(0, lhs);
6908 }
6909 ExprKind::MethodCall {
6910 object,
6911 method,
6912 args,
6913 super_call,
6914 }
6915 }
6916 ExprKind::IndirectCall {
6917 target,
6918 mut args,
6919 ampersand,
6920 pass_caller_arglist: _,
6921 } => {
6922 if self.thread_last_mode {
6923 args.push(lhs);
6924 } else {
6925 args.insert(0, lhs);
6926 }
6927 ExprKind::IndirectCall {
6928 target,
6929 args,
6930 ampersand,
6931 pass_caller_arglist: false,
6934 }
6935 }
6936
6937 ExprKind::Print { handle, mut args } => {
6939 if self.thread_last_mode {
6940 args.push(lhs);
6941 } else {
6942 args.insert(0, lhs);
6943 }
6944 ExprKind::Print { handle, args }
6945 }
6946 ExprKind::Say { handle, mut args } => {
6947 if self.thread_last_mode {
6948 args.push(lhs);
6949 } else {
6950 args.insert(0, lhs);
6951 }
6952 ExprKind::Say { handle, args }
6953 }
6954 ExprKind::Printf { handle, mut args } => {
6955 if self.thread_last_mode {
6956 args.push(lhs);
6957 } else {
6958 args.insert(0, lhs);
6959 }
6960 ExprKind::Printf { handle, args }
6961 }
6962 ExprKind::Die(mut args) => {
6963 if self.thread_last_mode {
6964 args.push(lhs);
6965 } else {
6966 args.insert(0, lhs);
6967 }
6968 ExprKind::Die(args)
6969 }
6970 ExprKind::Warn(mut args) => {
6971 if self.thread_last_mode {
6972 args.push(lhs);
6973 } else {
6974 args.insert(0, lhs);
6975 }
6976 ExprKind::Warn(args)
6977 }
6978
6979 ExprKind::Sprintf { format, mut args } => {
6985 if self.thread_last_mode {
6986 args.push(lhs);
6987 } else {
6988 args.insert(0, lhs);
6989 }
6990 ExprKind::Sprintf { format, args }
6991 }
6992
6993 ExprKind::System(mut args) => {
6995 if self.thread_last_mode {
6996 args.push(lhs);
6997 } else {
6998 args.insert(0, lhs);
6999 }
7000 ExprKind::System(args)
7001 }
7002 ExprKind::Exec(mut args) => {
7003 if self.thread_last_mode {
7004 args.push(lhs);
7005 } else {
7006 args.insert(0, lhs);
7007 }
7008 ExprKind::Exec(args)
7009 }
7010 ExprKind::Unlink(mut args) => {
7011 if self.thread_last_mode {
7012 args.push(lhs);
7013 } else {
7014 args.insert(0, lhs);
7015 }
7016 ExprKind::Unlink(args)
7017 }
7018 ExprKind::Chmod(mut args) => {
7019 if self.thread_last_mode {
7020 args.push(lhs);
7021 } else {
7022 args.insert(0, lhs);
7023 }
7024 ExprKind::Chmod(args)
7025 }
7026 ExprKind::Chown(mut args) => {
7027 if self.thread_last_mode {
7028 args.push(lhs);
7029 } else {
7030 args.insert(0, lhs);
7031 }
7032 ExprKind::Chown(args)
7033 }
7034 ExprKind::Glob(mut args) => {
7035 if self.thread_last_mode {
7036 args.push(lhs);
7037 } else {
7038 args.insert(0, lhs);
7039 }
7040 ExprKind::Glob(args)
7041 }
7042 ExprKind::Files(mut args) => {
7043 if self.thread_last_mode {
7044 args.push(lhs);
7045 } else {
7046 args.insert(0, lhs);
7047 }
7048 ExprKind::Files(args)
7049 }
7050 ExprKind::Filesf(mut args) => {
7051 if self.thread_last_mode {
7052 args.push(lhs);
7053 } else {
7054 args.insert(0, lhs);
7055 }
7056 ExprKind::Filesf(args)
7057 }
7058 ExprKind::FilesfRecursive(mut args) => {
7059 if self.thread_last_mode {
7060 args.push(lhs);
7061 } else {
7062 args.insert(0, lhs);
7063 }
7064 ExprKind::FilesfRecursive(args)
7065 }
7066 ExprKind::Dirs(mut args) => {
7067 if self.thread_last_mode {
7068 args.push(lhs);
7069 } else {
7070 args.insert(0, lhs);
7071 }
7072 ExprKind::Dirs(args)
7073 }
7074 ExprKind::DirsRecursive(mut args) => {
7075 if self.thread_last_mode {
7076 args.push(lhs);
7077 } else {
7078 args.insert(0, lhs);
7079 }
7080 ExprKind::DirsRecursive(args)
7081 }
7082 ExprKind::SymLinks(mut args) => {
7083 if self.thread_last_mode {
7084 args.push(lhs);
7085 } else {
7086 args.insert(0, lhs);
7087 }
7088 ExprKind::SymLinks(args)
7089 }
7090 ExprKind::Sockets(mut args) => {
7091 if self.thread_last_mode {
7092 args.push(lhs);
7093 } else {
7094 args.insert(0, lhs);
7095 }
7096 ExprKind::Sockets(args)
7097 }
7098 ExprKind::Pipes(mut args) => {
7099 if self.thread_last_mode {
7100 args.push(lhs);
7101 } else {
7102 args.insert(0, lhs);
7103 }
7104 ExprKind::Pipes(args)
7105 }
7106 ExprKind::BlockDevices(mut args) => {
7107 if self.thread_last_mode {
7108 args.push(lhs);
7109 } else {
7110 args.insert(0, lhs);
7111 }
7112 ExprKind::BlockDevices(args)
7113 }
7114 ExprKind::CharDevices(mut args) => {
7115 if self.thread_last_mode {
7116 args.push(lhs);
7117 } else {
7118 args.insert(0, lhs);
7119 }
7120 ExprKind::CharDevices(args)
7121 }
7122 ExprKind::GlobPar { mut args, progress } => {
7123 if self.thread_last_mode {
7124 args.push(lhs);
7125 } else {
7126 args.insert(0, lhs);
7127 }
7128 ExprKind::GlobPar { args, progress }
7129 }
7130 ExprKind::ParSed { mut args, progress } => {
7131 if self.thread_last_mode {
7132 args.push(lhs);
7133 } else {
7134 args.insert(0, lhs);
7135 }
7136 ExprKind::ParSed { args, progress }
7137 }
7138
7139 ExprKind::Length(_) => ExprKind::Length(Box::new(lhs)),
7141 ExprKind::Abs(_) => ExprKind::Abs(Box::new(lhs)),
7142 ExprKind::Int(_) => ExprKind::Int(Box::new(lhs)),
7143 ExprKind::Sqrt(_) => ExprKind::Sqrt(Box::new(lhs)),
7144 ExprKind::Sin(_) => ExprKind::Sin(Box::new(lhs)),
7145 ExprKind::Cos(_) => ExprKind::Cos(Box::new(lhs)),
7146 ExprKind::Exp(_) => ExprKind::Exp(Box::new(lhs)),
7147 ExprKind::Log(_) => ExprKind::Log(Box::new(lhs)),
7148 ExprKind::Hex(_) => ExprKind::Hex(Box::new(lhs)),
7149 ExprKind::Oct(_) => ExprKind::Oct(Box::new(lhs)),
7150 ExprKind::Lc(_) => ExprKind::Lc(Box::new(lhs)),
7151 ExprKind::Uc(_) => ExprKind::Uc(Box::new(lhs)),
7152 ExprKind::Lcfirst(_) => ExprKind::Lcfirst(Box::new(lhs)),
7153 ExprKind::Ucfirst(_) => ExprKind::Ucfirst(Box::new(lhs)),
7154 ExprKind::Fc(_) => ExprKind::Fc(Box::new(lhs)),
7155 ExprKind::Chr(_) => ExprKind::Chr(Box::new(lhs)),
7156 ExprKind::Ord(_) => ExprKind::Ord(Box::new(lhs)),
7157 ExprKind::Chomp(_) => ExprKind::Chomp(Box::new(lhs)),
7158 ExprKind::Chop(_) => ExprKind::Chop(Box::new(lhs)),
7159 ExprKind::Defined(_) => ExprKind::Defined(Box::new(lhs)),
7160 ExprKind::Ref(_) => ExprKind::Ref(Box::new(lhs)),
7161 ExprKind::ScalarContext(_) => ExprKind::ScalarContext(Box::new(lhs)),
7162 ExprKind::Keys(_) => ExprKind::Keys(Box::new(lhs)),
7163 ExprKind::Values(_) => ExprKind::Values(Box::new(lhs)),
7164 ExprKind::Each(_) => ExprKind::Each(Box::new(lhs)),
7165 ExprKind::Pop(_) => ExprKind::Pop(Box::new(lhs)),
7166 ExprKind::Shift(_) => ExprKind::Shift(Box::new(lhs)),
7167 ExprKind::Delete(_) => ExprKind::Delete(Box::new(lhs)),
7168 ExprKind::Exists(_) => ExprKind::Exists(Box::new(lhs)),
7169 ExprKind::ReverseExpr(_) => ExprKind::ReverseExpr(Box::new(lhs)),
7170 ExprKind::Rev(_) => ExprKind::Rev(Box::new(lhs)),
7171 ExprKind::Slurp(_) => ExprKind::Slurp(Box::new(lhs)),
7172 ExprKind::Swallow(_) => ExprKind::Swallow(Box::new(lhs)),
7173 ExprKind::Ingest(_) => ExprKind::Ingest(Box::new(lhs)),
7174 ExprKind::Burp(_) => ExprKind::Burp(Box::new(lhs)),
7175 ExprKind::God(_) => ExprKind::God(Box::new(lhs)),
7176 ExprKind::Capture(_) => ExprKind::Capture(Box::new(lhs)),
7177 ExprKind::Qx(_) => ExprKind::Qx(Box::new(lhs)),
7178 ExprKind::FetchUrl(_) => ExprKind::FetchUrl(Box::new(lhs)),
7179 ExprKind::Close(_) => ExprKind::Close(Box::new(lhs)),
7180 ExprKind::Chdir(_) => ExprKind::Chdir(Box::new(lhs)),
7181 ExprKind::Readdir(_) => ExprKind::Readdir(Box::new(lhs)),
7182 ExprKind::Closedir(_) => ExprKind::Closedir(Box::new(lhs)),
7183 ExprKind::Rewinddir(_) => ExprKind::Rewinddir(Box::new(lhs)),
7184 ExprKind::Telldir(_) => ExprKind::Telldir(Box::new(lhs)),
7185 ExprKind::Stat(_) => ExprKind::Stat(Box::new(lhs)),
7186 ExprKind::Lstat(_) => ExprKind::Lstat(Box::new(lhs)),
7187 ExprKind::Readlink(_) => ExprKind::Readlink(Box::new(lhs)),
7188 ExprKind::Study(_) => ExprKind::Study(Box::new(lhs)),
7189 ExprKind::Await(_) => ExprKind::Await(Box::new(lhs)),
7190 ExprKind::Eval(_) => ExprKind::Eval(Box::new(lhs)),
7191 ExprKind::Rand(_) => ExprKind::Rand(Some(Box::new(lhs))),
7192 ExprKind::Srand(_) => ExprKind::Srand(Some(Box::new(lhs))),
7193 ExprKind::Pos(_) => ExprKind::Pos(Some(Box::new(lhs))),
7194 ExprKind::Exit(_) => ExprKind::Exit(Some(Box::new(lhs))),
7195
7196 ExprKind::MapExpr {
7198 block,
7199 list: _,
7200 flatten_array_refs,
7201 stream,
7202 } => ExprKind::MapExpr {
7203 block,
7204 list: Box::new(lhs),
7205 flatten_array_refs,
7206 stream,
7207 },
7208 ExprKind::MapExprComma {
7209 expr,
7210 list: _,
7211 flatten_array_refs,
7212 stream,
7213 } => ExprKind::MapExprComma {
7214 expr,
7215 list: Box::new(lhs),
7216 flatten_array_refs,
7217 stream,
7218 },
7219 ExprKind::GrepExpr {
7220 block,
7221 list: _,
7222 keyword,
7223 } => ExprKind::GrepExpr {
7224 block,
7225 list: Box::new(lhs),
7226 keyword,
7227 },
7228 ExprKind::GrepExprComma {
7229 expr,
7230 list: _,
7231 keyword,
7232 } => ExprKind::GrepExprComma {
7233 expr,
7234 list: Box::new(lhs),
7235 keyword,
7236 },
7237 ExprKind::ForEachExpr { block, list: _ } => ExprKind::ForEachExpr {
7238 block,
7239 list: Box::new(lhs),
7240 },
7241 ExprKind::SortExpr { cmp, list: _ } => ExprKind::SortExpr {
7242 cmp,
7243 list: Box::new(lhs),
7244 },
7245 ExprKind::JoinExpr { separator, list: _ } => ExprKind::JoinExpr {
7246 separator,
7247 list: Box::new(lhs),
7248 },
7249 ExprKind::ReduceExpr { block, list: _ } => ExprKind::ReduceExpr {
7250 block,
7251 list: Box::new(lhs),
7252 },
7253 ExprKind::PMapExpr {
7254 block,
7255 list: _,
7256 progress,
7257 flat_outputs,
7258 on_cluster,
7259 stream,
7260 } => ExprKind::PMapExpr {
7261 block,
7262 list: Box::new(lhs),
7263 progress,
7264 flat_outputs,
7265 on_cluster,
7266 stream,
7267 },
7268 ExprKind::ParExpr { block, list: _ } => ExprKind::ParExpr {
7269 block,
7270 list: Box::new(lhs),
7271 },
7272 ExprKind::ParReduceExpr {
7273 extract_block,
7274 reduce_block,
7275 list: _,
7276 } => ExprKind::ParReduceExpr {
7277 extract_block,
7278 reduce_block,
7279 list: Box::new(lhs),
7280 },
7281 ExprKind::PMapChunkedExpr {
7282 chunk_size,
7283 block,
7284 list: _,
7285 progress,
7286 } => ExprKind::PMapChunkedExpr {
7287 chunk_size,
7288 block,
7289 list: Box::new(lhs),
7290 progress,
7291 },
7292 ExprKind::PGrepExpr {
7293 block,
7294 list: _,
7295 progress,
7296 stream,
7297 } => ExprKind::PGrepExpr {
7298 block,
7299 list: Box::new(lhs),
7300 progress,
7301 stream,
7302 },
7303 ExprKind::PForExpr {
7304 block,
7305 list: _,
7306 progress,
7307 } => ExprKind::PForExpr {
7308 block,
7309 list: Box::new(lhs),
7310 progress,
7311 },
7312 ExprKind::PSortExpr {
7313 cmp,
7314 list: _,
7315 progress,
7316 } => ExprKind::PSortExpr {
7317 cmp,
7318 list: Box::new(lhs),
7319 progress,
7320 },
7321 ExprKind::PReduceExpr {
7322 block,
7323 list: _,
7324 progress,
7325 } => ExprKind::PReduceExpr {
7326 block,
7327 list: Box::new(lhs),
7328 progress,
7329 },
7330 ExprKind::PcacheExpr {
7331 block,
7332 list: _,
7333 progress,
7334 } => ExprKind::PcacheExpr {
7335 block,
7336 list: Box::new(lhs),
7337 progress,
7338 },
7339 ExprKind::PReduceInitExpr {
7340 init,
7341 block,
7342 list: _,
7343 progress,
7344 } => ExprKind::PReduceInitExpr {
7345 init,
7346 block,
7347 list: Box::new(lhs),
7348 progress,
7349 },
7350 ExprKind::PMapReduceExpr {
7351 map_block,
7352 reduce_block,
7353 list: _,
7354 progress,
7355 } => ExprKind::PMapReduceExpr {
7356 map_block,
7357 reduce_block,
7358 list: Box::new(lhs),
7359 progress,
7360 },
7361
7362 ExprKind::Push { array, mut values } => {
7367 values.insert(0, lhs);
7368 ExprKind::Push { array, values }
7369 }
7370 ExprKind::Unshift { array, mut values } => {
7371 values.insert(0, lhs);
7372 ExprKind::Unshift { array, values }
7373 }
7374
7375 ExprKind::SplitExpr {
7377 pattern,
7378 string: _,
7379 limit,
7380 } => ExprKind::SplitExpr {
7381 pattern,
7382 string: Box::new(lhs),
7383 limit,
7384 },
7385
7386 ExprKind::Substitution {
7390 pattern,
7391 replacement,
7392 mut flags,
7393 expr: _,
7394 delim,
7395 } => {
7396 if !flags.contains('r') {
7397 flags.push('r');
7398 }
7399 ExprKind::Substitution {
7400 expr: Box::new(lhs),
7401 pattern,
7402 replacement,
7403 flags,
7404 delim,
7405 }
7406 }
7407 ExprKind::Transliterate {
7408 from,
7409 to,
7410 mut flags,
7411 expr: _,
7412 delim,
7413 } => {
7414 if !flags.contains('r') {
7415 flags.push('r');
7416 }
7417 ExprKind::Transliterate {
7418 expr: Box::new(lhs),
7419 from,
7420 to,
7421 flags,
7422 delim,
7423 }
7424 }
7425 ExprKind::Match {
7426 pattern,
7427 flags,
7428 scalar_g,
7429 expr: _,
7430 delim,
7431 } => ExprKind::Match {
7432 expr: Box::new(lhs),
7433 pattern,
7434 flags,
7435 scalar_g,
7436 delim,
7437 },
7438 ExprKind::Regex(pattern, flags) => ExprKind::Match {
7440 expr: Box::new(lhs),
7441 pattern,
7442 flags,
7443 scalar_g: false,
7444 delim: '/',
7445 },
7446
7447 ExprKind::Bareword(name) => match name.as_str() {
7449 "reverse" => {
7450 if crate::no_interop_mode() {
7451 return Err(self.syntax_err(
7452 "stryke uses `rev` instead of `reverse` (--no-interop)",
7453 line,
7454 ));
7455 }
7456 ExprKind::ReverseExpr(Box::new(lhs))
7457 }
7458 "rv" | "reversed" | "rev" => ExprKind::Rev(Box::new(lhs)),
7459 "uq" | "uniq" | "distinct" => ExprKind::FuncCall {
7460 name: "uniq".to_string(),
7461 args: vec![lhs],
7462 },
7463 "fl" | "flatten" => ExprKind::FuncCall {
7464 name: "flatten".to_string(),
7465 args: vec![lhs],
7466 },
7467 _ => ExprKind::FuncCall {
7468 name,
7469 args: vec![lhs],
7470 },
7471 },
7472
7473 kind @ (ExprKind::ScalarVar(_)
7475 | ExprKind::ArrayElement { .. }
7476 | ExprKind::HashElement { .. }
7477 | ExprKind::Deref { .. }
7478 | ExprKind::ArrowDeref { .. }
7479 | ExprKind::CodeRef { .. }
7480 | ExprKind::SubroutineRef(_)
7481 | ExprKind::SubroutineCodeRef(_)
7482 | ExprKind::DynamicSubCodeRef(_)) => ExprKind::IndirectCall {
7483 target: Box::new(Expr { kind, line: rline }),
7484 args: vec![lhs],
7485 ampersand: false,
7486 pass_caller_arglist: false,
7487 },
7488
7489 ExprKind::Do(inner) if matches!(inner.kind, ExprKind::CodeRef { .. }) => {
7493 ExprKind::IndirectCall {
7494 target: inner,
7495 args: vec![lhs],
7496 ampersand: false,
7497 pass_caller_arglist: false,
7498 }
7499 }
7500
7501 other => {
7502 return Err(self.syntax_err(
7503 format!(
7504 "right-hand side of `|>` must be a call, builtin, or coderef \
7505 expression (got {})",
7506 Self::expr_kind_name(&other)
7507 ),
7508 line,
7509 ));
7510 }
7511 };
7512 Ok(Expr {
7513 kind: new_kind,
7514 line,
7515 })
7516 }
7517
7518 fn expr_kind_name(kind: &ExprKind) -> &'static str {
7520 match kind {
7521 ExprKind::Integer(_) | ExprKind::Float(_) => "numeric literal",
7522 ExprKind::String(_) | ExprKind::InterpolatedString(_) => "string literal",
7523 ExprKind::BinOp { .. } => "binary expression",
7524 ExprKind::UnaryOp { .. } => "unary expression",
7525 ExprKind::Ternary { .. } => "ternary expression",
7526 ExprKind::Assign { .. } | ExprKind::CompoundAssign { .. } => "assignment",
7527 ExprKind::List(_) => "list expression",
7528 ExprKind::Range { .. } => "range expression",
7529 _ => "expression",
7530 }
7531 }
7532
7533 fn parse_or_word(&mut self) -> StrykeResult<Expr> {
7535 let mut left = self.parse_and_word()?;
7536 while matches!(self.peek(), Token::LogOrWord) {
7537 let line = left.line;
7538 self.advance();
7539 let right = self.parse_and_word()?;
7540 left = Expr {
7541 kind: ExprKind::BinOp {
7542 left: Box::new(left),
7543 op: BinOp::LogOrWord,
7544 right: Box::new(right),
7545 },
7546 line,
7547 };
7548 }
7549 Ok(left)
7550 }
7551
7552 fn parse_and_word(&mut self) -> StrykeResult<Expr> {
7553 let mut left = self.parse_not_word()?;
7554 while matches!(self.peek(), Token::LogAndWord) {
7555 let line = left.line;
7556 self.advance();
7557 let right = self.parse_not_word()?;
7558 left = Expr {
7559 kind: ExprKind::BinOp {
7560 left: Box::new(left),
7561 op: BinOp::LogAndWord,
7562 right: Box::new(right),
7563 },
7564 line,
7565 };
7566 }
7567 Ok(left)
7568 }
7569
7570 fn parse_not_word(&mut self) -> StrykeResult<Expr> {
7571 if matches!(self.peek(), Token::LogNotWord) {
7572 let line = self.peek_line();
7573 self.advance();
7574 let expr = self.parse_not_word()?;
7575 return Ok(Expr {
7576 kind: ExprKind::UnaryOp {
7577 op: UnaryOp::LogNotWord,
7578 expr: Box::new(expr),
7579 },
7580 line,
7581 });
7582 }
7583 self.parse_assign_expr()
7586 }
7587
7588 fn parse_log_or(&mut self) -> StrykeResult<Expr> {
7589 let mut left = self.parse_log_and()?;
7590 loop {
7591 let op = match self.peek() {
7592 Token::LogOr => BinOp::LogOr,
7593 Token::DefinedOr => BinOp::DefinedOr,
7594 _ => break,
7595 };
7596 let line = left.line;
7597 self.advance();
7598 let right = self.parse_log_and()?;
7599 left = Expr {
7600 kind: ExprKind::BinOp {
7601 left: Box::new(left),
7602 op,
7603 right: Box::new(right),
7604 },
7605 line,
7606 };
7607 }
7608 Ok(left)
7609 }
7610
7611 fn parse_log_and(&mut self) -> StrykeResult<Expr> {
7612 let mut left = self.parse_bit_or()?;
7613 while matches!(self.peek(), Token::LogAnd) {
7614 let line = left.line;
7615 self.advance();
7616 let right = self.parse_bit_or()?;
7617 left = Expr {
7618 kind: ExprKind::BinOp {
7619 left: Box::new(left),
7620 op: BinOp::LogAnd,
7621 right: Box::new(right),
7622 },
7623 line,
7624 };
7625 }
7626 Ok(left)
7627 }
7628
7629 fn parse_bit_or(&mut self) -> StrykeResult<Expr> {
7630 let mut left = self.parse_bit_xor()?;
7631 while matches!(self.peek(), Token::BitOr) {
7632 let line = left.line;
7633 self.advance();
7634 let right = self.parse_bit_xor()?;
7635 left = Expr {
7636 kind: ExprKind::BinOp {
7637 left: Box::new(left),
7638 op: BinOp::BitOr,
7639 right: Box::new(right),
7640 },
7641 line,
7642 };
7643 }
7644 Ok(left)
7645 }
7646
7647 fn parse_bit_xor(&mut self) -> StrykeResult<Expr> {
7648 let mut left = self.parse_bit_and()?;
7649 while matches!(self.peek(), Token::BitXor) {
7650 let line = left.line;
7651 self.advance();
7652 let right = self.parse_bit_and()?;
7653 left = Expr {
7654 kind: ExprKind::BinOp {
7655 left: Box::new(left),
7656 op: BinOp::BitXor,
7657 right: Box::new(right),
7658 },
7659 line,
7660 };
7661 }
7662 Ok(left)
7663 }
7664
7665 fn parse_bit_and(&mut self) -> StrykeResult<Expr> {
7666 let mut left = self.parse_equality()?;
7667 while matches!(self.peek(), Token::BitAnd) {
7668 let line = left.line;
7669 self.advance();
7670 let right = self.parse_equality()?;
7671 left = Expr {
7672 kind: ExprKind::BinOp {
7673 left: Box::new(left),
7674 op: BinOp::BitAnd,
7675 right: Box::new(right),
7676 },
7677 line,
7678 };
7679 }
7680 Ok(left)
7681 }
7682
7683 fn parse_equality(&mut self) -> StrykeResult<Expr> {
7684 let mut left = self.parse_comparison()?;
7685 loop {
7686 let op = match self.peek() {
7687 Token::NumEq => BinOp::NumEq,
7688 Token::NumNe => BinOp::NumNe,
7689 Token::StrEq => BinOp::StrEq,
7690 Token::StrNe => BinOp::StrNe,
7691 Token::Spaceship => BinOp::Spaceship,
7692 Token::StrCmp => BinOp::StrCmp,
7693 _ => break,
7694 };
7695 let line = left.line;
7696 self.advance();
7697 let right = self.parse_comparison()?;
7698 left = Expr {
7699 kind: ExprKind::BinOp {
7700 left: Box::new(left),
7701 op,
7702 right: Box::new(right),
7703 },
7704 line,
7705 };
7706 }
7707 Ok(left)
7708 }
7709
7710 fn parse_comparison(&mut self) -> StrykeResult<Expr> {
7711 let left = self.parse_shift()?;
7712 let first_op = match self.peek() {
7713 Token::NumLt => BinOp::NumLt,
7714 Token::NumGt => BinOp::NumGt,
7715 Token::NumLe => BinOp::NumLe,
7716 Token::NumGe => BinOp::NumGe,
7717 Token::StrLt => BinOp::StrLt,
7718 Token::StrGt => BinOp::StrGt,
7719 Token::StrLe => BinOp::StrLe,
7720 Token::StrGe => BinOp::StrGe,
7721 _ => return Ok(left),
7722 };
7723 let line = left.line;
7724 self.advance();
7725 let middle = self.parse_shift()?;
7726
7727 let second_op = match self.peek() {
7728 Token::NumLt => Some(BinOp::NumLt),
7729 Token::NumGt => Some(BinOp::NumGt),
7730 Token::NumLe => Some(BinOp::NumLe),
7731 Token::NumGe => Some(BinOp::NumGe),
7732 Token::StrLt => Some(BinOp::StrLt),
7733 Token::StrGt => Some(BinOp::StrGt),
7734 Token::StrLe => Some(BinOp::StrLe),
7735 Token::StrGe => Some(BinOp::StrGe),
7736 _ => None,
7737 };
7738
7739 if second_op.is_none() {
7740 return Ok(Expr {
7741 kind: ExprKind::BinOp {
7742 left: Box::new(left),
7743 op: first_op,
7744 right: Box::new(middle),
7745 },
7746 line,
7747 });
7748 }
7749
7750 let mut operands = vec![left, middle];
7753 let mut ops = vec![first_op];
7754
7755 loop {
7756 let op = match self.peek() {
7757 Token::NumLt => BinOp::NumLt,
7758 Token::NumGt => BinOp::NumGt,
7759 Token::NumLe => BinOp::NumLe,
7760 Token::NumGe => BinOp::NumGe,
7761 Token::StrLt => BinOp::StrLt,
7762 Token::StrGt => BinOp::StrGt,
7763 Token::StrLe => BinOp::StrLe,
7764 Token::StrGe => BinOp::StrGe,
7765 _ => break,
7766 };
7767 self.advance();
7768 ops.push(op);
7769 operands.push(self.parse_shift()?);
7770 }
7771
7772 let mut result = Expr {
7774 kind: ExprKind::BinOp {
7775 left: Box::new(operands[0].clone()),
7776 op: ops[0],
7777 right: Box::new(operands[1].clone()),
7778 },
7779 line,
7780 };
7781
7782 for i in 1..ops.len() {
7783 let cmp = Expr {
7784 kind: ExprKind::BinOp {
7785 left: Box::new(operands[i].clone()),
7786 op: ops[i],
7787 right: Box::new(operands[i + 1].clone()),
7788 },
7789 line,
7790 };
7791 result = Expr {
7792 kind: ExprKind::BinOp {
7793 left: Box::new(result),
7794 op: BinOp::LogAnd,
7795 right: Box::new(cmp),
7796 },
7797 line,
7798 };
7799 }
7800
7801 Ok(result)
7802 }
7803
7804 fn parse_shift(&mut self) -> StrykeResult<Expr> {
7805 let mut left = self.parse_addition()?;
7806 loop {
7807 let op = match self.peek() {
7808 Token::ShiftLeft => BinOp::ShiftLeft,
7809 Token::ShiftRight => BinOp::ShiftRight,
7810 _ => break,
7811 };
7812 let line = left.line;
7813 self.advance();
7814 let right = self.parse_addition()?;
7815 left = Expr {
7816 kind: ExprKind::BinOp {
7817 left: Box::new(left),
7818 op,
7819 right: Box::new(right),
7820 },
7821 line,
7822 };
7823 }
7824 Ok(left)
7825 }
7826
7827 fn parse_addition(&mut self) -> StrykeResult<Expr> {
7828 let mut left = self.parse_multiplication()?;
7829 loop {
7830 let op = match self.peek() {
7833 Token::Plus if self.peek_line() == self.prev_line() => BinOp::Add,
7834 Token::Minus if self.peek_line() == self.prev_line() => BinOp::Sub,
7835 Token::Dot => BinOp::Concat,
7836 _ => break,
7837 };
7838 let line = left.line;
7839 self.advance();
7840 let right = self.parse_multiplication()?;
7841 left = Expr {
7842 kind: ExprKind::BinOp {
7843 left: Box::new(left),
7844 op,
7845 right: Box::new(right),
7846 },
7847 line,
7848 };
7849 }
7850 Ok(left)
7851 }
7852
7853 fn parse_multiplication(&mut self) -> StrykeResult<Expr> {
7854 let mut left = self.parse_regex_bind()?;
7855 loop {
7856 let op = match self.peek() {
7857 Token::Star => BinOp::Mul,
7858 Token::Slash if self.suppress_slash_as_div == 0 => BinOp::Div,
7859 Token::Percent if self.peek_line() == self.prev_line() => BinOp::Mod,
7862 Token::X => {
7863 let line = left.line;
7864 let list_repeat = self.list_construct_close_pos == Some(self.pos);
7871 self.advance();
7872 let right = self.parse_regex_bind()?;
7873 left = Expr {
7874 kind: ExprKind::Repeat {
7875 expr: Box::new(left),
7876 count: Box::new(right),
7877 list_repeat,
7878 },
7879 line,
7880 };
7881 continue;
7882 }
7883 _ => break,
7884 };
7885 let line = left.line;
7886 self.advance();
7887 let right = self.parse_regex_bind()?;
7888 left = Expr {
7889 kind: ExprKind::BinOp {
7890 left: Box::new(left),
7891 op,
7892 right: Box::new(right),
7893 },
7894 line,
7895 };
7896 }
7897 Ok(left)
7898 }
7899
7900 fn parse_regex_bind(&mut self) -> StrykeResult<Expr> {
7901 let left = self.parse_unary()?;
7902 match self.peek() {
7903 Token::BindMatch => {
7904 let line = left.line;
7905 self.advance();
7906 match self.peek().clone() {
7907 Token::Regex(pattern, flags, delim) => {
7908 self.advance();
7909 Ok(Expr {
7910 kind: ExprKind::Match {
7911 expr: Box::new(left),
7912 pattern,
7913 flags,
7914 scalar_g: false,
7915 delim,
7916 },
7917 line,
7918 })
7919 }
7920 Token::Ident(ref s) if s.starts_with('\x00') => {
7921 let (Token::Ident(encoded), _) = self.advance() else {
7922 unreachable!()
7923 };
7924 let parts: Vec<&str> = encoded.split('\x00').collect();
7925 if parts.len() >= 4 && parts[1] == "s" {
7926 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7927 Ok(Expr {
7928 kind: ExprKind::Substitution {
7929 expr: Box::new(left),
7930 pattern: parts[2].to_string(),
7931 replacement: parts[3].to_string(),
7932 flags: parts.get(4).unwrap_or(&"").to_string(),
7933 delim,
7934 },
7935 line,
7936 })
7937 } else if parts.len() >= 4 && parts[1] == "tr" {
7938 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7939 Ok(Expr {
7940 kind: ExprKind::Transliterate {
7941 expr: Box::new(left),
7942 from: parts[2].to_string(),
7943 to: parts[3].to_string(),
7944 flags: parts.get(4).unwrap_or(&"").to_string(),
7945 delim,
7946 },
7947 line,
7948 })
7949 } else {
7950 Err(self.syntax_err("Invalid regex binding", line))
7951 }
7952 }
7953 _ => {
7954 let rhs = self.parse_unary()?;
7955 Ok(Expr {
7956 kind: ExprKind::BinOp {
7957 left: Box::new(left),
7958 op: BinOp::BindMatch,
7959 right: Box::new(rhs),
7960 },
7961 line,
7962 })
7963 }
7964 }
7965 }
7966 Token::BindNotMatch => {
7967 let line = left.line;
7968 self.advance();
7969 match self.peek().clone() {
7970 Token::Regex(pattern, flags, delim) => {
7971 self.advance();
7972 Ok(Expr {
7973 kind: ExprKind::UnaryOp {
7974 op: UnaryOp::LogNot,
7975 expr: Box::new(Expr {
7976 kind: ExprKind::Match {
7977 expr: Box::new(left),
7978 pattern,
7979 flags,
7980 scalar_g: false,
7981 delim,
7982 },
7983 line,
7984 }),
7985 },
7986 line,
7987 })
7988 }
7989 Token::Ident(ref s) if s.starts_with('\x00') => {
7990 let (Token::Ident(encoded), _) = self.advance() else {
7991 unreachable!()
7992 };
7993 let parts: Vec<&str> = encoded.split('\x00').collect();
7994 if parts.len() >= 4 && parts[1] == "s" {
7995 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
7996 Ok(Expr {
7997 kind: ExprKind::UnaryOp {
7998 op: UnaryOp::LogNot,
7999 expr: Box::new(Expr {
8000 kind: ExprKind::Substitution {
8001 expr: Box::new(left),
8002 pattern: parts[2].to_string(),
8003 replacement: parts[3].to_string(),
8004 flags: parts.get(4).unwrap_or(&"").to_string(),
8005 delim,
8006 },
8007 line,
8008 }),
8009 },
8010 line,
8011 })
8012 } else if parts.len() >= 4 && parts[1] == "tr" {
8013 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
8014 Ok(Expr {
8015 kind: ExprKind::UnaryOp {
8016 op: UnaryOp::LogNot,
8017 expr: Box::new(Expr {
8018 kind: ExprKind::Transliterate {
8019 expr: Box::new(left),
8020 from: parts[2].to_string(),
8021 to: parts[3].to_string(),
8022 flags: parts.get(4).unwrap_or(&"").to_string(),
8023 delim,
8024 },
8025 line,
8026 }),
8027 },
8028 line,
8029 })
8030 } else {
8031 Err(self.syntax_err("Invalid regex binding after !~", line))
8032 }
8033 }
8034 _ => {
8035 let rhs = self.parse_unary()?;
8036 Ok(Expr {
8037 kind: ExprKind::BinOp {
8038 left: Box::new(left),
8039 op: BinOp::BindNotMatch,
8040 right: Box::new(rhs),
8041 },
8042 line,
8043 })
8044 }
8045 }
8046 }
8047 _ => Ok(left),
8048 }
8049 }
8050
8051 fn parse_thread_input(&mut self) -> StrykeResult<Expr> {
8054 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_add(1);
8055 let result = self.parse_range();
8056 self.suppress_slash_as_div = self.suppress_slash_as_div.saturating_sub(1);
8057 result
8058 }
8059
8060 fn parse_thread_macro_chunk_par(
8065 &mut self,
8066 line: usize,
8067 thread_last: bool,
8068 ) -> StrykeResult<Expr> {
8069 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8071 let source_expr = self.parse_thread_input();
8072 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8073 let source_expr = source_expr?;
8074
8075 self.pending_thread_input = Some(Expr {
8079 kind: ExprKind::ArrayVar("_".into()),
8080 line,
8081 });
8082 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8083 self.pending_thread_input = None;
8084 let chunk_chain = chunk_chain?;
8085
8086 let extract_block: Block = match chunk_chain.kind {
8091 ExprKind::CodeRef { params: _, body } => body,
8092 _ => vec![Statement {
8093 label: None,
8094 kind: StmtKind::Expression(chunk_chain),
8095 line,
8096 }],
8097 };
8098
8099 let par_reduce = Expr {
8100 kind: ExprKind::ParReduceExpr {
8101 extract_block,
8102 reduce_block: None,
8103 list: Box::new(source_expr),
8104 },
8105 line,
8106 };
8107
8108 if self.eat_chunk_par_split_boundary() {
8112 return self.parse_thread_macro_continuation(par_reduce, line, thread_last);
8113 }
8114 Ok(par_reduce)
8115 }
8116
8117 fn parse_thread_macro_dist(&mut self, line: usize, thread_last: bool) -> StrykeResult<Expr> {
8124 let on_ok = matches!(self.peek(), Token::Ident(ref s) if s == "on");
8126 if !on_ok {
8127 return Err(
8128 self.syntax_err("~d>: expected `on <cluster-expr>` after the operator", line)
8129 );
8130 }
8131 self.advance(); self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8135 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_add(1);
8139 let cluster_expr = self.parse_thread_input();
8140 self.suppress_indirect_paren_call = self.suppress_indirect_paren_call.saturating_sub(1);
8141 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8142 let cluster_expr = cluster_expr?;
8143
8144 self.suppress_parenless_call = self.suppress_parenless_call.saturating_add(1);
8146 let source_expr = self.parse_thread_input();
8147 self.suppress_parenless_call = self.suppress_parenless_call.saturating_sub(1);
8148 let source_expr = source_expr?;
8149
8150 self.pending_thread_input = Some(Expr {
8155 kind: ExprKind::ArrayVar("_".into()),
8156 line,
8157 });
8158 let chunk_chain = self.parse_thread_macro_inner(line, thread_last, None);
8159 self.pending_thread_input = None;
8160 let chunk_chain = chunk_chain?;
8161
8162 let extract_block: Block = match chunk_chain.kind {
8163 ExprKind::CodeRef { params: _, body } => body,
8164 _ => vec![Statement {
8165 label: None,
8166 kind: StmtKind::Expression(chunk_chain),
8167 line,
8168 }],
8169 };
8170
8171 let dist_reduce = Expr {
8172 kind: ExprKind::DistReduceExpr {
8173 cluster: Box::new(cluster_expr),
8174 extract_block,
8175 list: Box::new(source_expr),
8176 },
8177 line,
8178 };
8179
8180 if self.eat_chunk_par_split_boundary() {
8182 return self.parse_thread_macro_continuation(dist_reduce, line, thread_last);
8183 }
8184 Ok(dist_reduce)
8185 }
8186
8187 fn parse_thread_macro_continuation(
8193 &mut self,
8194 prior: Expr,
8195 line: usize,
8196 thread_last: bool,
8197 ) -> StrykeResult<Expr> {
8198 self.pending_thread_input = Some(prior);
8199 let res = self.parse_thread_macro_inner(line, thread_last, None);
8200 self.pending_thread_input = None;
8201 res
8202 }
8203
8204 fn eat_chunk_par_split_boundary(&mut self) -> bool {
8208 if matches!(self.peek(), Token::LogOr) && matches!(self.peek_at(1), Token::NumGt) {
8210 self.advance(); self.advance(); return true;
8213 }
8214 if matches!(self.peek(), Token::BitOr) {
8216 if let Token::Ident(name) = self.peek_at(1).clone() {
8217 if name == "then" && matches!(self.peek_at(2), Token::BitOr) {
8218 self.advance(); self.advance(); self.advance(); return true;
8222 }
8223 }
8224 }
8225 false
8226 }
8227
8228 fn parse_range(&mut self) -> StrykeResult<Expr> {
8235 let left = self.parse_log_or()?;
8236 let line = left.line;
8237 let (exclusive, _colon_style) = if self.eat(&Token::RangeExclusive) {
8244 (true, false)
8245 } else if self.eat(&Token::Range) {
8246 (false, false)
8247 } else if self.suppress_colon_range == 0 && self.eat(&Token::Colon) {
8248 (false, true)
8251 } else if self.suppress_tilde_range == 0 && self.eat(&Token::BitNot) {
8252 (false, true)
8253 } else {
8254 return Ok(left);
8255 };
8256 let right = self.parse_log_or()?;
8257 let step = if self.eat(&Token::Colon)
8261 || (self.suppress_tilde_range == 0 && self.eat(&Token::BitNot))
8262 {
8263 Some(Box::new(self.parse_unary()?))
8264 } else {
8265 None
8266 };
8267 Ok(Expr {
8268 kind: ExprKind::Range {
8269 from: Box::new(left),
8270 to: Box::new(right),
8271 exclusive,
8272 step,
8273 },
8274 line,
8275 })
8276 }
8277
8278 fn parse_package_qualified_identifier(&mut self) -> StrykeResult<String> {
8280 let mut name = match self.advance() {
8281 (Token::Ident(n), _) => n,
8282 (tok, l) => {
8283 return Err(self.syntax_err(format!("Expected identifier, got {:?}", tok), l));
8284 }
8285 };
8286 while self.eat(&Token::PackageSep) {
8287 match self.advance() {
8288 (Token::Ident(part), _) => {
8289 name.push_str("::");
8290 name.push_str(&part);
8291 }
8292 (Token::ScalarVar(part), _) if Self::is_underscore_topic_slot(&part) => {
8300 name.push_str("::");
8301 name.push_str(&part);
8302 }
8303 (tok, l) => {
8304 return Err(self
8305 .syntax_err(format!("Expected identifier after `::`, got {:?}", tok), l));
8306 }
8307 }
8308 }
8309 Ok(name)
8310 }
8311
8312 fn parse_qualified_subroutine_name(&mut self) -> StrykeResult<String> {
8314 self.parse_package_qualified_identifier()
8315 }
8316
8317 fn parse_unary(&mut self) -> StrykeResult<Expr> {
8318 let line = self.peek_line();
8319 match self.peek().clone() {
8320 Token::Minus => {
8321 self.advance();
8322 let expr = self.parse_power()?;
8323 Ok(Expr {
8324 kind: ExprKind::UnaryOp {
8325 op: UnaryOp::Negate,
8326 expr: Box::new(expr),
8327 },
8328 line,
8329 })
8330 }
8331 Token::Plus => {
8338 self.advance();
8339 if matches!(self.peek(), Token::LBrace) {
8340 let line = self.peek_line();
8341 self.advance(); return self.parse_forced_hashref_body(line);
8343 }
8344 self.parse_unary()
8345 }
8346 Token::LogNot => {
8347 self.advance();
8348 let expr = self.parse_unary()?;
8349 Ok(Expr {
8350 kind: ExprKind::UnaryOp {
8351 op: UnaryOp::LogNot,
8352 expr: Box::new(expr),
8353 },
8354 line,
8355 })
8356 }
8357 Token::BitNot => {
8358 self.advance();
8359 let expr = self.parse_unary()?;
8360 Ok(Expr {
8361 kind: ExprKind::UnaryOp {
8362 op: UnaryOp::BitNot,
8363 expr: Box::new(expr),
8364 },
8365 line,
8366 })
8367 }
8368 Token::Increment => {
8369 self.advance();
8370 let expr = self.parse_postfix()?;
8371 Ok(Expr {
8372 kind: ExprKind::UnaryOp {
8373 op: UnaryOp::PreIncrement,
8374 expr: Box::new(expr),
8375 },
8376 line,
8377 })
8378 }
8379 Token::Decrement => {
8380 self.advance();
8381 let expr = self.parse_postfix()?;
8382 Ok(Expr {
8383 kind: ExprKind::UnaryOp {
8384 op: UnaryOp::PreDecrement,
8385 expr: Box::new(expr),
8386 },
8387 line,
8388 })
8389 }
8390 Token::BitAnd => {
8391 self.advance();
8394 if matches!(self.peek(), Token::LBrace) {
8395 self.advance();
8396 let inner = self.parse_expression()?;
8397 self.expect(&Token::RBrace)?;
8398 return Ok(Expr {
8399 kind: ExprKind::DynamicSubCodeRef(Box::new(inner)),
8400 line,
8401 });
8402 }
8403 if matches!(self.peek(), Token::Ident(_)) {
8404 let name = self.parse_qualified_subroutine_name()?;
8405 return Ok(Expr {
8406 kind: ExprKind::SubroutineRef(name),
8407 line,
8408 });
8409 }
8410 let target = self.parse_primary()?;
8411 if matches!(self.peek(), Token::LParen) {
8412 self.advance();
8413 let args = self.parse_arg_list()?;
8414 self.expect(&Token::RParen)?;
8415 return Ok(Expr {
8416 kind: ExprKind::IndirectCall {
8417 target: Box::new(target),
8418 args,
8419 ampersand: true,
8420 pass_caller_arglist: false,
8421 },
8422 line,
8423 });
8424 }
8425 Ok(Expr {
8427 kind: ExprKind::IndirectCall {
8428 target: Box::new(target),
8429 args: vec![],
8430 ampersand: true,
8431 pass_caller_arglist: true,
8432 },
8433 line,
8434 })
8435 }
8436 Token::Backslash => {
8437 self.advance();
8438 let expr = self.parse_unary()?;
8439 if let ExprKind::SubroutineRef(name) = expr.kind {
8440 return Ok(Expr {
8441 kind: ExprKind::SubroutineCodeRef(name),
8442 line,
8443 });
8444 }
8445 if matches!(expr.kind, ExprKind::DynamicSubCodeRef(_)) {
8446 return Ok(expr);
8447 }
8448 Ok(Expr {
8450 kind: ExprKind::ScalarRef(Box::new(expr)),
8451 line,
8452 })
8453 }
8454 Token::FileTest(op) => {
8455 self.advance();
8456 let expr = if Self::filetest_allows_implicit_topic(self.peek()) {
8458 Expr {
8459 kind: ExprKind::ScalarVar("_".into()),
8460 line: self.peek_line(),
8461 }
8462 } else {
8463 self.parse_unary()?
8464 };
8465 Ok(Expr {
8466 kind: ExprKind::FileTest {
8467 op,
8468 expr: Box::new(expr),
8469 },
8470 line,
8471 })
8472 }
8473 _ => self.parse_power(),
8474 }
8475 }
8476
8477 fn parse_power(&mut self) -> StrykeResult<Expr> {
8478 let left = self.parse_postfix()?;
8479 if matches!(self.peek(), Token::Power) {
8480 let line = left.line;
8481 self.advance();
8482 let right = self.parse_unary()?; return Ok(Expr {
8484 kind: ExprKind::BinOp {
8485 left: Box::new(left),
8486 op: BinOp::Pow,
8487 right: Box::new(right),
8488 },
8489 line,
8490 });
8491 }
8492 Ok(left)
8493 }
8494
8495 fn parse_postfix(&mut self) -> StrykeResult<Expr> {
8496 let mut expr = self.parse_primary()?;
8497 loop {
8498 match self.peek().clone() {
8499 Token::Increment => {
8500 if self.peek_line() > self.prev_line() {
8503 break;
8504 }
8505 let line = expr.line;
8506 self.advance();
8507 expr = Expr {
8508 kind: ExprKind::PostfixOp {
8509 expr: Box::new(expr),
8510 op: PostfixOp::Increment,
8511 },
8512 line,
8513 };
8514 }
8515 Token::Decrement => {
8516 if self.peek_line() > self.prev_line() {
8519 break;
8520 }
8521 let line = expr.line;
8522 self.advance();
8523 expr = Expr {
8524 kind: ExprKind::PostfixOp {
8525 expr: Box::new(expr),
8526 op: PostfixOp::Decrement,
8527 },
8528 line,
8529 };
8530 }
8531 Token::LParen => {
8532 if self.suppress_indirect_paren_call > 0 {
8533 break;
8534 }
8535 if self.peek_line() > self.prev_line() {
8539 break;
8540 }
8541 let line = expr.line;
8542 self.advance();
8543 let args = self.parse_arg_list()?;
8544 self.expect(&Token::RParen)?;
8545 expr = Expr {
8546 kind: ExprKind::IndirectCall {
8547 target: Box::new(expr),
8548 args,
8549 ampersand: false,
8550 pass_caller_arglist: false,
8551 },
8552 line,
8553 };
8554 }
8555 Token::Arrow => {
8556 let line = expr.line;
8557 self.advance();
8558 match self.peek().clone() {
8559 Token::LBracket => {
8560 self.advance();
8561 let index = self.parse_expression()?;
8562 self.expect(&Token::RBracket)?;
8563 expr = Expr {
8564 kind: ExprKind::ArrowDeref {
8565 expr: Box::new(expr),
8566 index: Box::new(index),
8567 kind: DerefKind::Array,
8568 },
8569 line,
8570 };
8571 }
8572 Token::LBrace => {
8573 self.advance();
8574 let key = self.parse_hash_subscript_key()?;
8575 self.expect(&Token::RBrace)?;
8576 expr = Expr {
8577 kind: ExprKind::ArrowDeref {
8578 expr: Box::new(expr),
8579 index: Box::new(key),
8580 kind: DerefKind::Hash,
8581 },
8582 line,
8583 };
8584 }
8585 Token::LParen => {
8586 self.advance();
8587 let args = self.parse_arg_list()?;
8588 self.expect(&Token::RParen)?;
8589 expr = Expr {
8590 kind: ExprKind::ArrowDeref {
8591 expr: Box::new(expr),
8592 index: Box::new(Expr {
8593 kind: ExprKind::List(args),
8594 line,
8595 }),
8596 kind: DerefKind::Call,
8597 },
8598 line,
8599 };
8600 }
8601 Token::Ident(method) => {
8602 self.advance();
8603 if method == "SUPER" {
8604 self.expect(&Token::PackageSep)?;
8605 let real_method = match self.advance() {
8606 (Token::Ident(n), _) => n,
8607 (tok, l) => {
8608 return Err(self.syntax_err(
8609 format!(
8610 "Expected method name after SUPER::, got {:?}",
8611 tok
8612 ),
8613 l,
8614 ));
8615 }
8616 };
8617 let args = if self.eat(&Token::LParen) {
8618 let a = self.parse_arg_list()?;
8619 self.expect(&Token::RParen)?;
8620 a
8621 } else {
8622 self.parse_method_arg_list_no_paren()?
8623 };
8624 expr = Expr {
8625 kind: ExprKind::MethodCall {
8626 object: Box::new(expr),
8627 method: real_method,
8628 args,
8629 super_call: true,
8630 },
8631 line,
8632 };
8633 } else {
8634 let mut method_name = method;
8635 while self.eat(&Token::PackageSep) {
8636 match self.advance() {
8637 (Token::Ident(part), _) => {
8638 method_name.push_str("::");
8639 method_name.push_str(&part);
8640 }
8641 (tok, l) => {
8642 return Err(self.syntax_err(
8643 format!(
8644 "Expected identifier after :: in method name, got {:?}",
8645 tok
8646 ),
8647 l,
8648 ));
8649 }
8650 }
8651 }
8652 let args = if self.eat(&Token::LParen) {
8653 let a = self.parse_arg_list()?;
8654 self.expect(&Token::RParen)?;
8655 a
8656 } else {
8657 self.parse_method_arg_list_no_paren()?
8658 };
8659 expr = Expr {
8660 kind: ExprKind::MethodCall {
8661 object: Box::new(expr),
8662 method: method_name,
8663 args,
8664 super_call: false,
8665 },
8666 line,
8667 };
8668 }
8669 }
8670 Token::ArrayAt => {
8676 self.advance(); match self.peek().clone() {
8678 Token::Star => {
8679 self.advance();
8680 expr = Expr {
8681 kind: ExprKind::Deref {
8682 expr: Box::new(expr),
8683 kind: Sigil::Array,
8684 },
8685 line,
8686 };
8687 }
8688 Token::LBracket => {
8689 self.advance();
8690 let indices = self.parse_slice_arg_list(false)?;
8691 self.expect(&Token::RBracket)?;
8692 let source = Expr {
8693 kind: ExprKind::Deref {
8694 expr: Box::new(expr),
8695 kind: Sigil::Array,
8696 },
8697 line,
8698 };
8699 expr = Expr {
8700 kind: ExprKind::AnonymousListSlice {
8701 source: Box::new(source),
8702 indices,
8703 },
8704 line,
8705 };
8706 }
8707 Token::LBrace => {
8708 self.advance();
8709 let keys = self.parse_slice_arg_list(true)?;
8710 self.expect(&Token::RBrace)?;
8711 expr = Expr {
8712 kind: ExprKind::HashSliceDeref {
8713 container: Box::new(expr),
8714 keys,
8715 },
8716 line,
8717 };
8718 }
8719 tok => {
8720 return Err(self.syntax_err(
8721 format!(
8722 "Expected `*`, `[…]`, or `{{…}}` after `->@`, got {:?}",
8723 tok
8724 ),
8725 line,
8726 ));
8727 }
8728 }
8729 }
8730 Token::HashPercent => {
8731 self.advance(); match self.peek().clone() {
8733 Token::Star => {
8734 self.advance();
8735 expr = Expr {
8736 kind: ExprKind::Deref {
8737 expr: Box::new(expr),
8738 kind: Sigil::Hash,
8739 },
8740 line,
8741 };
8742 }
8743 tok => {
8744 return Err(self.syntax_err(
8745 format!("Expected `*` after `->%`, got {:?}", tok),
8746 line,
8747 ));
8748 }
8749 }
8750 }
8751 Token::X => {
8753 self.advance();
8754 let args = if self.eat(&Token::LParen) {
8755 let a = self.parse_arg_list()?;
8756 self.expect(&Token::RParen)?;
8757 a
8758 } else {
8759 self.parse_method_arg_list_no_paren()?
8760 };
8761 expr = Expr {
8762 kind: ExprKind::MethodCall {
8763 object: Box::new(expr),
8764 method: "x".to_string(),
8765 args,
8766 super_call: false,
8767 },
8768 line,
8769 };
8770 }
8771 _ => break,
8772 }
8773 }
8774 Token::LBracket => {
8775 if self.peek_line() > self.prev_line() {
8778 break;
8779 }
8780 let line = expr.line;
8782 if matches!(expr.kind, ExprKind::ScalarVar(_)) {
8783 if let ExprKind::ScalarVar(ref name) = expr.kind {
8784 let name = name.clone();
8785 self.advance();
8786 let index = self.parse_expression()?;
8789 self.expect(&Token::RBracket)?;
8790 expr = Expr {
8791 kind: ExprKind::ArrayElement {
8792 array: name,
8793 index: Box::new(index),
8794 },
8795 line,
8796 };
8797 }
8798 } else if postfix_lbracket_is_arrow_container(&expr) {
8799 self.advance();
8800 let indices = self.parse_arg_list()?;
8801 self.expect(&Token::RBracket)?;
8802 expr = Expr {
8803 kind: ExprKind::ArrowDeref {
8804 expr: Box::new(expr),
8805 index: Box::new(Expr {
8806 kind: ExprKind::List(indices),
8807 line,
8808 }),
8809 kind: DerefKind::Array,
8810 },
8811 line,
8812 };
8813 } else {
8814 self.advance();
8815 let indices = self.parse_arg_list()?;
8816 self.expect(&Token::RBracket)?;
8817 expr = Expr {
8818 kind: ExprKind::AnonymousListSlice {
8819 source: Box::new(expr),
8820 indices,
8821 },
8822 line,
8823 };
8824 }
8825 }
8826 Token::LBrace => {
8827 if self.suppress_scalar_hash_brace > 0 {
8828 break;
8829 }
8830 if self.peek_line() > self.prev_line() {
8833 break;
8834 }
8835 let line = expr.line;
8838 let is_scalar_named_hash = matches!(expr.kind, ExprKind::ScalarVar(_));
8839 let is_chainable_hash_subscript = is_scalar_named_hash
8840 || matches!(
8841 expr.kind,
8842 ExprKind::HashElement { .. }
8843 | ExprKind::ArrayElement { .. }
8844 | ExprKind::ArrowDeref { .. }
8845 | ExprKind::Deref {
8846 kind: Sigil::Scalar,
8847 ..
8848 }
8849 );
8850 if !is_chainable_hash_subscript {
8851 break;
8852 }
8853 self.advance();
8854 let key = self.parse_hash_subscript_key()?;
8855 self.expect(&Token::RBrace)?;
8856 expr = if is_scalar_named_hash {
8857 if let ExprKind::ScalarVar(ref name) = expr.kind {
8858 let name = name.clone();
8859 if name == "_" {
8861 Expr {
8862 kind: ExprKind::ArrowDeref {
8863 expr: Box::new(Expr {
8864 kind: ExprKind::ScalarVar("_".into()),
8865 line,
8866 }),
8867 index: Box::new(key),
8868 kind: DerefKind::Hash,
8869 },
8870 line,
8871 }
8872 } else {
8873 Expr {
8874 kind: ExprKind::HashElement {
8875 hash: name,
8876 key: Box::new(key),
8877 },
8878 line,
8879 }
8880 }
8881 } else {
8882 unreachable!("is_scalar_named_hash implies ScalarVar");
8883 }
8884 } else {
8885 Expr {
8886 kind: ExprKind::ArrowDeref {
8887 expr: Box::new(expr),
8888 index: Box::new(key),
8889 kind: DerefKind::Hash,
8890 },
8891 line,
8892 }
8893 };
8894 }
8895 Token::LogNot | Token::BitNot => {
8896 if !matches!(expr.kind, ExprKind::ScalarVar(_)) {
8911 break;
8912 }
8913 if self.peek_line() > self.prev_line() {
8914 break;
8915 }
8916 let opener = self.peek().clone();
8917 let line = expr.line;
8918 let name = if let ExprKind::ScalarVar(ref n) = expr.kind {
8919 n.clone()
8920 } else {
8921 unreachable!()
8922 };
8923 self.advance(); self.suppress_tilde_range = self.suppress_tilde_range.saturating_add(1);
8929 let index_result = self.parse_expression();
8930 self.suppress_tilde_range = self.suppress_tilde_range.saturating_sub(1);
8931 let index = index_result?;
8932 let close_match = matches!(
8933 (&opener, self.peek()),
8934 (Token::LogNot, Token::LogNot) | (Token::BitNot, Token::BitNot)
8935 );
8936 if !close_match {
8937 let want = if matches!(opener, Token::LogNot) {
8938 "!"
8939 } else {
8940 "~"
8941 };
8942 return Err(self.syntax_err(
8943 format!("expected closing `{}` for string subscript", want),
8944 self.peek_line(),
8945 ));
8946 }
8947 self.advance(); expr = Expr {
8949 kind: ExprKind::ArrayElement {
8950 array: format!("__topicstr__{}", name),
8951 index: Box::new(index),
8952 },
8953 line,
8954 };
8955 }
8956 _ => break,
8957 }
8958 }
8959 Ok(expr)
8960 }
8961
8962 fn parse_primary(&mut self) -> StrykeResult<Expr> {
8963 let line = self.peek_line();
8964 if let Token::Ident(ref kw) = self.peek().clone() {
8969 if matches!(
8970 kw.as_str(),
8971 "my" | "var" | "val" | "our" | "state" | "local"
8972 ) {
8973 let raw_kw = kw.clone();
8974 let kw_owned: String = match raw_kw.as_str() {
8980 "var" | "val" => "my".to_string(),
8981 _ => raw_kw.clone(),
8982 };
8983 let allow_type = matches!(raw_kw.as_str(), "val");
8984 let mark_frozen = matches!(raw_kw.as_str(), "val");
8985 let saved_pos = self.pos;
8990 let stmt = self.parse_my_our_local(&kw_owned, allow_type)?;
8991 let mut decls = match stmt.kind {
8992 StmtKind::My(d)
8993 | StmtKind::Our(d)
8994 | StmtKind::State(d)
8995 | StmtKind::Local(d) => d,
8996 _ => {
8997 self.pos = saved_pos;
9002 return Err(self.syntax_err(
9003 "`my`/`our`/`local` in expression must declare variables",
9004 line,
9005 ));
9006 }
9007 };
9008 if mark_frozen {
9009 for d in decls.iter_mut() {
9010 d.frozen = true;
9011 }
9012 }
9013 return Ok(Expr {
9014 kind: ExprKind::MyExpr {
9015 keyword: kw_owned,
9016 decls,
9017 },
9018 line,
9019 });
9020 }
9021 }
9022 match self.peek().clone() {
9023 Token::Integer(n) => {
9024 self.advance();
9025 Ok(Expr {
9026 kind: ExprKind::Integer(n),
9027 line,
9028 })
9029 }
9030 Token::Float(f) => {
9031 self.advance();
9032 Ok(Expr {
9033 kind: ExprKind::Float(f),
9034 line,
9035 })
9036 }
9037 Token::ArrowBrace => {
9043 self.advance();
9044 let mut stmts = Vec::new();
9045 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9046 if self.eat(&Token::Semicolon) {
9047 continue;
9048 }
9049 stmts.push(self.parse_statement()?);
9050 }
9051 self.expect(&Token::RBrace)?;
9052 let inner_line = stmts.first().map(|s| s.line).unwrap_or(line);
9053 let inner = Expr {
9054 kind: ExprKind::CodeRef {
9055 params: vec![],
9056 body: stmts,
9057 },
9058 line: inner_line,
9059 };
9060 Ok(Expr {
9061 kind: ExprKind::Do(Box::new(inner)),
9062 line,
9063 })
9064 }
9065 Token::Star => {
9066 self.advance();
9067 if matches!(self.peek(), Token::LBrace) {
9068 self.advance();
9069 let inner = self.parse_expression()?;
9070 self.expect(&Token::RBrace)?;
9071 return Ok(Expr {
9072 kind: ExprKind::Deref {
9073 expr: Box::new(inner),
9074 kind: Sigil::Typeglob,
9075 },
9076 line,
9077 });
9078 }
9079 if matches!(
9081 self.peek(),
9082 Token::ScalarVar(_)
9083 | Token::ArrayVar(_)
9084 | Token::HashVar(_)
9085 | Token::DerefScalarVar(_)
9086 | Token::HashPercent
9087 ) {
9088 let inner = self.parse_postfix()?;
9089 return Ok(Expr {
9090 kind: ExprKind::TypeglobExpr(Box::new(inner)),
9091 line,
9092 });
9093 }
9094 let mut full_name = match self.advance() {
9096 (Token::Ident(n), _) => n,
9097 (Token::X, _) => "x".to_string(),
9098 (tok, l) => {
9099 return Err(self
9100 .syntax_err(format!("Expected identifier after *, got {:?}", tok), l));
9101 }
9102 };
9103 while self.eat(&Token::PackageSep) {
9104 match self.advance() {
9105 (Token::Ident(part), _) => {
9106 full_name = format!("{}::{}", full_name, part);
9107 }
9108 (Token::X, _) => {
9109 full_name = format!("{}::x", full_name);
9110 }
9111 (tok, l) => {
9112 return Err(self.syntax_err(
9113 format!("Expected identifier after :: in typeglob, got {:?}", tok),
9114 l,
9115 ));
9116 }
9117 }
9118 }
9119 Ok(Expr {
9120 kind: ExprKind::Typeglob(full_name),
9121 line,
9122 })
9123 }
9124 Token::SingleString(s) => {
9125 self.advance();
9126 Ok(Expr {
9127 kind: ExprKind::String(s),
9128 line,
9129 })
9130 }
9131 Token::DoubleString(s) => {
9132 self.advance();
9133 self.parse_interpolated_string(&s, line)
9134 }
9135 Token::BacktickString(s) => {
9136 self.advance();
9137 let inner = self.parse_interpolated_string(&s, line)?;
9138 Ok(Expr {
9139 kind: ExprKind::Qx(Box::new(inner)),
9140 line,
9141 })
9142 }
9143 Token::HereDoc(_, body, interpolate) => {
9144 self.advance();
9145 if interpolate {
9146 self.parse_interpolated_string(&body, line)
9147 } else {
9148 Ok(Expr {
9149 kind: ExprKind::String(body),
9150 line,
9151 })
9152 }
9153 }
9154 Token::Regex(pattern, flags, _delim) => {
9155 self.advance();
9156 Ok(Expr {
9157 kind: ExprKind::Regex(pattern, flags),
9158 line,
9159 })
9160 }
9161 Token::QW(words) => {
9162 self.advance();
9163 self.list_construct_close_pos = Some(self.pos);
9166 Ok(Expr {
9167 kind: ExprKind::QW(words),
9168 line,
9169 })
9170 }
9171 Token::DerefScalarVar(name) => {
9172 self.advance();
9173 Ok(Expr {
9174 kind: ExprKind::Deref {
9175 expr: Box::new(Expr {
9176 kind: ExprKind::ScalarVar(name),
9177 line,
9178 }),
9179 kind: Sigil::Scalar,
9180 },
9181 line,
9182 })
9183 }
9184 Token::ScalarVar(name) => {
9185 self.advance();
9186 Ok(Expr {
9187 kind: ExprKind::ScalarVar(name),
9188 line,
9189 })
9190 }
9191 Token::ArrayVar(name) => {
9192 self.advance();
9193 match self.peek() {
9195 Token::LBracket => {
9196 self.advance();
9197 let indices = self.parse_slice_arg_list(false)?;
9198 self.expect(&Token::RBracket)?;
9199 Ok(Expr {
9200 kind: ExprKind::ArraySlice {
9201 array: name,
9202 indices,
9203 },
9204 line,
9205 })
9206 }
9207 Token::LBrace if self.suppress_scalar_hash_brace == 0 => {
9208 self.advance();
9209 let keys = self.parse_slice_arg_list(true)?;
9210 self.expect(&Token::RBrace)?;
9211 Ok(Expr {
9212 kind: ExprKind::HashSlice { hash: name, keys },
9213 line,
9214 })
9215 }
9216 _ => Ok(Expr {
9217 kind: ExprKind::ArrayVar(name),
9218 line,
9219 }),
9220 }
9221 }
9222 Token::HashVar(name) => {
9223 self.advance();
9224 if matches!(self.peek(), Token::LBrace) && self.suppress_scalar_hash_brace == 0 {
9229 self.advance(); let keys = self.parse_slice_arg_list(true)?;
9231 self.expect(&Token::RBrace)?;
9232 return Ok(Expr {
9233 kind: ExprKind::HashKvSlice { hash: name, keys },
9234 line,
9235 });
9236 }
9237 Ok(Expr {
9238 kind: ExprKind::HashVar(name),
9239 line,
9240 })
9241 }
9242 Token::HashPercent => {
9243 self.advance();
9245 if matches!(self.peek(), Token::ScalarVar(_)) {
9246 let n = match self.advance() {
9247 (Token::ScalarVar(n), _) => n,
9248 (tok, l) => {
9249 return Err(self.syntax_err(
9250 format!("Expected scalar variable after %%, got {:?}", tok),
9251 l,
9252 ));
9253 }
9254 };
9255 return Ok(Expr {
9256 kind: ExprKind::Deref {
9257 expr: Box::new(Expr {
9258 kind: ExprKind::ScalarVar(n),
9259 line,
9260 }),
9261 kind: Sigil::Hash,
9262 },
9263 line,
9264 });
9265 }
9266 if matches!(self.peek(), Token::LBracket) {
9271 self.advance();
9272 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9273 self.expect(&Token::RBracket)?;
9274 let href = Expr {
9275 kind: ExprKind::HashRef(pairs),
9276 line,
9277 };
9278 return Ok(Expr {
9279 kind: ExprKind::Deref {
9280 expr: Box::new(href),
9281 kind: Sigil::Hash,
9282 },
9283 line,
9284 });
9285 }
9286 self.expect(&Token::LBrace)?;
9287 let looks_like_pair = matches!(
9293 self.peek(),
9294 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9295 ) && matches!(self.peek_at(1), Token::FatArrow);
9296 let inner = if looks_like_pair {
9297 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9298 Expr {
9299 kind: ExprKind::HashRef(pairs),
9300 line,
9301 }
9302 } else {
9303 self.parse_expression()?
9304 };
9305 self.expect(&Token::RBrace)?;
9306 Ok(Expr {
9307 kind: ExprKind::Deref {
9308 expr: Box::new(inner),
9309 kind: Sigil::Hash,
9310 },
9311 line,
9312 })
9313 }
9314 Token::ArrayAt => {
9315 self.advance();
9316 if matches!(self.peek(), Token::LBrace) {
9318 self.advance();
9319 let inner = self.parse_expression()?;
9320 self.expect(&Token::RBrace)?;
9321 if matches!(self.peek(), Token::LBrace) {
9326 self.advance();
9327 let keys = self.parse_slice_arg_list(true)?;
9328 self.expect(&Token::RBrace)?;
9329 return Ok(Expr {
9330 kind: ExprKind::HashSliceDeref {
9331 container: Box::new(inner),
9332 keys,
9333 },
9334 line,
9335 });
9336 }
9337 if matches!(self.peek(), Token::LBracket) {
9338 self.advance();
9339 let indices = self.parse_slice_arg_list(false)?;
9340 self.expect(&Token::RBracket)?;
9341 let source = Expr {
9342 kind: ExprKind::Deref {
9343 expr: Box::new(inner),
9344 kind: Sigil::Array,
9345 },
9346 line,
9347 };
9348 return Ok(Expr {
9349 kind: ExprKind::AnonymousListSlice {
9350 source: Box::new(source),
9351 indices,
9352 },
9353 line,
9354 });
9355 }
9356 return Ok(Expr {
9357 kind: ExprKind::Deref {
9358 expr: Box::new(inner),
9359 kind: Sigil::Array,
9360 },
9361 line,
9362 });
9363 }
9364 if matches!(self.peek(), Token::LBracket) {
9368 self.advance();
9369 let mut elems = Vec::new();
9370 if !matches!(self.peek(), Token::RBracket) {
9371 elems.push(self.parse_assign_expr()?);
9372 while self.eat(&Token::Comma) {
9373 if matches!(self.peek(), Token::RBracket) {
9374 break;
9375 }
9376 elems.push(self.parse_assign_expr()?);
9377 }
9378 }
9379 self.expect(&Token::RBracket)?;
9380 let aref = Expr {
9381 kind: ExprKind::ArrayRef(elems),
9382 line,
9383 };
9384 return Ok(Expr {
9385 kind: ExprKind::Deref {
9386 expr: Box::new(aref),
9387 kind: Sigil::Array,
9388 },
9389 line,
9390 });
9391 }
9392 let container = match self.peek().clone() {
9394 Token::ScalarVar(n) => {
9395 self.advance();
9396 Expr {
9397 kind: ExprKind::ScalarVar(n),
9398 line,
9399 }
9400 }
9401 _ => {
9402 return Err(self.syntax_err(
9403 "Expected `$name`, `{`, or `[` after `@` (e.g. `@$aref`, `@{expr}`, `@[1,2,3]`, or `@$href{keys}`)",
9404 line,
9405 ));
9406 }
9407 };
9408 if matches!(self.peek(), Token::LBrace) {
9409 self.advance();
9410 let keys = self.parse_slice_arg_list(true)?;
9411 self.expect(&Token::RBrace)?;
9412 return Ok(Expr {
9413 kind: ExprKind::HashSliceDeref {
9414 container: Box::new(container),
9415 keys,
9416 },
9417 line,
9418 });
9419 }
9420 Ok(Expr {
9421 kind: ExprKind::Deref {
9422 expr: Box::new(container),
9423 kind: Sigil::Array,
9424 },
9425 line,
9426 })
9427 }
9428 Token::LParen => {
9429 self.advance();
9430 if matches!(self.peek(), Token::RParen) {
9431 self.advance();
9432 self.list_construct_close_pos = Some(self.pos);
9435 return Ok(Expr {
9436 kind: ExprKind::List(vec![]),
9437 line,
9438 });
9439 }
9440 let saved_no_pipe = self.no_pipe_forward_depth;
9443 self.no_pipe_forward_depth = 0;
9444 let saved_indirect = self.suppress_indirect_paren_call;
9448 self.suppress_indirect_paren_call = 0;
9449 let expr = self.parse_expression();
9450 self.no_pipe_forward_depth = saved_no_pipe;
9451 self.suppress_indirect_paren_call = saved_indirect;
9452 let expr = expr?;
9453 self.expect(&Token::RParen)?;
9454 self.list_construct_close_pos = Some(self.pos);
9459 Ok(expr)
9460 }
9461 Token::LBracket => {
9462 self.advance();
9463 let elems = self.parse_arg_list()?;
9464 self.expect(&Token::RBracket)?;
9465 Ok(Expr {
9466 kind: ExprKind::ArrayRef(elems),
9467 line,
9468 })
9469 }
9470 Token::LBrace => {
9471 self.advance();
9473 let saved = self.pos;
9475 match self.try_parse_hash_ref() {
9476 Ok(pairs) => Ok(Expr {
9477 kind: ExprKind::HashRef(pairs),
9478 line,
9479 }),
9480 Err(_) => {
9481 self.pos = saved;
9482 let mut stmts = Vec::new();
9484 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
9485 if self.eat(&Token::Semicolon) {
9486 continue;
9487 }
9488 stmts.push(self.parse_statement()?);
9489 }
9490 self.expect(&Token::RBrace)?;
9491 Ok(Expr {
9492 kind: ExprKind::CodeRef {
9493 params: vec![],
9494 body: stmts,
9495 },
9496 line,
9497 })
9498 }
9499 }
9500 }
9501 Token::Diamond => {
9502 self.advance();
9503 Ok(Expr {
9504 kind: ExprKind::ReadLine(None),
9505 line,
9506 })
9507 }
9508 Token::ReadLine(handle) => {
9509 self.advance();
9510 Ok(Expr {
9511 kind: ExprKind::ReadLine(Some(handle)),
9512 line,
9513 })
9514 }
9515
9516 Token::ThreadArrow => {
9518 self.advance();
9519 self.parse_thread_macro(line, false)
9520 }
9521 Token::ThreadArrowLast => {
9522 self.advance();
9523 self.parse_thread_macro(line, true)
9524 }
9525 Token::ThreadArrowStream => {
9526 self.advance();
9527 let mut stages = Vec::new();
9528 self.parse_thread_macro_inner(line, false, Some(&mut stages))
9529 }
9530 Token::ThreadArrowStreamLast => {
9531 self.advance();
9532 let mut stages = Vec::new();
9533 self.parse_thread_macro_inner(line, true, Some(&mut stages))
9534 }
9535 Token::ThreadArrowPar => {
9536 self.advance();
9537 self.parse_thread_macro_chunk_par(line, false)
9538 }
9539 Token::ThreadArrowParLast => {
9540 self.advance();
9541 self.parse_thread_macro_chunk_par(line, true)
9542 }
9543 Token::ThreadArrowDist => {
9544 self.advance();
9545 self.parse_thread_macro_dist(line, false)
9546 }
9547 Token::ThreadArrowDistLast => {
9548 self.advance();
9549 self.parse_thread_macro_dist(line, true)
9550 }
9551 Token::Ident(ref name) => {
9552 let name = name.clone();
9553 if name.starts_with('\x00') {
9555 self.advance();
9556 let parts: Vec<&str> = name.split('\x00').collect();
9557 if parts.len() >= 4 && parts[1] == "s" {
9558 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9559 return Ok(Expr {
9560 kind: ExprKind::Substitution {
9561 expr: Box::new(Expr {
9562 kind: ExprKind::ScalarVar("_".into()),
9563 line,
9564 }),
9565 pattern: parts[2].to_string(),
9566 replacement: parts[3].to_string(),
9567 flags: parts.get(4).unwrap_or(&"").to_string(),
9568 delim,
9569 },
9570 line,
9571 });
9572 }
9573 if parts.len() >= 4 && parts[1] == "tr" {
9574 let delim = parts.get(5).and_then(|s| s.chars().next()).unwrap_or('/');
9575 return Ok(Expr {
9576 kind: ExprKind::Transliterate {
9577 expr: Box::new(Expr {
9578 kind: ExprKind::ScalarVar("_".into()),
9579 line,
9580 }),
9581 from: parts[2].to_string(),
9582 to: parts[3].to_string(),
9583 flags: parts.get(4).unwrap_or(&"").to_string(),
9584 delim,
9585 },
9586 line,
9587 });
9588 }
9589 return Err(self.syntax_err("Unexpected encoded token", line));
9590 }
9591 self.parse_named_expr(name)
9592 }
9593
9594 Token::Percent => {
9597 self.advance();
9598 match self.peek().clone() {
9599 Token::Ident(name) => {
9600 self.advance();
9601 Ok(Expr {
9602 kind: ExprKind::HashVar(name),
9603 line,
9604 })
9605 }
9606 Token::ScalarVar(n) => {
9607 self.advance();
9608 Ok(Expr {
9609 kind: ExprKind::Deref {
9610 expr: Box::new(Expr {
9611 kind: ExprKind::ScalarVar(n),
9612 line,
9613 }),
9614 kind: Sigil::Hash,
9615 },
9616 line,
9617 })
9618 }
9619 Token::LBrace => {
9620 self.advance();
9621 let looks_like_pair = matches!(
9622 self.peek(),
9623 Token::Ident(_) | Token::SingleString(_) | Token::DoubleString(_)
9624 ) && matches!(self.peek_at(1), Token::FatArrow);
9625 let inner = if looks_like_pair {
9626 let pairs = self.parse_hashref_pairs_until(&Token::RBrace)?;
9627 Expr {
9628 kind: ExprKind::HashRef(pairs),
9629 line,
9630 }
9631 } else {
9632 self.parse_expression()?
9633 };
9634 self.expect(&Token::RBrace)?;
9635 Ok(Expr {
9636 kind: ExprKind::Deref {
9637 expr: Box::new(inner),
9638 kind: Sigil::Hash,
9639 },
9640 line,
9641 })
9642 }
9643 Token::LBracket => {
9644 self.advance();
9645 let pairs = self.parse_hashref_pairs_until(&Token::RBracket)?;
9646 self.expect(&Token::RBracket)?;
9647 let href = Expr {
9648 kind: ExprKind::HashRef(pairs),
9649 line,
9650 };
9651 Ok(Expr {
9652 kind: ExprKind::Deref {
9653 expr: Box::new(href),
9654 kind: Sigil::Hash,
9655 },
9656 line,
9657 })
9658 }
9659 tok => Err(self.syntax_err(
9660 format!(
9661 "Expected identifier, `$`, `{{`, or `[` after `%`, got {:?}",
9662 tok
9663 ),
9664 line,
9665 )),
9666 }
9667 }
9668
9669 tok => Err(self.syntax_err(format!("Unexpected token {:?}", tok), line)),
9670 }
9671 }
9672
9673 fn parse_named_expr(&mut self, mut name: String) -> StrykeResult<Expr> {
9674 let line = self.peek_line();
9675 self.advance(); while self.eat(&Token::PackageSep) {
9677 match self.advance() {
9678 (Token::Ident(part), _) => {
9679 name = format!("{}::{}", name, part);
9680 }
9681 (tok, err_line) => {
9682 return Err(self.syntax_err(
9683 format!("Expected identifier after `::`, got {:?}", tok),
9684 err_line,
9685 ));
9686 }
9687 }
9688 }
9689
9690 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name) {
9697 return Ok(Expr {
9698 kind: ExprKind::String(name),
9699 line,
9700 });
9701 }
9702
9703 if crate::compat_mode() {
9704 if let Some(ext) = Self::stryke_extension_name(&name) {
9705 if !self.declared_subs.contains(&name) {
9706 return Err(self.syntax_err(
9707 format!("`{ext}` is a stryke extension (disabled by --compat)"),
9708 line,
9709 ));
9710 }
9711 }
9712 }
9713
9714 if let Some(rest) = name.strip_prefix("CORE::") {
9721 name = rest.to_string();
9722 }
9723
9724 match name.as_str() {
9725 "__FILE__" => Ok(Expr {
9726 kind: ExprKind::MagicConst(MagicConstKind::File),
9727 line,
9728 }),
9729 "__LINE__" => Ok(Expr {
9730 kind: ExprKind::MagicConst(MagicConstKind::Line),
9731 line,
9732 }),
9733 "__SUB__" => Ok(Expr {
9734 kind: ExprKind::MagicConst(MagicConstKind::Sub),
9735 line,
9736 }),
9737 "__PACKAGE__" => Ok(Expr {
9742 kind: ExprKind::String(self.current_package.clone()),
9743 line,
9744 }),
9745 "stdin" => Ok(Expr {
9746 kind: ExprKind::FuncCall {
9747 name: "stdin".into(),
9748 args: vec![],
9749 },
9750 line,
9751 }),
9752 "range" => {
9753 let args = self.parse_builtin_args()?;
9754 Ok(Expr {
9755 kind: ExprKind::FuncCall {
9756 name: "range".into(),
9757 args,
9758 },
9759 line,
9760 })
9761 }
9762 "print" | "pr" => self.parse_print_like(|h, a| ExprKind::Print { handle: h, args: a }),
9763 "say" => {
9764 if crate::no_interop_mode() {
9765 return Err(
9766 self.syntax_err("stryke uses `p` instead of `say` (--no-interop)", line)
9767 );
9768 }
9769 self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a })
9770 }
9771 "p" => self.parse_print_like(|h, a| ExprKind::Say { handle: h, args: a }),
9772 "printf" => self.parse_print_like(|h, a| ExprKind::Printf { handle: h, args: a }),
9773 "die" => {
9774 let args = self.parse_list_until_terminator()?;
9775 Ok(Expr {
9776 kind: ExprKind::Die(args),
9777 line,
9778 })
9779 }
9780 "warn" => {
9781 let args = self.parse_list_until_terminator()?;
9782 Ok(Expr {
9783 kind: ExprKind::Warn(args),
9784 line,
9785 })
9786 }
9787 "croak" | "confess" => {
9792 let args = self.parse_list_until_terminator()?;
9793 Ok(Expr {
9794 kind: ExprKind::Die(args),
9795 line,
9796 })
9797 }
9798 "carp" | "cluck" => {
9800 let args = self.parse_list_until_terminator()?;
9801 Ok(Expr {
9802 kind: ExprKind::Warn(args),
9803 line,
9804 })
9805 }
9806 "chomp" => {
9807 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9808 return Ok(e);
9809 }
9810 let a = self.parse_one_arg_or_default()?;
9811 Ok(Expr {
9812 kind: ExprKind::Chomp(Box::new(a)),
9813 line,
9814 })
9815 }
9816 "chop" => {
9817 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9818 return Ok(e);
9819 }
9820 let a = self.parse_one_arg_or_default()?;
9821 Ok(Expr {
9822 kind: ExprKind::Chop(Box::new(a)),
9823 line,
9824 })
9825 }
9826 "length" => {
9827 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9828 return Ok(e);
9829 }
9830 let a = self.parse_one_arg_or_default()?;
9831 Ok(Expr {
9832 kind: ExprKind::Length(Box::new(a)),
9833 line,
9834 })
9835 }
9836 "defined" => {
9837 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9838 return Ok(e);
9839 }
9840 let a = if matches!(
9848 self.peek(),
9849 Token::Semicolon
9850 | Token::RBrace
9851 | Token::RParen
9852 | Token::RBracket
9853 | Token::Eof
9854 | Token::Comma
9855 | Token::FatArrow
9856 | Token::PipeForward
9857 | Token::Question
9858 | Token::Colon
9859 | Token::NumEq
9860 | Token::NumNe
9861 | Token::NumLt
9862 | Token::NumGt
9863 | Token::NumLe
9864 | Token::NumGe
9865 | Token::Spaceship
9866 | Token::StrEq
9867 | Token::StrNe
9868 | Token::StrLt
9869 | Token::StrGt
9870 | Token::StrLe
9871 | Token::StrGe
9872 | Token::StrCmp
9873 | Token::LogAnd
9874 | Token::LogOr
9875 | Token::LogNot
9876 | Token::LogAndWord
9877 | Token::LogOrWord
9878 | Token::LogNotWord
9879 | Token::DefinedOr
9880 | Token::Range
9881 | Token::RangeExclusive
9882 | Token::Assign
9883 | Token::PlusAssign
9884 | Token::MinusAssign
9885 | Token::MulAssign
9886 | Token::DivAssign
9887 | Token::ModAssign
9888 | Token::PowAssign
9889 | Token::DotAssign
9890 | Token::AndAssign
9891 | Token::OrAssign
9892 | Token::XorAssign
9893 | Token::DefinedOrAssign
9894 | Token::ShiftLeftAssign
9895 | Token::ShiftRightAssign
9896 | Token::BitAndAssign
9897 | Token::BitOrAssign
9898 ) {
9899 Expr {
9900 kind: ExprKind::ScalarVar("_".into()),
9901 line: self.peek_line(),
9902 }
9903 } else if matches!(self.peek(), Token::LParen)
9904 && matches!(self.peek_at(1), Token::RParen)
9905 {
9906 let pl = self.peek_line();
9907 self.advance();
9908 self.advance();
9909 Expr {
9910 kind: ExprKind::ScalarVar("_".into()),
9911 line: pl,
9912 }
9913 } else {
9914 self.parse_named_unary_arg()?
9915 };
9916 Ok(Expr {
9917 kind: ExprKind::Defined(Box::new(a)),
9918 line,
9919 })
9920 }
9921 "ref" => {
9922 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9923 return Ok(e);
9924 }
9925 let a = self.parse_one_arg_or_default()?;
9926 Ok(Expr {
9927 kind: ExprKind::Ref(Box::new(a)),
9928 line,
9929 })
9930 }
9931 "undef" => {
9932 if self.peek_line() == self.prev_line()
9935 && matches!(
9936 self.peek(),
9937 Token::ScalarVar(_) | Token::ArrayVar(_) | Token::HashVar(_)
9938 )
9939 {
9940 let target = self.parse_primary()?;
9941 return Ok(Expr {
9942 kind: ExprKind::Assign {
9943 target: Box::new(target),
9944 value: Box::new(Expr {
9945 kind: ExprKind::Undef,
9946 line,
9947 }),
9948 },
9949 line,
9950 });
9951 }
9952 Ok(Expr {
9953 kind: ExprKind::Undef,
9954 line,
9955 })
9956 }
9957 "scalar" => {
9958 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9959 return Ok(e);
9960 }
9961 if crate::no_interop_mode() {
9962 return Err(self.syntax_err(
9963 "stryke uses `len` (also `cnt` / `count`) instead of `scalar` (--no-interop)",
9964 line,
9965 ));
9966 }
9967 let a = self.parse_one_arg_or_default()?;
9968 Ok(Expr {
9969 kind: ExprKind::ScalarContext(Box::new(a)),
9970 line,
9971 })
9972 }
9973 "abs" => {
9974 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9975 return Ok(e);
9976 }
9977 let a = self.parse_one_arg_or_default()?;
9978 Ok(Expr {
9979 kind: ExprKind::Abs(Box::new(a)),
9980 line,
9981 })
9982 }
9983 "inc" | "dec" => {
9988 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
9989 return Ok(e);
9990 }
9991 let a = self.parse_one_arg_or_default()?;
9992 Ok(Expr {
9993 kind: ExprKind::FuncCall {
9994 name,
9995 args: vec![a],
9996 },
9997 line,
9998 })
9999 }
10000 "int" => {
10001 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10002 return Ok(e);
10003 }
10004 let a = self.parse_one_arg_or_default()?;
10005 Ok(Expr {
10006 kind: ExprKind::Int(Box::new(a)),
10007 line,
10008 })
10009 }
10010 "sqrt" => {
10011 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10012 return Ok(e);
10013 }
10014 let a = self.parse_one_arg_or_default()?;
10015 Ok(Expr {
10016 kind: ExprKind::Sqrt(Box::new(a)),
10017 line,
10018 })
10019 }
10020 "sin" => {
10021 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10022 return Ok(e);
10023 }
10024 let a = self.parse_one_arg_or_default()?;
10025 Ok(Expr {
10026 kind: ExprKind::Sin(Box::new(a)),
10027 line,
10028 })
10029 }
10030 "cos" => {
10031 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10032 return Ok(e);
10033 }
10034 let a = self.parse_one_arg_or_default()?;
10035 Ok(Expr {
10036 kind: ExprKind::Cos(Box::new(a)),
10037 line,
10038 })
10039 }
10040 "atan2" => {
10041 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10042 return Ok(e);
10043 }
10044 let args = self.parse_builtin_args()?;
10045 if args.len() != 2 {
10046 return Err(self.syntax_err("atan2 requires two arguments", line));
10047 }
10048 Ok(Expr {
10049 kind: ExprKind::Atan2 {
10050 y: Box::new(args[0].clone()),
10051 x: Box::new(args[1].clone()),
10052 },
10053 line,
10054 })
10055 }
10056 "exp" => {
10057 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10058 return Ok(e);
10059 }
10060 let a = self.parse_one_arg_or_default()?;
10061 Ok(Expr {
10062 kind: ExprKind::Exp(Box::new(a)),
10063 line,
10064 })
10065 }
10066 "log" => {
10067 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10068 return Ok(e);
10069 }
10070 let a = self.parse_one_arg_or_default()?;
10071 Ok(Expr {
10072 kind: ExprKind::Log(Box::new(a)),
10073 line,
10074 })
10075 }
10076 "input" => {
10077 let args = if matches!(
10078 self.peek(),
10079 Token::Semicolon
10080 | Token::RBrace
10081 | Token::RParen
10082 | Token::Eof
10083 | Token::Comma
10084 | Token::PipeForward
10085 ) {
10086 vec![]
10087 } else if matches!(self.peek(), Token::LParen) {
10088 self.advance();
10089 if matches!(self.peek(), Token::RParen) {
10090 self.advance();
10091 vec![]
10092 } else {
10093 let a = self.parse_expression()?;
10094 self.expect(&Token::RParen)?;
10095 vec![a]
10096 }
10097 } else {
10098 let a = self.parse_one_arg()?;
10099 vec![a]
10100 };
10101 Ok(Expr {
10102 kind: ExprKind::FuncCall {
10103 name: "input".to_string(),
10104 args,
10105 },
10106 line,
10107 })
10108 }
10109 "rand" => {
10110 if matches!(
10111 self.peek(),
10112 Token::Semicolon
10113 | Token::RBrace
10114 | Token::RParen
10115 | Token::Eof
10116 | Token::Comma
10117 | Token::PipeForward
10118 ) {
10119 Ok(Expr {
10120 kind: ExprKind::Rand(None),
10121 line,
10122 })
10123 } else if matches!(self.peek(), Token::LParen) {
10124 self.advance();
10125 if matches!(self.peek(), Token::RParen) {
10126 self.advance();
10127 Ok(Expr {
10128 kind: ExprKind::Rand(None),
10129 line,
10130 })
10131 } else {
10132 let a = self.parse_expression()?;
10133 self.expect(&Token::RParen)?;
10134 Ok(Expr {
10135 kind: ExprKind::Rand(Some(Box::new(a))),
10136 line,
10137 })
10138 }
10139 } else {
10140 let a = self.parse_one_arg()?;
10141 Ok(Expr {
10142 kind: ExprKind::Rand(Some(Box::new(a))),
10143 line,
10144 })
10145 }
10146 }
10147 "srand" => {
10148 if matches!(
10149 self.peek(),
10150 Token::Semicolon
10151 | Token::RBrace
10152 | Token::RParen
10153 | Token::Eof
10154 | Token::Comma
10155 | Token::PipeForward
10156 ) {
10157 Ok(Expr {
10158 kind: ExprKind::Srand(None),
10159 line,
10160 })
10161 } else if matches!(self.peek(), Token::LParen) {
10162 self.advance();
10163 if matches!(self.peek(), Token::RParen) {
10164 self.advance();
10165 Ok(Expr {
10166 kind: ExprKind::Srand(None),
10167 line,
10168 })
10169 } else {
10170 let a = self.parse_expression()?;
10171 self.expect(&Token::RParen)?;
10172 Ok(Expr {
10173 kind: ExprKind::Srand(Some(Box::new(a))),
10174 line,
10175 })
10176 }
10177 } else {
10178 let a = self.parse_one_arg()?;
10179 Ok(Expr {
10180 kind: ExprKind::Srand(Some(Box::new(a))),
10181 line,
10182 })
10183 }
10184 }
10185 "hex" => {
10186 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10187 return Ok(e);
10188 }
10189 let a = self.parse_one_arg_or_default()?;
10190 Ok(Expr {
10191 kind: ExprKind::Hex(Box::new(a)),
10192 line,
10193 })
10194 }
10195 "oct" => {
10196 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10197 return Ok(e);
10198 }
10199 let a = self.parse_one_arg_or_default()?;
10200 Ok(Expr {
10201 kind: ExprKind::Oct(Box::new(a)),
10202 line,
10203 })
10204 }
10205 "chr" => {
10206 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10207 return Ok(e);
10208 }
10209 let a = self.parse_one_arg_or_default()?;
10210 Ok(Expr {
10211 kind: ExprKind::Chr(Box::new(a)),
10212 line,
10213 })
10214 }
10215 "ord" => {
10216 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10217 return Ok(e);
10218 }
10219 let a = self.parse_one_arg_or_default()?;
10220 Ok(Expr {
10221 kind: ExprKind::Ord(Box::new(a)),
10222 line,
10223 })
10224 }
10225 "lc" => {
10226 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10227 return Ok(e);
10228 }
10229 let a = self.parse_one_arg_or_default()?;
10230 Ok(Expr {
10231 kind: ExprKind::Lc(Box::new(a)),
10232 line,
10233 })
10234 }
10235 "uc" => {
10236 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10237 return Ok(e);
10238 }
10239 let a = self.parse_one_arg_or_default()?;
10240 Ok(Expr {
10241 kind: ExprKind::Uc(Box::new(a)),
10242 line,
10243 })
10244 }
10245 "lcfirst" => {
10246 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10247 return Ok(e);
10248 }
10249 let a = self.parse_one_arg_or_default()?;
10250 Ok(Expr {
10251 kind: ExprKind::Lcfirst(Box::new(a)),
10252 line,
10253 })
10254 }
10255 "ucfirst" => {
10256 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10257 return Ok(e);
10258 }
10259 let a = self.parse_one_arg_or_default()?;
10260 Ok(Expr {
10261 kind: ExprKind::Ucfirst(Box::new(a)),
10262 line,
10263 })
10264 }
10265 "fc" => {
10266 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10267 return Ok(e);
10268 }
10269 let a = self.parse_one_arg_or_default()?;
10270 Ok(Expr {
10271 kind: ExprKind::Fc(Box::new(a)),
10272 line,
10273 })
10274 }
10275 "crypt" => {
10276 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10277 return Ok(e);
10278 }
10279 let args = self.parse_builtin_args()?;
10280 if args.len() != 2 {
10281 return Err(self.syntax_err("crypt requires two arguments", line));
10282 }
10283 Ok(Expr {
10284 kind: ExprKind::Crypt {
10285 plaintext: Box::new(args[0].clone()),
10286 salt: Box::new(args[1].clone()),
10287 },
10288 line,
10289 })
10290 }
10291 "pos" => {
10292 if matches!(
10293 self.peek(),
10294 Token::Semicolon
10295 | Token::RBrace
10296 | Token::RParen
10297 | Token::Eof
10298 | Token::Comma
10299 | Token::PipeForward
10300 ) {
10301 Ok(Expr {
10302 kind: ExprKind::Pos(None),
10303 line,
10304 })
10305 } else if matches!(self.peek(), Token::Assign) {
10306 self.advance();
10308 let rhs = self.parse_assign_expr()?;
10309 Ok(Expr {
10310 kind: ExprKind::Assign {
10311 target: Box::new(Expr {
10312 kind: ExprKind::Pos(Some(Box::new(Expr {
10313 kind: ExprKind::ScalarVar("_".into()),
10314 line,
10315 }))),
10316 line,
10317 }),
10318 value: Box::new(rhs),
10319 },
10320 line,
10321 })
10322 } else if matches!(self.peek(), Token::LParen) {
10323 self.advance();
10324 if matches!(self.peek(), Token::RParen) {
10325 self.advance();
10326 Ok(Expr {
10327 kind: ExprKind::Pos(None),
10328 line,
10329 })
10330 } else {
10331 let a = self.parse_expression()?;
10332 self.expect(&Token::RParen)?;
10333 Ok(Expr {
10334 kind: ExprKind::Pos(Some(Box::new(a))),
10335 line,
10336 })
10337 }
10338 } else {
10339 let saved = self.pos;
10340 let subj = self.parse_unary()?;
10341 if matches!(self.peek(), Token::Assign) {
10342 self.advance();
10343 let rhs = self.parse_assign_expr()?;
10344 Ok(Expr {
10345 kind: ExprKind::Assign {
10346 target: Box::new(Expr {
10347 kind: ExprKind::Pos(Some(Box::new(subj))),
10348 line,
10349 }),
10350 value: Box::new(rhs),
10351 },
10352 line,
10353 })
10354 } else {
10355 self.pos = saved;
10356 let a = self.parse_one_arg()?;
10357 Ok(Expr {
10358 kind: ExprKind::Pos(Some(Box::new(a))),
10359 line,
10360 })
10361 }
10362 }
10363 }
10364 "study" => {
10365 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10366 return Ok(e);
10367 }
10368 let a = self.parse_one_arg_or_default()?;
10369 Ok(Expr {
10370 kind: ExprKind::Study(Box::new(a)),
10371 line,
10372 })
10373 }
10374 "push" => {
10375 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10376 return Ok(e);
10377 }
10378 let args = self.parse_builtin_args()?;
10379 let (first, rest) = args
10380 .split_first()
10381 .ok_or_else(|| self.syntax_err("push requires arguments", line))?;
10382 if matches!(
10387 first.kind,
10388 ExprKind::ScalarVar(_)
10389 | ExprKind::Integer(_)
10390 | ExprKind::Float(_)
10391 | ExprKind::String(_)
10392 ) {
10393 return Err(self
10394 .syntax_err("Experimental push on scalar is now forbidden", line)
10395 .with_near("at EOF"));
10396 }
10397 Ok(Expr {
10398 kind: ExprKind::Push {
10399 array: Box::new(first.clone()),
10400 values: rest.to_vec(),
10401 },
10402 line,
10403 })
10404 }
10405 "pop" => {
10406 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10407 return Ok(e);
10408 }
10409 let a = self.parse_one_arg_or_argv()?;
10410 Ok(Expr {
10411 kind: ExprKind::Pop(Box::new(a)),
10412 line,
10413 })
10414 }
10415 "shift" => {
10416 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10417 return Ok(e);
10418 }
10419 let a = self.parse_one_arg_or_argv()?;
10420 Ok(Expr {
10421 kind: ExprKind::Shift(Box::new(a)),
10422 line,
10423 })
10424 }
10425 "unshift" => {
10426 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10427 return Ok(e);
10428 }
10429 let args = self.parse_builtin_args()?;
10430 let (first, rest) = args
10431 .split_first()
10432 .ok_or_else(|| self.syntax_err("unshift requires arguments", line))?;
10433 Ok(Expr {
10434 kind: ExprKind::Unshift {
10435 array: Box::new(first.clone()),
10436 values: rest.to_vec(),
10437 },
10438 line,
10439 })
10440 }
10441 "splice" => {
10442 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10443 return Ok(e);
10444 }
10445 let args = self.parse_builtin_args()?;
10446 let mut iter = args.into_iter();
10447 let array = Box::new(
10448 iter.next()
10449 .ok_or_else(|| self.syntax_err("splice requires arguments", line))?,
10450 );
10451 let offset = iter.next().map(Box::new);
10452 let length = iter.next().map(Box::new);
10453 let replacement: Vec<Expr> = iter.collect();
10454 Ok(Expr {
10455 kind: ExprKind::Splice {
10456 array,
10457 offset,
10458 length,
10459 replacement,
10460 },
10461 line,
10462 })
10463 }
10464 "splice_last" | "splice1" | "spl_last" => {
10469 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10470 return Ok(e);
10471 }
10472 let args = self.parse_builtin_args()?;
10473 let mut iter = args.into_iter();
10474 let array = Box::new(
10475 iter.next()
10476 .ok_or_else(|| self.syntax_err("splice_last requires arguments", line))?,
10477 );
10478 let offset = iter.next().map(Box::new);
10479 let length = iter.next().map(Box::new);
10480 let replacement: Vec<Expr> = iter.collect();
10481 let splice_expr = Expr {
10482 kind: ExprKind::Splice {
10483 array,
10484 offset,
10485 length,
10486 replacement,
10487 },
10488 line,
10489 };
10490 Ok(Expr {
10491 kind: ExprKind::FuncCall {
10492 name: "tail".to_string(),
10493 args: vec![splice_expr],
10494 },
10495 line,
10496 })
10497 }
10498 "delete" => {
10499 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10500 return Ok(e);
10501 }
10502 let a = self.parse_postfix()?;
10503 Ok(Expr {
10504 kind: ExprKind::Delete(Box::new(a)),
10505 line,
10506 })
10507 }
10508 "exists" => {
10509 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10510 return Ok(e);
10511 }
10512 let a = self.parse_unary()?;
10517 Ok(Expr {
10518 kind: ExprKind::Exists(Box::new(a)),
10519 line,
10520 })
10521 }
10522 "keys" => {
10523 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10524 return Ok(e);
10525 }
10526 let a = self.parse_one_arg_or_default()?;
10527 Ok(Expr {
10528 kind: ExprKind::Keys(Box::new(a)),
10529 line,
10530 })
10531 }
10532 "values" => {
10533 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10534 return Ok(e);
10535 }
10536 let a = self.parse_one_arg_or_default()?;
10537 Ok(Expr {
10538 kind: ExprKind::Values(Box::new(a)),
10539 line,
10540 })
10541 }
10542 "each" => {
10543 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10544 return Ok(e);
10545 }
10546 let a = self.parse_one_arg_or_default()?;
10547 Ok(Expr {
10548 kind: ExprKind::Each(Box::new(a)),
10549 line,
10550 })
10551 }
10552 "fore" | "e" | "ep" => {
10553 if matches!(self.peek(), Token::LBrace) {
10555 let (block, list) = self.parse_block_list()?;
10556 Ok(Expr {
10557 kind: ExprKind::ForEachExpr {
10558 block,
10559 list: Box::new(list),
10560 },
10561 line,
10562 })
10563 } else if self.in_pipe_rhs() {
10564 let is_terminal = matches!(
10567 self.peek(),
10568 Token::Semicolon
10569 | Token::RParen
10570 | Token::Eof
10571 | Token::PipeForward
10572 | Token::RBrace
10573 );
10574 let block = if name == "ep" && is_terminal {
10575 vec![Statement {
10576 label: None,
10577 kind: StmtKind::Expression(Expr {
10578 kind: ExprKind::Say {
10579 handle: None,
10580 args: vec![Expr {
10581 kind: ExprKind::ScalarVar("_".into()),
10582 line,
10583 }],
10584 },
10585 line,
10586 }),
10587 line,
10588 }]
10589 } else {
10590 let expr = self.parse_assign_expr_stop_at_pipe()?;
10591 let expr = Self::lift_bareword_to_topic_call(expr);
10592 vec![Statement {
10593 label: None,
10594 kind: StmtKind::Expression(expr),
10595 line,
10596 }]
10597 };
10598 let list = self.pipe_placeholder_list(line);
10599 Ok(Expr {
10600 kind: ExprKind::ForEachExpr {
10601 block,
10602 list: Box::new(list),
10603 },
10604 line,
10605 })
10606 } else {
10607 let expr = self.parse_assign_expr()?;
10616 let expr = Self::lift_bareword_to_topic_call(expr);
10617 if !matches!(self.peek(), Token::Comma) && name == "ep" {
10618 let block = vec![Statement {
10619 label: None,
10620 kind: StmtKind::Expression(Expr {
10621 kind: ExprKind::Say {
10622 handle: None,
10623 args: vec![Expr {
10624 kind: ExprKind::ScalarVar("_".into()),
10625 line,
10626 }],
10627 },
10628 line,
10629 }),
10630 line,
10631 }];
10632 return Ok(Expr {
10633 kind: ExprKind::ForEachExpr {
10634 block,
10635 list: Box::new(expr),
10636 },
10637 line,
10638 });
10639 }
10640 self.expect(&Token::Comma)?;
10641 let list_parts = self.parse_list_until_terminator()?;
10642 let list_expr = if list_parts.len() == 1 {
10643 list_parts.into_iter().next().unwrap()
10644 } else {
10645 Expr {
10646 kind: ExprKind::List(list_parts),
10647 line,
10648 }
10649 };
10650 let block = vec![Statement {
10651 label: None,
10652 kind: StmtKind::Expression(expr),
10653 line,
10654 }];
10655 Ok(Expr {
10656 kind: ExprKind::ForEachExpr {
10657 block,
10658 list: Box::new(list_expr),
10659 },
10660 line,
10661 })
10662 }
10663 }
10664 "rev" => {
10665 let prev = self.prev_line();
10671 let a = if self.in_pipe_rhs()
10672 && (matches!(
10673 self.peek(),
10674 Token::Semicolon | Token::RParen | Token::Eof | Token::PipeForward
10675 ) || self.peek_line() > prev)
10676 {
10677 self.pipe_placeholder_list(line)
10678 } else if self.peek_line() > prev {
10679 Expr {
10685 kind: ExprKind::ScalarVar("_".into()),
10686 line: prev,
10687 }
10688 } else if matches!(
10689 self.peek(),
10690 Token::Semicolon
10691 | Token::RBrace
10692 | Token::RParen
10693 | Token::RBracket
10694 | Token::Eof
10695 | Token::Comma
10696 | Token::FatArrow
10697 | Token::PipeForward
10698 ) {
10699 Expr {
10700 kind: ExprKind::ScalarVar("_".into()),
10701 line: self.peek_line(),
10702 }
10703 } else if matches!(self.peek(), Token::LParen)
10704 && matches!(self.peek_at(1), Token::RParen)
10705 {
10706 let pl = self.peek_line();
10709 self.advance(); self.advance(); Expr {
10712 kind: ExprKind::ScalarVar("_".into()),
10713 line: pl,
10714 }
10715 } else {
10716 self.parse_one_arg()?
10717 };
10718 Ok(Expr {
10719 kind: ExprKind::Rev(Box::new(a)),
10720 line,
10721 })
10722 }
10723 "reverse" => {
10724 if crate::no_interop_mode() {
10725 return Err(self.syntax_err(
10726 "stryke uses `rev` instead of `reverse` (--no-interop)",
10727 line,
10728 ));
10729 }
10730 let a = if self.in_pipe_rhs()
10732 && matches!(
10733 self.peek(),
10734 Token::Semicolon
10735 | Token::RBrace
10736 | Token::RParen
10737 | Token::Eof
10738 | Token::PipeForward
10739 ) {
10740 self.pipe_placeholder_list(line)
10741 } else if matches!(self.peek(), Token::LParen)
10742 && matches!(self.peek_at(1), Token::RParen)
10743 {
10744 self.advance();
10746 self.advance();
10747 Expr {
10748 kind: ExprKind::List(Vec::new()),
10749 line,
10750 }
10751 } else {
10752 self.parse_one_arg()?
10753 };
10754 Ok(Expr {
10755 kind: ExprKind::ReverseExpr(Box::new(a)),
10756 line,
10757 })
10758 }
10759 "reversed" | "rv" => {
10760 let a = if self.in_pipe_rhs()
10762 && matches!(
10763 self.peek(),
10764 Token::Semicolon
10765 | Token::RBrace
10766 | Token::RParen
10767 | Token::Eof
10768 | Token::PipeForward
10769 ) {
10770 self.pipe_placeholder_list(line)
10771 } else {
10772 self.parse_one_arg()?
10773 };
10774 Ok(Expr {
10775 kind: ExprKind::Rev(Box::new(a)),
10776 line,
10777 })
10778 }
10779 "join" => {
10780 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10781 return Ok(e);
10782 }
10783 let args = self.parse_builtin_args()?;
10784 if args.is_empty() {
10785 return Err(self.syntax_err("join requires separator and list", line));
10786 }
10787 if args.len() < 2 && !self.in_pipe_rhs() {
10789 return Err(self.syntax_err("join requires separator and list", line));
10790 }
10791 Ok(Expr {
10792 kind: ExprKind::JoinExpr {
10793 separator: Box::new(args[0].clone()),
10794 list: Box::new(Expr {
10795 kind: ExprKind::List(args[1..].to_vec()),
10796 line,
10797 }),
10798 },
10799 line,
10800 })
10801 }
10802 "split" => {
10803 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10804 return Ok(e);
10805 }
10806 let args = self.parse_builtin_args()?;
10807 let pattern = args.first().cloned().unwrap_or(Expr {
10808 kind: ExprKind::String(" ".into()),
10809 line,
10810 });
10811 let string = args.get(1).cloned().unwrap_or(Expr {
10812 kind: ExprKind::ScalarVar("_".into()),
10813 line,
10814 });
10815 let limit = args.get(2).cloned().map(Box::new);
10816 Ok(Expr {
10817 kind: ExprKind::SplitExpr {
10818 pattern: Box::new(pattern),
10819 string: Box::new(string),
10820 limit,
10821 },
10822 line,
10823 })
10824 }
10825 "substr" => {
10826 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10827 return Ok(e);
10828 }
10829 let args = self.parse_builtin_args()?;
10830 Ok(Expr {
10831 kind: ExprKind::Substr {
10832 string: Box::new(args[0].clone()),
10833 offset: Box::new(args[1].clone()),
10834 length: args.get(2).cloned().map(Box::new),
10835 replacement: args.get(3).cloned().map(Box::new),
10836 },
10837 line,
10838 })
10839 }
10840 "index" => {
10841 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10842 return Ok(e);
10843 }
10844 let args = self.parse_builtin_args()?;
10845 Ok(Expr {
10846 kind: ExprKind::Index {
10847 string: Box::new(args[0].clone()),
10848 substr: Box::new(args[1].clone()),
10849 position: args.get(2).cloned().map(Box::new),
10850 },
10851 line,
10852 })
10853 }
10854 "rindex" => {
10855 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10856 return Ok(e);
10857 }
10858 let args = self.parse_builtin_args()?;
10859 Ok(Expr {
10860 kind: ExprKind::Rindex {
10861 string: Box::new(args[0].clone()),
10862 substr: Box::new(args[1].clone()),
10863 position: args.get(2).cloned().map(Box::new),
10864 },
10865 line,
10866 })
10867 }
10868 "sprintf" => {
10869 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
10870 return Ok(e);
10871 }
10872 let args = self.parse_builtin_args()?;
10873 let (first, rest) = args
10874 .split_first()
10875 .ok_or_else(|| self.syntax_err("sprintf requires format", line))?;
10876 Ok(Expr {
10877 kind: ExprKind::Sprintf {
10878 format: Box::new(first.clone()),
10879 args: rest.to_vec(),
10880 },
10881 line,
10882 })
10883 }
10884 "map" | "flat_map" | "maps" | "flat_maps" => {
10885 let flatten_array_refs = matches!(name.as_str(), "flat_map" | "flat_maps");
10886 let stream = matches!(name.as_str(), "maps" | "flat_maps");
10887 if matches!(self.peek(), Token::LBrace) {
10888 let (block, list) = self.parse_block_list()?;
10889 Ok(Expr {
10890 kind: ExprKind::MapExpr {
10891 block,
10892 list: Box::new(list),
10893 flatten_array_refs,
10894 stream,
10895 },
10896 line,
10897 })
10898 } else {
10899 let expr = self.parse_assign_expr_stop_at_pipe()?;
10900 let expr = Self::lift_bareword_to_topic_call(expr);
10903 let list_expr = if self.pipe_supplies_slurped_list_operand() {
10904 self.pipe_placeholder_list(line)
10905 } else {
10906 self.expect(&Token::Comma)?;
10907 let list_parts = self.parse_list_until_terminator()?;
10908 if list_parts.len() == 1 {
10909 list_parts.into_iter().next().unwrap()
10910 } else {
10911 Expr {
10912 kind: ExprKind::List(list_parts),
10913 line,
10914 }
10915 }
10916 };
10917 Ok(Expr {
10918 kind: ExprKind::MapExprComma {
10919 expr: Box::new(expr),
10920 list: Box::new(list_expr),
10921 flatten_array_refs,
10922 stream,
10923 },
10924 line,
10925 })
10926 }
10927 }
10928 "cond" => {
10929 if crate::compat_mode() {
10930 return Err(self
10931 .syntax_err("`cond` is a stryke extension (disabled by --compat)", line));
10932 }
10933 self.parse_cond_expr(line)
10934 }
10935 "match" => {
10936 if crate::compat_mode() {
10937 return Err(self.syntax_err(
10938 "algebraic `match` is a stryke extension (disabled by --compat)",
10939 line,
10940 ));
10941 }
10942 self.parse_algebraic_match_expr(line)
10943 }
10944 "grep" | "greps" | "filter" | "fi" | "find_all" => {
10945 let keyword = match name.as_str() {
10946 "grep" => crate::ast::GrepBuiltinKeyword::Grep,
10947 "greps" => crate::ast::GrepBuiltinKeyword::Greps,
10948 "filter" | "fi" => crate::ast::GrepBuiltinKeyword::Filter,
10949 "find_all" => crate::ast::GrepBuiltinKeyword::FindAll,
10950 _ => unreachable!(),
10951 };
10952 if matches!(self.peek(), Token::LBrace) {
10953 let (block, list) = self.parse_block_list()?;
10954 Ok(Expr {
10955 kind: ExprKind::GrepExpr {
10956 block,
10957 list: Box::new(list),
10958 keyword,
10959 },
10960 line,
10961 })
10962 } else {
10963 let expr = self.parse_assign_expr_stop_at_pipe()?;
10964 if self.pipe_supplies_slurped_list_operand() {
10965 let list = self.pipe_placeholder_list(line);
10970 let topic = Expr {
10971 kind: ExprKind::ScalarVar("_".into()),
10972 line,
10973 };
10974 let test = match &expr.kind {
10975 ExprKind::Integer(_) | ExprKind::Float(_) => Expr {
10976 kind: ExprKind::BinOp {
10977 op: BinOp::NumEq,
10978 left: Box::new(topic),
10979 right: Box::new(expr),
10980 },
10981 line,
10982 },
10983 ExprKind::String(_) | ExprKind::InterpolatedString(_) => Expr {
10984 kind: ExprKind::BinOp {
10985 op: BinOp::StrEq,
10986 left: Box::new(topic),
10987 right: Box::new(expr),
10988 },
10989 line,
10990 },
10991 ExprKind::Regex { .. } => Expr {
10992 kind: ExprKind::BinOp {
10993 op: BinOp::BindMatch,
10994 left: Box::new(topic),
10995 right: Box::new(expr),
10996 },
10997 line,
10998 },
10999 _ => {
11000 let expr = Self::lift_bareword_to_topic_call(expr);
11006 return Ok(Expr {
11007 kind: ExprKind::GrepExprComma {
11008 expr: Box::new(expr),
11009 list: Box::new(list),
11010 keyword,
11011 },
11012 line,
11013 });
11014 }
11015 };
11016 let block = vec![Statement {
11017 label: None,
11018 kind: StmtKind::Expression(test),
11019 line,
11020 }];
11021 Ok(Expr {
11022 kind: ExprKind::GrepExpr {
11023 block,
11024 list: Box::new(list),
11025 keyword,
11026 },
11027 line,
11028 })
11029 } else {
11030 let expr = Self::lift_bareword_to_topic_call(expr);
11031 self.expect(&Token::Comma)?;
11032 let list_parts = self.parse_list_until_terminator()?;
11033 let list_expr = if list_parts.len() == 1 {
11034 list_parts.into_iter().next().unwrap()
11035 } else {
11036 Expr {
11037 kind: ExprKind::List(list_parts),
11038 line,
11039 }
11040 };
11041 Ok(Expr {
11042 kind: ExprKind::GrepExprComma {
11043 expr: Box::new(expr),
11044 list: Box::new(list_expr),
11045 keyword,
11046 },
11047 line,
11048 })
11049 }
11050 }
11051 }
11052 "sort" => {
11053 use crate::ast::SortComparator;
11054 if matches!(self.peek(), Token::LBrace) {
11055 let block = self.parse_block()?;
11056 let block_end_line = self.prev_line();
11057 let _ = self.eat(&Token::Comma);
11058 let list = if self.in_pipe_rhs()
11059 && (matches!(
11060 self.peek(),
11061 Token::Semicolon
11062 | Token::RBrace
11063 | Token::RParen
11064 | Token::Eof
11065 | Token::PipeForward
11066 ) || self.peek_line() > block_end_line)
11067 {
11068 self.pipe_placeholder_list(line)
11069 } else {
11070 self.parse_expression()?
11071 };
11072 Ok(Expr {
11073 kind: ExprKind::SortExpr {
11074 cmp: Some(SortComparator::Block(block)),
11075 list: Box::new(list),
11076 },
11077 line,
11078 })
11079 } else if matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b") {
11080 let block = self.parse_block_or_bareword_cmp_block()?;
11082 let _ = self.eat(&Token::Comma);
11083 let list = if self.in_pipe_rhs()
11084 && matches!(
11085 self.peek(),
11086 Token::Semicolon
11087 | Token::RBrace
11088 | Token::RParen
11089 | Token::Eof
11090 | Token::PipeForward
11091 ) {
11092 self.pipe_placeholder_list(line)
11093 } else {
11094 self.parse_expression()?
11095 };
11096 Ok(Expr {
11097 kind: ExprKind::SortExpr {
11098 cmp: Some(SortComparator::Block(block)),
11099 list: Box::new(list),
11100 },
11101 line,
11102 })
11103 } else if matches!(self.peek(), Token::ScalarVar(_)) {
11104 self.suppress_indirect_paren_call =
11107 self.suppress_indirect_paren_call.saturating_add(1);
11108 let code = self.parse_assign_expr()?;
11109 self.suppress_indirect_paren_call =
11110 self.suppress_indirect_paren_call.saturating_sub(1);
11111 let _ = self.eat(&Token::Comma);
11112 let list = if self.in_pipe_rhs()
11113 && matches!(
11114 self.peek(),
11115 Token::Semicolon
11116 | Token::RBrace
11117 | Token::RParen
11118 | Token::Eof
11119 | Token::PipeForward
11120 ) {
11121 self.pipe_placeholder_list(line)
11122 } else if matches!(self.peek(), Token::LParen) {
11123 self.advance();
11124 let e = self.parse_expression()?;
11125 self.expect(&Token::RParen)?;
11126 e
11127 } else {
11128 self.parse_expression()?
11129 };
11130 Ok(Expr {
11131 kind: ExprKind::SortExpr {
11132 cmp: Some(SortComparator::Code(Box::new(code))),
11133 list: Box::new(list),
11134 },
11135 line,
11136 })
11137 } else if matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
11138 {
11139 let block = self.parse_block_or_bareword_cmp_block()?;
11141 let _ = self.eat(&Token::Comma);
11142 let list = if self.in_pipe_rhs()
11143 && matches!(
11144 self.peek(),
11145 Token::Semicolon
11146 | Token::RBrace
11147 | Token::RParen
11148 | Token::Eof
11149 | Token::PipeForward
11150 ) {
11151 self.pipe_placeholder_list(line)
11152 } else {
11153 self.parse_expression()?
11154 };
11155 Ok(Expr {
11156 kind: ExprKind::SortExpr {
11157 cmp: Some(SortComparator::Block(block)),
11158 list: Box::new(list),
11159 },
11160 line,
11161 })
11162 } else {
11163 let list = if self.in_pipe_rhs()
11169 && (matches!(
11170 self.peek(),
11171 Token::Semicolon
11172 | Token::RBrace
11173 | Token::RParen
11174 | Token::Eof
11175 | Token::PipeForward
11176 ) || self.peek_line() > line)
11177 {
11178 self.pipe_placeholder_list(line)
11179 } else {
11180 self.parse_expression()?
11181 };
11182 Ok(Expr {
11183 kind: ExprKind::SortExpr {
11184 cmp: None,
11185 list: Box::new(list),
11186 },
11187 line,
11188 })
11189 }
11190 }
11191 "reduce" | "fold" | "inject" => {
11192 let (block, list) = self.parse_block_list()?;
11193 Ok(Expr {
11194 kind: ExprKind::ReduceExpr {
11195 block,
11196 list: Box::new(list),
11197 },
11198 line,
11199 })
11200 }
11201 "pmap" => {
11203 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11204 Ok(Expr {
11205 kind: ExprKind::PMapExpr {
11206 block,
11207 list: Box::new(list),
11208 progress: progress.map(Box::new),
11209 flat_outputs: false,
11210 on_cluster: None,
11211 stream: false,
11212 },
11213 line,
11214 })
11215 }
11216 "pmap_on" => {
11217 let (cluster, block, list, progress) =
11218 self.parse_cluster_block_then_list_optional_progress()?;
11219 Ok(Expr {
11220 kind: ExprKind::PMapExpr {
11221 block,
11222 list: Box::new(list),
11223 progress: progress.map(Box::new),
11224 flat_outputs: false,
11225 on_cluster: Some(Box::new(cluster)),
11226 stream: false,
11227 },
11228 line,
11229 })
11230 }
11231 "pflat_map" => {
11232 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11233 Ok(Expr {
11234 kind: ExprKind::PMapExpr {
11235 block,
11236 list: Box::new(list),
11237 progress: progress.map(Box::new),
11238 flat_outputs: true,
11239 on_cluster: None,
11240 stream: false,
11241 },
11242 line,
11243 })
11244 }
11245 "pflat_map_on" => {
11246 let (cluster, block, list, progress) =
11247 self.parse_cluster_block_then_list_optional_progress()?;
11248 Ok(Expr {
11249 kind: ExprKind::PMapExpr {
11250 block,
11251 list: Box::new(list),
11252 progress: progress.map(Box::new),
11253 flat_outputs: true,
11254 on_cluster: Some(Box::new(cluster)),
11255 stream: false,
11256 },
11257 line,
11258 })
11259 }
11260 "pmaps" => {
11261 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11262 Ok(Expr {
11263 kind: ExprKind::PMapExpr {
11264 block,
11265 list: Box::new(list),
11266 progress: progress.map(Box::new),
11267 flat_outputs: false,
11268 on_cluster: None,
11269 stream: true,
11270 },
11271 line,
11272 })
11273 }
11274 "pflat_maps" => {
11275 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11276 Ok(Expr {
11277 kind: ExprKind::PMapExpr {
11278 block,
11279 list: Box::new(list),
11280 progress: progress.map(Box::new),
11281 flat_outputs: true,
11282 on_cluster: None,
11283 stream: true,
11284 },
11285 line,
11286 })
11287 }
11288 "pgreps" => {
11289 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11290 Ok(Expr {
11291 kind: ExprKind::PGrepExpr {
11292 block,
11293 list: Box::new(list),
11294 progress: progress.map(Box::new),
11295 stream: true,
11296 },
11297 line,
11298 })
11299 }
11300 "pmap_chunked" => {
11301 let chunk_size = self.parse_assign_expr()?;
11302 let block = self.parse_block_or_bareword_block()?;
11303 self.eat(&Token::Comma);
11304 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11305 Ok(Expr {
11306 kind: ExprKind::PMapChunkedExpr {
11307 chunk_size: Box::new(chunk_size),
11308 block,
11309 list: Box::new(list),
11310 progress: progress.map(Box::new),
11311 },
11312 line,
11313 })
11314 }
11315 "pgrep" => {
11316 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11317 Ok(Expr {
11318 kind: ExprKind::PGrepExpr {
11319 block,
11320 list: Box::new(list),
11321 progress: progress.map(Box::new),
11322 stream: false,
11323 },
11324 line,
11325 })
11326 }
11327 "pfor" => {
11328 if matches!(self.peek(), Token::LParen) {
11329 self.expect(&Token::LParen)?;
11330 let list = self.parse_expression()?;
11331 self.expect(&Token::RParen)?;
11332 let block = self.parse_block()?;
11333 Ok(Expr {
11334 kind: ExprKind::PForExpr {
11335 block,
11336 list: Box::new(list),
11337 progress: None,
11338 },
11339 line,
11340 })
11341 } else {
11342 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11343 Ok(Expr {
11344 kind: ExprKind::PForExpr {
11345 block,
11346 list: Box::new(list),
11347 progress: progress.map(Box::new),
11348 },
11349 line,
11350 })
11351 }
11352 }
11353 "par" => {
11359 let (block, list, _progress) = self.parse_block_then_list_optional_progress()?;
11360 Ok(Expr {
11361 kind: ExprKind::ParExpr {
11362 block,
11363 list: Box::new(list),
11364 },
11365 line,
11366 })
11367 }
11368 "par_lines" | "par_walk" => {
11369 let args = self.parse_builtin_args()?;
11370 if args.len() < 2 {
11371 return Err(
11372 self.syntax_err(format!("{} requires at least two arguments", name), line)
11373 );
11374 }
11375
11376 if name == "par_lines" {
11377 Ok(Expr {
11378 kind: ExprKind::ParLinesExpr {
11379 path: Box::new(args[0].clone()),
11380 callback: Box::new(args[1].clone()),
11381 progress: None,
11382 },
11383 line,
11384 })
11385 } else {
11386 Ok(Expr {
11387 kind: ExprKind::ParWalkExpr {
11388 path: Box::new(args[0].clone()),
11389 callback: Box::new(args[1].clone()),
11390 progress: None,
11391 },
11392 line,
11393 })
11394 }
11395 }
11396 "pwatch" | "watch" => {
11397 let args = self.parse_builtin_args()?;
11398 if args.len() < 2 {
11399 return Err(
11400 self.syntax_err(format!("{} requires at least two arguments", name), line)
11401 );
11402 }
11403 Ok(Expr {
11404 kind: ExprKind::PwatchExpr {
11405 path: Box::new(args[0].clone()),
11406 callback: Box::new(args[1].clone()),
11407 },
11408 line,
11409 })
11410 }
11411 "fan" => {
11412 let (count, block) = self.parse_fan_count_and_block(line)?;
11418 let progress = self.parse_fan_optional_progress("fan")?;
11419 Ok(Expr {
11420 kind: ExprKind::FanExpr {
11421 count,
11422 block,
11423 progress,
11424 capture: false,
11425 },
11426 line,
11427 })
11428 }
11429 "fan_cap" => {
11430 let (count, block) = self.parse_fan_count_and_block(line)?;
11431 let progress = self.parse_fan_optional_progress("fan_cap")?;
11432 Ok(Expr {
11433 kind: ExprKind::FanExpr {
11434 count,
11435 block,
11436 progress,
11437 capture: true,
11438 },
11439 line,
11440 })
11441 }
11442 "async" => {
11443 if !matches!(self.peek(), Token::LBrace) {
11444 return Err(self.syntax_err("async must be followed by { BLOCK }", line));
11445 }
11446 let block = self.parse_block()?;
11447 Ok(Expr {
11448 kind: ExprKind::AsyncBlock { body: block },
11449 line,
11450 })
11451 }
11452 "spawn" => {
11453 if !matches!(self.peek(), Token::LBrace) {
11454 return Err(self.syntax_err("spawn must be followed by { BLOCK }", line));
11455 }
11456 let block = self.parse_block()?;
11457 Ok(Expr {
11458 kind: ExprKind::SpawnBlock { body: block },
11459 line,
11460 })
11461 }
11462 "trace" => {
11463 if !matches!(self.peek(), Token::LBrace) {
11464 return Err(self.syntax_err("trace must be followed by { BLOCK }", line));
11465 }
11466 let block = self.parse_block()?;
11467 Ok(Expr {
11468 kind: ExprKind::Trace { body: block },
11469 line,
11470 })
11471 }
11472 "timer" => {
11473 let block = self.parse_block_or_bareword_block_no_args()?;
11474 Ok(Expr {
11475 kind: ExprKind::Timer { body: block },
11476 line,
11477 })
11478 }
11479 "bench" => {
11480 let block = self.parse_block_or_bareword_block_no_args()?;
11481 let times = Box::new(self.parse_expression()?);
11482 Ok(Expr {
11483 kind: ExprKind::Bench { body: block, times },
11484 line,
11485 })
11486 }
11487 "spinner" => {
11488 let (message, body) = if matches!(self.peek(), Token::LBrace) {
11490 let body = self.parse_block()?;
11491 (
11492 Box::new(Expr {
11493 kind: ExprKind::String("working".to_string()),
11494 line,
11495 }),
11496 body,
11497 )
11498 } else {
11499 let msg = self.parse_assign_expr()?;
11500 let body = self.parse_block()?;
11501 (Box::new(msg), body)
11502 };
11503 Ok(Expr {
11504 kind: ExprKind::Spinner { message, body },
11505 line,
11506 })
11507 }
11508 "thread" | "t" => {
11509 self.parse_thread_macro(line, false)
11519 }
11520 "retry" => {
11521 let body = if matches!(self.peek(), Token::LBrace) {
11524 self.parse_block()?
11525 } else {
11526 let bw_line = self.peek_line();
11527 let Token::Ident(ref name) = self.peek().clone() else {
11528 return Err(self
11529 .syntax_err("retry: expected block or bareword function name", line));
11530 };
11531 let name = name.clone();
11532 self.advance();
11533 vec![Statement::new(
11534 StmtKind::Expression(Expr {
11535 kind: ExprKind::FuncCall { name, args: vec![] },
11536 line: bw_line,
11537 }),
11538 bw_line,
11539 )]
11540 };
11541 self.eat(&Token::Comma);
11542 match self.peek() {
11543 Token::Ident(ref s) if s == "times" => {
11544 self.advance();
11545 }
11546 _ => {
11547 return Err(self.syntax_err("retry: expected `times =>` after block", line));
11548 }
11549 }
11550 self.expect(&Token::FatArrow)?;
11551 let times = Box::new(self.parse_assign_expr()?);
11552 let mut backoff = RetryBackoff::None;
11553 if self.eat(&Token::Comma) {
11554 match self.peek() {
11555 Token::Ident(ref s) if s == "backoff" => {
11556 self.advance();
11557 }
11558 _ => {
11559 return Err(
11560 self.syntax_err("retry: expected `backoff =>` after comma", line)
11561 );
11562 }
11563 }
11564 self.expect(&Token::FatArrow)?;
11565 let Token::Ident(mode) = self.peek().clone() else {
11566 return Err(self.syntax_err(
11567 "retry: expected backoff mode (none, linear, exponential)",
11568 line,
11569 ));
11570 };
11571 backoff = match mode.as_str() {
11572 "none" => RetryBackoff::None,
11573 "linear" => RetryBackoff::Linear,
11574 "exponential" => RetryBackoff::Exponential,
11575 _ => {
11576 return Err(
11577 self.syntax_err(format!("retry: invalid backoff `{mode}`"), line)
11578 );
11579 }
11580 };
11581 self.advance();
11582 }
11583 Ok(Expr {
11584 kind: ExprKind::RetryBlock {
11585 body,
11586 times,
11587 backoff,
11588 },
11589 line,
11590 })
11591 }
11592 "rate_limit" => {
11593 self.expect(&Token::LParen)?;
11594 let max = Box::new(self.parse_assign_expr()?);
11595 self.expect(&Token::Comma)?;
11596 let window = Box::new(self.parse_assign_expr()?);
11597 self.expect(&Token::RParen)?;
11598 let body = self.parse_block_or_bareword_block_no_args()?;
11599 let slot = self.alloc_rate_limit_slot();
11600 Ok(Expr {
11601 kind: ExprKind::RateLimitBlock {
11602 slot,
11603 max,
11604 window,
11605 body,
11606 },
11607 line,
11608 })
11609 }
11610 "every" => {
11611 let has_paren = self.eat(&Token::LParen);
11614 let interval = Box::new(self.parse_assign_expr()?);
11615 if has_paren {
11616 self.expect(&Token::RParen)?;
11617 }
11618 let body = if matches!(self.peek(), Token::LBrace) {
11619 self.parse_block()?
11620 } else {
11621 let bline = self.peek_line();
11622 let expr = self.parse_assign_expr()?;
11623 vec![Statement::new(StmtKind::Expression(expr), bline)]
11624 };
11625 Ok(Expr {
11626 kind: ExprKind::EveryBlock { interval, body },
11627 line,
11628 })
11629 }
11630 "gen" => {
11631 if !matches!(self.peek(), Token::LBrace) {
11632 return Err(self.syntax_err("gen must be followed by { BLOCK }", line));
11633 }
11634 let body = self.parse_block()?;
11635 Ok(Expr {
11636 kind: ExprKind::GenBlock { body },
11637 line,
11638 })
11639 }
11640 "yield" => {
11641 let e = self.parse_assign_expr()?;
11642 Ok(Expr {
11643 kind: ExprKind::Yield(Box::new(e)),
11644 line,
11645 })
11646 }
11647 "await" => {
11648 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11649 return Ok(e);
11650 }
11651 let a = self.parse_one_arg_or_default()?;
11654 Ok(Expr {
11655 kind: ExprKind::Await(Box::new(a)),
11656 line,
11657 })
11658 }
11659 "slurp" | "cat" | "c" => {
11660 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11661 return Ok(e);
11662 }
11663 let a = self.parse_one_arg_or_default()?;
11664 Ok(Expr {
11665 kind: ExprKind::Slurp(Box::new(a)),
11666 line,
11667 })
11668 }
11669 "swallow" | "swa" => {
11670 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11671 return Ok(e);
11672 }
11673 let a = self.parse_one_arg_or_default()?;
11674 Ok(Expr {
11675 kind: ExprKind::Swallow(Box::new(a)),
11676 line,
11677 })
11678 }
11679 "ingest" | "ing" => {
11680 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11681 return Ok(e);
11682 }
11683 let a = self.parse_one_arg_or_default()?;
11684 Ok(Expr {
11685 kind: ExprKind::Ingest(Box::new(a)),
11686 line,
11687 })
11688 }
11689 "burp" => {
11690 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11691 return Ok(e);
11692 }
11693 let a = self.parse_one_arg_or_default()?;
11694 Ok(Expr {
11695 kind: ExprKind::Burp(Box::new(a)),
11696 line,
11697 })
11698 }
11699 "god" => {
11700 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11701 return Ok(e);
11702 }
11703 let a = self.parse_one_arg_or_default()?;
11704 Ok(Expr {
11705 kind: ExprKind::God(Box::new(a)),
11706 line,
11707 })
11708 }
11709 "capture" => {
11710 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11711 return Ok(e);
11712 }
11713 let a = self.parse_one_arg()?;
11714 Ok(Expr {
11715 kind: ExprKind::Capture(Box::new(a)),
11716 line,
11717 })
11718 }
11719 "fetch_url" => {
11720 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
11721 return Ok(e);
11722 }
11723 let a = self.parse_one_arg()?;
11724 Ok(Expr {
11725 kind: ExprKind::FetchUrl(Box::new(a)),
11726 line,
11727 })
11728 }
11729 "pchannel" => {
11730 let capacity = if self.eat(&Token::LParen) {
11731 if matches!(self.peek(), Token::RParen) {
11732 self.advance();
11733 None
11734 } else {
11735 let e = self.parse_expression()?;
11736 self.expect(&Token::RParen)?;
11737 Some(Box::new(e))
11738 }
11739 } else {
11740 None
11741 };
11742 Ok(Expr {
11743 kind: ExprKind::Pchannel { capacity },
11744 line,
11745 })
11746 }
11747 "psort" => {
11748 if matches!(self.peek(), Token::LBrace)
11749 || matches!(self.peek(), Token::ScalarVar(ref v) if v == "a" || v == "b")
11750 || matches!(self.peek(), Token::Ident(ref name) if !Self::is_known_bareword(name))
11751 {
11752 let block = self.parse_block_or_bareword_cmp_block()?;
11753 let block_end_line = self.prev_line();
11761 self.eat(&Token::Comma);
11762 let use_placeholder = self.in_pipe_rhs()
11763 && (matches!(
11764 self.peek(),
11765 Token::Semicolon
11766 | Token::RBrace
11767 | Token::RParen
11768 | Token::Eof
11769 | Token::PipeForward
11770 ) || self.peek_line() > block_end_line);
11771 let (list, progress) = if use_placeholder {
11772 (self.pipe_placeholder_list(line), None)
11773 } else {
11774 self.parse_assign_expr_list_optional_progress()?
11775 };
11776 Ok(Expr {
11777 kind: ExprKind::PSortExpr {
11778 cmp: Some(block),
11779 list: Box::new(list),
11780 progress: progress.map(Box::new),
11781 },
11782 line,
11783 })
11784 } else {
11785 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11786 Ok(Expr {
11787 kind: ExprKind::PSortExpr {
11788 cmp: None,
11789 list: Box::new(list),
11790 progress: progress.map(Box::new),
11791 },
11792 line,
11793 })
11794 }
11795 }
11796 "preduce" => {
11797 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11798 Ok(Expr {
11799 kind: ExprKind::PReduceExpr {
11800 block,
11801 list: Box::new(list),
11802 progress: progress.map(Box::new),
11803 },
11804 line,
11805 })
11806 }
11807 "preduce_init" => {
11808 let (init, block, list, progress) =
11809 self.parse_init_block_then_list_optional_progress()?;
11810 Ok(Expr {
11811 kind: ExprKind::PReduceInitExpr {
11812 init: Box::new(init),
11813 block,
11814 list: Box::new(list),
11815 progress: progress.map(Box::new),
11816 },
11817 line,
11818 })
11819 }
11820 "pmap_reduce" => {
11821 let map_block = self.parse_block_or_bareword_block()?;
11822 let reduce_block = if matches!(self.peek(), Token::LBrace) {
11825 self.parse_block()?
11826 } else {
11827 self.expect(&Token::Comma)?;
11829 self.parse_block_or_bareword_cmp_block()?
11830 };
11831 self.eat(&Token::Comma);
11832 let line = self.peek_line();
11833 if let Token::Ident(ref kw) = self.peek().clone() {
11834 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11835 self.advance();
11836 self.expect(&Token::FatArrow)?;
11837 let prog = self.parse_assign_expr()?;
11838 return Ok(Expr {
11839 kind: ExprKind::PMapReduceExpr {
11840 map_block,
11841 reduce_block,
11842 list: Box::new(Expr {
11843 kind: ExprKind::List(vec![]),
11844 line,
11845 }),
11846 progress: Some(Box::new(prog)),
11847 },
11848 line,
11849 });
11850 }
11851 }
11852 if self.pipe_supplies_slurped_list_operand()
11860 || matches!(
11861 self.peek(),
11862 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11863 )
11864 {
11865 return Ok(Expr {
11866 kind: ExprKind::PMapReduceExpr {
11867 map_block,
11868 reduce_block,
11869 list: Box::new(Expr {
11870 kind: ExprKind::List(vec![]),
11871 line,
11872 }),
11873 progress: None,
11874 },
11875 line,
11876 });
11877 }
11878 let mut parts = vec![self.parse_assign_expr()?];
11879 loop {
11880 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
11881 break;
11882 }
11883 if matches!(
11884 self.peek(),
11885 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
11886 ) {
11887 break;
11888 }
11889 if let Token::Ident(ref kw) = self.peek().clone() {
11890 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
11891 self.advance();
11892 self.expect(&Token::FatArrow)?;
11893 let prog = self.parse_assign_expr()?;
11894 return Ok(Expr {
11895 kind: ExprKind::PMapReduceExpr {
11896 map_block,
11897 reduce_block,
11898 list: Box::new(merge_expr_list(parts)),
11899 progress: Some(Box::new(prog)),
11900 },
11901 line,
11902 });
11903 }
11904 }
11905 parts.push(self.parse_assign_expr()?);
11906 }
11907 Ok(Expr {
11908 kind: ExprKind::PMapReduceExpr {
11909 map_block,
11910 reduce_block,
11911 list: Box::new(merge_expr_list(parts)),
11912 progress: None,
11913 },
11914 line,
11915 })
11916 }
11917 "puniq" => {
11918 if self.pipe_supplies_slurped_list_operand() {
11919 return Ok(Expr {
11920 kind: ExprKind::FuncCall {
11921 name: "puniq".to_string(),
11922 args: vec![],
11923 },
11924 line,
11925 });
11926 }
11927 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11928 let mut args = vec![list];
11929 if let Some(p) = progress {
11930 args.push(p);
11931 }
11932 Ok(Expr {
11933 kind: ExprKind::FuncCall {
11934 name: "puniq".to_string(),
11935 args,
11936 },
11937 line,
11938 })
11939 }
11940 "pfirst" => {
11941 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11942 let cr = Expr {
11943 kind: ExprKind::CodeRef {
11944 params: vec![],
11945 body: block,
11946 },
11947 line,
11948 };
11949 let mut args = vec![cr, list];
11950 if let Some(p) = progress {
11951 args.push(p);
11952 }
11953 Ok(Expr {
11954 kind: ExprKind::FuncCall {
11955 name: "pfirst".to_string(),
11956 args,
11957 },
11958 line,
11959 })
11960 }
11961 "pany" => {
11962 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
11963 let cr = Expr {
11964 kind: ExprKind::CodeRef {
11965 params: vec![],
11966 body: block,
11967 },
11968 line,
11969 };
11970 let mut args = vec![cr, list];
11971 if let Some(p) = progress {
11972 args.push(p);
11973 }
11974 Ok(Expr {
11975 kind: ExprKind::FuncCall {
11976 name: "pany".to_string(),
11977 args,
11978 },
11979 line,
11980 })
11981 }
11982 "uniq" | "distinct" => {
11983 if self.pipe_supplies_slurped_list_operand() {
11984 return Ok(Expr {
11985 kind: ExprKind::FuncCall {
11986 name: name.clone(),
11987 args: vec![],
11988 },
11989 line,
11990 });
11991 }
11992 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
11993 if progress.is_some() {
11994 return Err(self.syntax_err(
11995 "`progress =>` is not supported for uniq (use puniq for parallel + progress)",
11996 line,
11997 ));
11998 }
11999 Ok(Expr {
12000 kind: ExprKind::FuncCall {
12001 name: name.clone(),
12002 args: vec![list],
12003 },
12004 line,
12005 })
12006 }
12007 "flatten" => {
12008 if self.pipe_supplies_slurped_list_operand() {
12009 return Ok(Expr {
12010 kind: ExprKind::FuncCall {
12011 name: "flatten".to_string(),
12012 args: vec![],
12013 },
12014 line,
12015 });
12016 }
12017 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12018 if progress.is_some() {
12019 return Err(self.syntax_err("`progress =>` is not supported for flatten", line));
12020 }
12021 Ok(Expr {
12022 kind: ExprKind::FuncCall {
12023 name: "flatten".to_string(),
12024 args: vec![list],
12025 },
12026 line,
12027 })
12028 }
12029 "set" => {
12030 if self.pipe_supplies_slurped_list_operand() {
12031 return Ok(Expr {
12032 kind: ExprKind::FuncCall {
12033 name: "set".to_string(),
12034 args: vec![],
12035 },
12036 line,
12037 });
12038 }
12039 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12040 if progress.is_some() {
12041 return Err(self.syntax_err("`progress =>` is not supported for set", line));
12042 }
12043 Ok(Expr {
12044 kind: ExprKind::FuncCall {
12045 name: "set".to_string(),
12046 args: vec![list],
12047 },
12048 line,
12049 })
12050 }
12051 "size" => {
12055 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12056 return Ok(e);
12057 }
12058 if self.pipe_supplies_slurped_list_operand() {
12059 return Ok(Expr {
12060 kind: ExprKind::FuncCall {
12061 name: "size".to_string(),
12062 args: vec![],
12063 },
12064 line,
12065 });
12066 }
12067 let a = self.parse_one_arg_or_default()?;
12068 Ok(Expr {
12069 kind: ExprKind::FuncCall {
12070 name: "size".to_string(),
12071 args: vec![a],
12072 },
12073 line,
12074 })
12075 }
12076 "list_count" | "list_size" | "count" | "len" | "cnt" => {
12077 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12078 return Ok(e);
12079 }
12080 if self.pipe_supplies_slurped_list_operand() {
12081 return Ok(Expr {
12082 kind: ExprKind::FuncCall {
12083 name: name.clone(),
12084 args: vec![],
12085 },
12086 line,
12087 });
12088 }
12089 let args = if matches!(self.peek(), Token::LParen) {
12103 self.advance();
12104 if matches!(self.peek(), Token::RParen) {
12105 self.advance();
12106 Vec::new()
12107 } else {
12108 let inner = self.parse_expression()?;
12109 self.expect(&Token::RParen)?;
12110 vec![inner]
12111 }
12112 } else if self.peek_is_named_unary_terminator() {
12113 Vec::new()
12114 } else {
12115 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12116 if progress.is_some() {
12117 return Err(self.syntax_err(
12118 "`progress =>` is not supported for list_count / list_size / count / cnt",
12119 line,
12120 ));
12121 }
12122 vec![list]
12123 };
12124 Ok(Expr {
12125 kind: ExprKind::FuncCall {
12126 name: name.clone(),
12127 args,
12128 },
12129 line,
12130 })
12131 }
12132 "shuffle" | "shuffled" => {
12133 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12134 return Ok(e);
12135 }
12136 if self.pipe_supplies_slurped_list_operand() {
12137 return Ok(Expr {
12138 kind: ExprKind::FuncCall {
12139 name: "shuffle".to_string(),
12140 args: vec![],
12141 },
12142 line,
12143 });
12144 }
12145 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12146 if progress.is_some() {
12147 return Err(self.syntax_err("`progress =>` is not supported for shuffle", line));
12148 }
12149 Ok(Expr {
12150 kind: ExprKind::FuncCall {
12151 name: "shuffle".to_string(),
12152 args: vec![list],
12153 },
12154 line,
12155 })
12156 }
12157 "chunked" => {
12158 let mut parts = Vec::new();
12159 if self.eat(&Token::LParen) {
12160 if !matches!(self.peek(), Token::RParen) {
12161 parts.push(self.parse_assign_expr()?);
12162 while self.eat(&Token::Comma) {
12163 if matches!(self.peek(), Token::RParen) {
12164 break;
12165 }
12166 parts.push(self.parse_assign_expr()?);
12167 }
12168 }
12169 self.expect(&Token::RParen)?;
12170 } else {
12171 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12175 loop {
12176 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12177 break;
12178 }
12179 if matches!(
12180 self.peek(),
12181 Token::Semicolon
12182 | Token::RBrace
12183 | Token::RParen
12184 | Token::Eof
12185 | Token::PipeForward
12186 ) {
12187 break;
12188 }
12189 if self.peek_is_postfix_stmt_modifier_keyword() {
12190 break;
12191 }
12192 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12193 }
12194 }
12195 if parts.len() == 1 {
12196 let n = parts.pop().unwrap();
12197 return Ok(Expr {
12198 kind: ExprKind::FuncCall {
12199 name: "chunked".to_string(),
12200 args: vec![n],
12201 },
12202 line,
12203 });
12204 }
12205 if parts.is_empty() {
12206 return Ok(Expr {
12207 kind: ExprKind::FuncCall {
12208 name: "chunked".to_string(),
12209 args: parts,
12210 },
12211 line,
12212 });
12213 }
12214 if parts.len() == 2 {
12215 let n = parts.pop().unwrap();
12216 let list = parts.pop().unwrap();
12217 return Ok(Expr {
12218 kind: ExprKind::FuncCall {
12219 name: "chunked".to_string(),
12220 args: vec![list, n],
12221 },
12222 line,
12223 });
12224 }
12225 Err(self.syntax_err(
12226 "chunked: use LIST |> chunked(N) or chunked((1,2,3), 2)",
12227 line,
12228 ))
12229 }
12230 "windowed" => {
12231 let mut parts = Vec::new();
12232 if self.eat(&Token::LParen) {
12233 if !matches!(self.peek(), Token::RParen) {
12234 parts.push(self.parse_assign_expr()?);
12235 while self.eat(&Token::Comma) {
12236 if matches!(self.peek(), Token::RParen) {
12237 break;
12238 }
12239 parts.push(self.parse_assign_expr()?);
12240 }
12241 }
12242 self.expect(&Token::RParen)?;
12243 } else {
12244 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12247 loop {
12248 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
12249 break;
12250 }
12251 if matches!(
12252 self.peek(),
12253 Token::Semicolon
12254 | Token::RBrace
12255 | Token::RParen
12256 | Token::Eof
12257 | Token::PipeForward
12258 ) {
12259 break;
12260 }
12261 if self.peek_is_postfix_stmt_modifier_keyword() {
12262 break;
12263 }
12264 parts.push(self.parse_assign_expr_stop_at_pipe()?);
12265 }
12266 }
12267 if parts.len() == 1 {
12268 let n = parts.pop().unwrap();
12269 return Ok(Expr {
12270 kind: ExprKind::FuncCall {
12271 name: "windowed".to_string(),
12272 args: vec![n],
12273 },
12274 line,
12275 });
12276 }
12277 if parts.is_empty() {
12278 return Ok(Expr {
12279 kind: ExprKind::FuncCall {
12280 name: "windowed".to_string(),
12281 args: parts,
12282 },
12283 line,
12284 });
12285 }
12286 if parts.len() == 2 {
12287 let n = parts.pop().unwrap();
12288 let list = parts.pop().unwrap();
12289 return Ok(Expr {
12290 kind: ExprKind::FuncCall {
12291 name: "windowed".to_string(),
12292 args: vec![list, n],
12293 },
12294 line,
12295 });
12296 }
12297 Err(self.syntax_err(
12298 "windowed: use LIST |> windowed(N) or windowed((1,2,3), 2)",
12299 line,
12300 ))
12301 }
12302 "any" | "all" | "none" => {
12303 if matches!(self.peek(), Token::LParen) {
12305 self.advance();
12306 let args = self.parse_arg_list()?;
12307 self.expect(&Token::RParen)?;
12308 return Ok(Expr {
12309 kind: ExprKind::FuncCall {
12310 name: name.clone(),
12311 args,
12312 },
12313 line,
12314 });
12315 }
12316 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12320 return Ok(Expr {
12321 kind: ExprKind::FuncCall {
12322 name: name.clone(),
12323 args,
12324 },
12325 line,
12326 });
12327 }
12328 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12330 if progress.is_some() {
12331 return Err(self.syntax_err(
12332 "`progress =>` is not supported for any/all/none (use pany for parallel + progress)",
12333 line,
12334 ));
12335 }
12336 let cr = Expr {
12337 kind: ExprKind::CodeRef {
12338 params: vec![],
12339 body: block,
12340 },
12341 line,
12342 };
12343 Ok(Expr {
12344 kind: ExprKind::FuncCall {
12345 name: name.clone(),
12346 args: vec![cr, list],
12347 },
12348 line,
12349 })
12350 }
12351 "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index" => {
12353 let canonical =
12354 if matches!(name.as_str(), "find_index" | "firstidx" | "first_index") {
12355 "find_index"
12356 } else {
12357 "first"
12358 };
12359 if matches!(self.peek(), Token::LParen) {
12361 self.advance();
12362 let args = self.parse_arg_list()?;
12363 self.expect(&Token::RParen)?;
12364 return Ok(Expr {
12365 kind: ExprKind::FuncCall {
12366 name: canonical.to_string(),
12367 args,
12368 },
12369 line,
12370 });
12371 }
12372 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12374 return Ok(Expr {
12375 kind: ExprKind::FuncCall {
12376 name: canonical.to_string(),
12377 args,
12378 },
12379 line,
12380 });
12381 }
12382 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12384 if progress.is_some() {
12385 return Err(self.syntax_err(
12386 "`progress =>` is not supported for first/detect/find/find_index (use pfirst for parallel + progress)",
12387 line,
12388 ));
12389 }
12390 let cr = Expr {
12391 kind: ExprKind::CodeRef {
12392 params: vec![],
12393 body: block,
12394 },
12395 line,
12396 };
12397 Ok(Expr {
12398 kind: ExprKind::FuncCall {
12399 name: canonical.to_string(),
12400 args: vec![cr, list],
12401 },
12402 line,
12403 })
12404 }
12405 "take_while" | "drop_while" | "skip_while" | "reject" | "grepv" | "tap" | "peek"
12406 | "partition" | "min_by" | "max_by" | "zip_with" | "count_by" => {
12407 if let Some(args) = self.try_parse_coderef_listop_args(line)? {
12409 return Ok(Expr {
12410 kind: ExprKind::FuncCall {
12411 name: name.to_string(),
12412 args,
12413 },
12414 line,
12415 });
12416 }
12417 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12418 if progress.is_some() {
12419 return Err(
12420 self.syntax_err(format!("`progress =>` is not supported for {name}"), line)
12421 );
12422 }
12423 let cr = Expr {
12424 kind: ExprKind::CodeRef {
12425 params: vec![],
12426 body: block,
12427 },
12428 line,
12429 };
12430 Ok(Expr {
12431 kind: ExprKind::FuncCall {
12432 name: name.to_string(),
12433 args: vec![cr, list],
12434 },
12435 line,
12436 })
12437 }
12438 "group_by" | "chunk_by" => {
12439 if matches!(self.peek(), Token::LBrace) {
12440 let (block, list) = self.parse_block_list()?;
12441 let cr = Expr {
12442 kind: ExprKind::CodeRef {
12443 params: vec![],
12444 body: block,
12445 },
12446 line,
12447 };
12448 Ok(Expr {
12449 kind: ExprKind::FuncCall {
12450 name: name.to_string(),
12451 args: vec![cr, list],
12452 },
12453 line,
12454 })
12455 } else {
12456 let key_expr = self.parse_assign_expr()?;
12457 self.expect(&Token::Comma)?;
12458 let list_parts = self.parse_list_until_terminator()?;
12459 let list_expr = if list_parts.len() == 1 {
12460 list_parts.into_iter().next().unwrap()
12461 } else {
12462 Expr {
12463 kind: ExprKind::List(list_parts),
12464 line,
12465 }
12466 };
12467 Ok(Expr {
12468 kind: ExprKind::FuncCall {
12469 name: name.to_string(),
12470 args: vec![key_expr, list_expr],
12471 },
12472 line,
12473 })
12474 }
12475 }
12476 "with_index" => {
12477 if self.pipe_supplies_slurped_list_operand() {
12478 return Ok(Expr {
12479 kind: ExprKind::FuncCall {
12480 name: "with_index".to_string(),
12481 args: vec![],
12482 },
12483 line,
12484 });
12485 }
12486 let (list, progress) = self.parse_assign_expr_list_optional_progress()?;
12487 if progress.is_some() {
12488 return Err(
12489 self.syntax_err("`progress =>` is not supported for with_index", line)
12490 );
12491 }
12492 Ok(Expr {
12493 kind: ExprKind::FuncCall {
12494 name: "with_index".to_string(),
12495 args: vec![list],
12496 },
12497 line,
12498 })
12499 }
12500 "pcache" => {
12501 let (block, list, progress) = self.parse_block_then_list_optional_progress()?;
12502 Ok(Expr {
12503 kind: ExprKind::PcacheExpr {
12504 block,
12505 list: Box::new(list),
12506 progress: progress.map(Box::new),
12507 },
12508 line,
12509 })
12510 }
12511 "pselect" => {
12512 let paren = self.eat(&Token::LParen);
12513 let (receivers, timeout) = self.parse_comma_expr_list_with_timeout_tail(paren)?;
12514 if paren {
12515 self.expect(&Token::RParen)?;
12516 }
12517 if receivers.is_empty() {
12518 return Err(self.syntax_err("pselect needs at least one receiver", line));
12519 }
12520 Ok(Expr {
12521 kind: ExprKind::PselectExpr {
12522 receivers,
12523 timeout: timeout.map(Box::new),
12524 },
12525 line,
12526 })
12527 }
12528 "open" => {
12529 let paren = matches!(self.peek(), Token::LParen);
12530 if paren {
12531 self.advance();
12532 }
12533 if matches!(self.peek(), Token::Ident(ref s) if s == "my") {
12534 self.advance();
12535 let name = self.parse_scalar_var_name()?;
12536 self.expect(&Token::Comma)?;
12537 let mode = self.parse_assign_expr()?;
12538 let file = if self.eat(&Token::Comma) {
12539 Some(self.parse_assign_expr()?)
12540 } else {
12541 None
12542 };
12543 if paren {
12544 self.expect(&Token::RParen)?;
12545 }
12546 Ok(Expr {
12547 kind: ExprKind::Open {
12548 handle: Box::new(Expr {
12549 kind: ExprKind::OpenMyHandle { name },
12550 line,
12551 }),
12552 mode: Box::new(mode),
12553 file: file.map(Box::new),
12554 },
12555 line,
12556 })
12557 } else {
12558 let handle_lit = self.take_bareword_filehandle();
12566 if handle_lit.is_some() {
12567 self.expect(&Token::Comma)?;
12570 }
12571 let args = if paren {
12572 self.parse_arg_list()?
12573 } else {
12574 self.parse_list_until_terminator()?
12575 };
12576 if paren {
12577 self.expect(&Token::RParen)?;
12578 }
12579 let total = handle_lit.is_some() as usize + args.len();
12580 if total < 2 {
12581 return Err(self.syntax_err("open requires at least 2 arguments", line));
12582 }
12583 let (handle_expr, mode_expr, file_expr) = match handle_lit {
12584 Some(name) => {
12585 let h = Expr {
12586 kind: ExprKind::String(name),
12587 line,
12588 };
12589 (h, args[0].clone(), args.get(1).cloned())
12590 }
12591 None => (args[0].clone(), args[1].clone(), args.get(2).cloned()),
12592 };
12593 Ok(Expr {
12594 kind: ExprKind::Open {
12595 handle: Box::new(handle_expr),
12596 mode: Box::new(mode_expr),
12597 file: file_expr.map(Box::new),
12598 },
12599 line,
12600 })
12601 }
12602 }
12603 "close" => {
12604 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12605 return Ok(e);
12606 }
12607 let a = self
12609 .take_bareword_filehandle_arg(line)
12610 .map(Ok)
12611 .unwrap_or_else(|| self.parse_one_arg_or_default())?;
12612 Ok(Expr {
12613 kind: ExprKind::Close(Box::new(a)),
12614 line,
12615 })
12616 }
12617 "opendir" => {
12618 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12619 return Ok(e);
12620 }
12621 let args = self.parse_builtin_args()?;
12622 if args.len() != 2 {
12623 return Err(self.syntax_err("opendir requires two arguments", line));
12624 }
12625 Ok(Expr {
12626 kind: ExprKind::Opendir {
12627 handle: Box::new(args[0].clone()),
12628 path: Box::new(args[1].clone()),
12629 },
12630 line,
12631 })
12632 }
12633 "readdir" => {
12634 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12635 return Ok(e);
12636 }
12637 let a = self.parse_one_arg()?;
12638 Ok(Expr {
12639 kind: ExprKind::Readdir(Box::new(a)),
12640 line,
12641 })
12642 }
12643 "closedir" => {
12644 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12645 return Ok(e);
12646 }
12647 let a = self.parse_one_arg()?;
12648 Ok(Expr {
12649 kind: ExprKind::Closedir(Box::new(a)),
12650 line,
12651 })
12652 }
12653 "rewinddir" => {
12654 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12655 return Ok(e);
12656 }
12657 let a = self.parse_one_arg()?;
12658 Ok(Expr {
12659 kind: ExprKind::Rewinddir(Box::new(a)),
12660 line,
12661 })
12662 }
12663 "telldir" => {
12664 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12665 return Ok(e);
12666 }
12667 let a = self.parse_one_arg()?;
12668 Ok(Expr {
12669 kind: ExprKind::Telldir(Box::new(a)),
12670 line,
12671 })
12672 }
12673 "seekdir" => {
12674 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12675 return Ok(e);
12676 }
12677 let args = self.parse_builtin_args()?;
12678 if args.len() != 2 {
12679 return Err(self.syntax_err("seekdir requires two arguments", line));
12680 }
12681 Ok(Expr {
12682 kind: ExprKind::Seekdir {
12683 handle: Box::new(args[0].clone()),
12684 position: Box::new(args[1].clone()),
12685 },
12686 line,
12687 })
12688 }
12689 "eof" => {
12690 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12691 return Ok(e);
12692 }
12693 if let Some(a) = self.take_bareword_filehandle_arg(line) {
12697 return Ok(Expr {
12698 kind: ExprKind::Eof(Some(Box::new(a))),
12699 line,
12700 });
12701 }
12702 if matches!(self.peek(), Token::LParen) {
12703 self.advance();
12704 if matches!(self.peek(), Token::RParen) {
12705 self.advance();
12706 Ok(Expr {
12707 kind: ExprKind::Eof(None),
12708 line,
12709 })
12710 } else {
12711 let a = self
12713 .take_bareword_filehandle_arg(line)
12714 .map(Ok)
12715 .unwrap_or_else(|| self.parse_expression())?;
12716 self.expect(&Token::RParen)?;
12717 Ok(Expr {
12718 kind: ExprKind::Eof(Some(Box::new(a))),
12719 line,
12720 })
12721 }
12722 } else {
12723 Ok(Expr {
12724 kind: ExprKind::Eof(None),
12725 line,
12726 })
12727 }
12728 }
12729 "system" => {
12730 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12731 return Ok(e);
12732 }
12733 let args = self.parse_builtin_args()?;
12734 Ok(Expr {
12735 kind: ExprKind::System(args),
12736 line,
12737 })
12738 }
12739 "exec" => {
12740 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12741 return Ok(e);
12742 }
12743 let args = self.parse_builtin_args()?;
12744 Ok(Expr {
12745 kind: ExprKind::Exec(args),
12746 line,
12747 })
12748 }
12749 "eval" => {
12750 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12751 return Ok(e);
12752 }
12753 let a = if matches!(self.peek(), Token::LBrace) {
12754 let block = self.parse_block()?;
12755 Expr {
12756 kind: ExprKind::CodeRef {
12757 params: vec![],
12758 body: block,
12759 },
12760 line,
12761 }
12762 } else {
12763 self.parse_one_arg_or_default()?
12764 };
12765 Ok(Expr {
12766 kind: ExprKind::Eval(Box::new(a)),
12767 line,
12768 })
12769 }
12770 "do" => {
12771 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12772 return Ok(e);
12773 }
12774 let a = self.parse_one_arg()?;
12775 Ok(Expr {
12776 kind: ExprKind::Do(Box::new(a)),
12777 line,
12778 })
12779 }
12780 "require" => {
12781 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12782 return Ok(e);
12783 }
12784 let a = self.parse_one_arg()?;
12785 Ok(Expr {
12786 kind: ExprKind::Require(Box::new(a)),
12787 line,
12788 })
12789 }
12790 "exit" => {
12791 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12792 return Ok(e);
12793 }
12794 if matches!(
12795 self.peek(),
12796 Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
12797 ) {
12798 Ok(Expr {
12799 kind: ExprKind::Exit(None),
12800 line,
12801 })
12802 } else {
12803 let a = self.parse_one_arg()?;
12804 Ok(Expr {
12805 kind: ExprKind::Exit(Some(Box::new(a))),
12806 line,
12807 })
12808 }
12809 }
12810 "chdir" => {
12811 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12812 return Ok(e);
12813 }
12814 let a = self.parse_one_arg_or_default()?;
12815 Ok(Expr {
12816 kind: ExprKind::Chdir(Box::new(a)),
12817 line,
12818 })
12819 }
12820 "mkdir" => {
12821 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12822 return Ok(e);
12823 }
12824 let args = self.parse_builtin_args()?;
12825 Ok(Expr {
12826 kind: ExprKind::Mkdir {
12827 path: Box::new(args[0].clone()),
12828 mode: args.get(1).cloned().map(Box::new),
12829 },
12830 line,
12831 })
12832 }
12833 "unlink" | "rm" => {
12834 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12835 return Ok(e);
12836 }
12837 let args = self.parse_builtin_args()?;
12838 Ok(Expr {
12839 kind: ExprKind::Unlink(args),
12840 line,
12841 })
12842 }
12843 "rename" => {
12844 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12845 return Ok(e);
12846 }
12847 let args = self.parse_builtin_args()?;
12848 if args.len() != 2 {
12849 return Err(self.syntax_err("rename requires two arguments", line));
12850 }
12851 Ok(Expr {
12852 kind: ExprKind::Rename {
12853 old: Box::new(args[0].clone()),
12854 new: Box::new(args[1].clone()),
12855 },
12856 line,
12857 })
12858 }
12859 "chmod" => {
12860 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12861 return Ok(e);
12862 }
12863 let args = self.parse_builtin_args()?;
12864 if args.len() < 2 {
12865 return Err(self.syntax_err("chmod requires mode and at least one file", line));
12866 }
12867 Ok(Expr {
12868 kind: ExprKind::Chmod(args),
12869 line,
12870 })
12871 }
12872 "chown" => {
12873 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12874 return Ok(e);
12875 }
12876 let args = self.parse_builtin_args()?;
12877 if args.len() < 3 {
12878 return Err(
12879 self.syntax_err("chown requires uid, gid, and at least one file", line)
12880 );
12881 }
12882 Ok(Expr {
12883 kind: ExprKind::Chown(args),
12884 line,
12885 })
12886 }
12887 "stat" => {
12888 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12889 return Ok(e);
12890 }
12891 let args = self.parse_builtin_args()?;
12892 let arg = if args.len() == 1 {
12893 args[0].clone()
12894 } else if args.is_empty() {
12895 Expr {
12896 kind: ExprKind::ScalarVar("_".into()),
12897 line,
12898 }
12899 } else {
12900 return Err(self.syntax_err("stat requires zero or one argument", line));
12901 };
12902 Ok(Expr {
12903 kind: ExprKind::Stat(Box::new(arg)),
12904 line,
12905 })
12906 }
12907 "lstat" => {
12908 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12909 return Ok(e);
12910 }
12911 let args = self.parse_builtin_args()?;
12912 let arg = if args.len() == 1 {
12913 args[0].clone()
12914 } else if args.is_empty() {
12915 Expr {
12916 kind: ExprKind::ScalarVar("_".into()),
12917 line,
12918 }
12919 } else {
12920 return Err(self.syntax_err("lstat requires zero or one argument", line));
12921 };
12922 Ok(Expr {
12923 kind: ExprKind::Lstat(Box::new(arg)),
12924 line,
12925 })
12926 }
12927 "link" => {
12928 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12929 return Ok(e);
12930 }
12931 let args = self.parse_builtin_args()?;
12932 if args.len() != 2 {
12933 return Err(self.syntax_err("link requires two arguments", line));
12934 }
12935 Ok(Expr {
12936 kind: ExprKind::Link {
12937 old: Box::new(args[0].clone()),
12938 new: Box::new(args[1].clone()),
12939 },
12940 line,
12941 })
12942 }
12943 "symlink" => {
12944 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12945 return Ok(e);
12946 }
12947 let args = self.parse_builtin_args()?;
12948 if args.len() != 2 {
12949 return Err(self.syntax_err("symlink requires two arguments", line));
12950 }
12951 Ok(Expr {
12952 kind: ExprKind::Symlink {
12953 old: Box::new(args[0].clone()),
12954 new: Box::new(args[1].clone()),
12955 },
12956 line,
12957 })
12958 }
12959 "readlink" => {
12960 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12961 return Ok(e);
12962 }
12963 let args = self.parse_builtin_args()?;
12964 let arg = if args.len() == 1 {
12965 args[0].clone()
12966 } else if args.is_empty() {
12967 Expr {
12968 kind: ExprKind::ScalarVar("_".into()),
12969 line,
12970 }
12971 } else {
12972 return Err(self.syntax_err("readlink requires zero or one argument", line));
12973 };
12974 Ok(Expr {
12975 kind: ExprKind::Readlink(Box::new(arg)),
12976 line,
12977 })
12978 }
12979 "files" => {
12980 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12981 return Ok(e);
12982 }
12983 let args = self.parse_builtin_args()?;
12984 Ok(Expr {
12985 kind: ExprKind::Files(args),
12986 line,
12987 })
12988 }
12989 "filesf" | "f" => {
12990 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
12991 return Ok(e);
12992 }
12993 let args = self.parse_builtin_args()?;
12994 Ok(Expr {
12995 kind: ExprKind::Filesf(args),
12996 line,
12997 })
12998 }
12999 "fr" => {
13000 let args = self.parse_builtin_args()?;
13001 Ok(Expr {
13002 kind: ExprKind::FilesfRecursive(args),
13003 line,
13004 })
13005 }
13006 "dirs" | "d" => {
13007 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13008 return Ok(e);
13009 }
13010 let args = self.parse_builtin_args()?;
13011 Ok(Expr {
13012 kind: ExprKind::Dirs(args),
13013 line,
13014 })
13015 }
13016 "dr" => {
13017 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13018 return Ok(e);
13019 }
13020 let args = self.parse_builtin_args()?;
13021 Ok(Expr {
13022 kind: ExprKind::DirsRecursive(args),
13023 line,
13024 })
13025 }
13026 "sym_links" => {
13027 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13028 return Ok(e);
13029 }
13030 let args = self.parse_builtin_args()?;
13031 Ok(Expr {
13032 kind: ExprKind::SymLinks(args),
13033 line,
13034 })
13035 }
13036 "sockets" => {
13037 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13038 return Ok(e);
13039 }
13040 let args = self.parse_builtin_args()?;
13041 Ok(Expr {
13042 kind: ExprKind::Sockets(args),
13043 line,
13044 })
13045 }
13046 "pipes" => {
13047 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13048 return Ok(e);
13049 }
13050 let args = self.parse_builtin_args()?;
13051 Ok(Expr {
13052 kind: ExprKind::Pipes(args),
13053 line,
13054 })
13055 }
13056 "block_devices" => {
13057 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13058 return Ok(e);
13059 }
13060 let args = self.parse_builtin_args()?;
13061 Ok(Expr {
13062 kind: ExprKind::BlockDevices(args),
13063 line,
13064 })
13065 }
13066 "char_devices" => {
13067 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13068 return Ok(e);
13069 }
13070 let args = self.parse_builtin_args()?;
13071 Ok(Expr {
13072 kind: ExprKind::CharDevices(args),
13073 line,
13074 })
13075 }
13076 "exe" | "executables" => {
13077 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13078 return Ok(e);
13079 }
13080 let args = self.parse_builtin_args()?;
13081 Ok(Expr {
13082 kind: ExprKind::Executables(args),
13083 line,
13084 })
13085 }
13086 "glob" => {
13087 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13088 return Ok(e);
13089 }
13090 let args = self.parse_builtin_args()?;
13091 Ok(Expr {
13092 kind: ExprKind::Glob(args),
13093 line,
13094 })
13095 }
13096 "glob_par" => {
13097 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13098 return Ok(e);
13099 }
13100 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
13101 Ok(Expr {
13102 kind: ExprKind::GlobPar { args, progress },
13103 line,
13104 })
13105 }
13106 "par_sed" => {
13107 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13108 return Ok(e);
13109 }
13110 let (args, progress) = self.parse_glob_par_or_par_sed_args()?;
13111 Ok(Expr {
13112 kind: ExprKind::ParSed { args, progress },
13113 line,
13114 })
13115 }
13116 "bless" => {
13117 if let Some(e) = self.fat_arrow_autoquote(&name, line) {
13118 return Ok(e);
13119 }
13120 let args = self.parse_builtin_args()?;
13121 Ok(Expr {
13122 kind: ExprKind::Bless {
13123 ref_expr: Box::new(args[0].clone()),
13124 class: args.get(1).cloned().map(Box::new),
13125 },
13126 line,
13127 })
13128 }
13129 "caller" => {
13130 if matches!(self.peek(), Token::LParen) {
13131 self.advance();
13132 if matches!(self.peek(), Token::RParen) {
13133 self.advance();
13134 Ok(Expr {
13135 kind: ExprKind::Caller(None),
13136 line,
13137 })
13138 } else {
13139 let a = self.parse_expression()?;
13140 self.expect(&Token::RParen)?;
13141 Ok(Expr {
13142 kind: ExprKind::Caller(Some(Box::new(a))),
13143 line,
13144 })
13145 }
13146 } else {
13147 Ok(Expr {
13148 kind: ExprKind::Caller(None),
13149 line,
13150 })
13151 }
13152 }
13153 "wantarray" => {
13154 if crate::no_interop_mode() {
13155 return Err(self.syntax_err(
13156 "stryke `wantarray` is rejected under --no-interop — \
13157 use explicit return-shape (`@result` vs `$scalar`) \
13158 or pass a flag arg instead of context-sniffing",
13159 line,
13160 ));
13161 }
13162 if matches!(self.peek(), Token::LParen) {
13163 self.advance();
13164 self.expect(&Token::RParen)?;
13165 }
13166 Ok(Expr {
13167 kind: ExprKind::Wantarray,
13168 line,
13169 })
13170 }
13171 "sub" => {
13172 if crate::no_interop_mode() {
13174 return Err(self.syntax_err(
13175 "stryke uses `fn {}` instead of `sub {}` (--no-interop)",
13176 line,
13177 ));
13178 }
13179 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13181 let body = self.parse_block()?;
13182 Ok(Expr {
13183 kind: ExprKind::CodeRef { params, body },
13184 line,
13185 })
13186 }
13187 "fn" => {
13188 let (params, _prototype) = self.parse_sub_sig_or_prototype_opt()?;
13190 self.parse_sub_attributes()?;
13191 let body = self.parse_fn_eq_body_or_block(false)?;
13192 Ok(Expr {
13193 kind: ExprKind::CodeRef { params, body },
13194 line,
13195 })
13196 }
13197 _ => {
13198 if matches!(self.peek(), Token::FatArrow) && !Self::is_underscore_topic_slot(&name)
13203 {
13204 return Ok(Expr {
13205 kind: ExprKind::String(name),
13206 line,
13207 });
13208 }
13209 if Self::is_underscore_topic_slot(&name) {
13225 if matches!(self.peek(), Token::LBracket) && self.peek_line() == line {
13226 self.advance(); let index = self.parse_expression()?;
13228 self.expect(&Token::RBracket)?;
13229 return Ok(Expr {
13230 kind: ExprKind::ArrayElement {
13231 array: format!("__topicstr__{}", name),
13232 index: Box::new(index),
13233 },
13234 line,
13235 });
13236 }
13237 return Ok(Expr {
13238 kind: ExprKind::ScalarVar(name.clone()),
13239 line,
13240 });
13241 }
13242 if matches!(self.peek(), Token::LParen) {
13244 self.advance();
13245 let args = self.parse_arg_list()?;
13246 self.expect(&Token::RParen)?;
13247 Ok(Expr {
13248 kind: ExprKind::FuncCall { name, args },
13249 line,
13250 })
13251 } else if self.peek().is_term_start()
13252 && !(matches!(self.peek(), Token::Ident(ref kw) if kw == "sub")
13253 && matches!(self.peek_at(1), Token::Ident(_)))
13254 && !(self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)))
13255 && !(matches!(self.peek(), Token::LBrace)
13256 && self.peek_line() > self.prev_line())
13257 && !(matches!(self.peek(), Token::BitNot)
13258 && self.suppress_tilde_range == 0
13259 && matches!(
13260 self.peek_at(1),
13261 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13262 ))
13263 {
13264 let args = self.parse_list_until_terminator()?;
13278 Ok(Expr {
13279 kind: ExprKind::FuncCall { name, args },
13280 line,
13281 })
13282 } else {
13283 Ok(Expr {
13289 kind: ExprKind::Bareword(name),
13290 line,
13291 })
13292 }
13293 }
13294 }
13295 }
13296
13297 fn take_bareword_filehandle_if(&mut self, accept: &[Token]) -> Option<String> {
13309 let Token::Ident(h) = self.peek().clone() else {
13310 return None;
13311 };
13312 let saved = self.pos;
13318 let qualified = h == "main"
13319 && matches!(self.peek_at(1), Token::PackageSep)
13320 && matches!(self.peek_at(2), Token::Ident(s) if !s.is_empty());
13321 if qualified {
13322 self.advance(); self.advance(); }
13325 let leaf = match self.peek().clone() {
13326 Token::Ident(s) => s,
13327 _ => {
13328 self.pos = saved;
13329 return None;
13330 }
13331 };
13332 let mut chars = leaf.chars();
13333 let first = match chars.next() {
13334 Some(c) => c,
13335 None => {
13336 self.pos = saved;
13337 return None;
13338 }
13339 };
13340 if !(first.is_ascii_uppercase() || first == '_') {
13341 self.pos = saved;
13342 return None;
13343 }
13344 if !leaf
13345 .chars()
13346 .all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
13347 {
13348 self.pos = saved;
13349 return None;
13350 }
13351 let next = self.peek_at(1);
13352 let ok = if accept.is_empty() {
13353 matches!(
13354 next,
13355 Token::Comma
13356 | Token::Semicolon
13357 | Token::RParen
13358 | Token::RBrace
13359 | Token::Eof
13360 | Token::PipeForward
13361 )
13362 } else {
13363 accept
13364 .iter()
13365 .any(|t| std::mem::discriminant(t) == std::mem::discriminant(next))
13366 };
13367 if !ok {
13368 self.pos = saved;
13369 return None;
13370 }
13371 self.advance();
13372 Some(leaf)
13373 }
13374
13375 fn take_bareword_filehandle(&mut self) -> Option<String> {
13377 self.take_bareword_filehandle_if(&[Token::Comma])
13378 }
13379
13380 fn take_bareword_filehandle_arg(&mut self, line: usize) -> Option<Expr> {
13384 self.take_bareword_filehandle_if(&[]).map(|name| Expr {
13385 kind: ExprKind::String(name),
13386 line,
13387 })
13388 }
13389
13390 fn parse_print_like(
13391 &mut self,
13392 make: impl FnOnce(Option<String>, Vec<Expr>) -> ExprKind,
13393 ) -> StrykeResult<Expr> {
13394 let line = self.peek_line();
13395 let handle = if let Token::Ident(ref h) = self.peek().clone() {
13397 if h == "main"
13404 && matches!(self.peek_at(1), Token::PackageSep)
13405 && matches!(
13406 self.peek_at(2),
13407 Token::Ident(s) if !s.is_empty()
13408 && s.chars().all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
13409 && s.chars().next().is_some_and(|c| c.is_ascii_uppercase() || c == '_'))
13410 {
13411 self.advance(); self.advance(); }
13414 let h = match self.peek().clone() {
13415 Token::Ident(s) => s,
13416 _ => h.clone(),
13417 };
13418 if h.chars().all(|c| c.is_uppercase() || c == '_')
13419 && !matches!(self.peek(), Token::LParen)
13420 {
13421 let saved = self.pos;
13422 self.advance();
13423 let is_tilde_range_after = matches!(self.peek(), Token::BitNot)
13430 && self.suppress_tilde_range == 0
13431 && matches!(
13432 self.peek_at(1),
13433 Token::Ident(_) | Token::Integer(_) | Token::Float(_)
13434 );
13435 if !is_tilde_range_after
13436 && (self.peek().is_term_start()
13437 || matches!(
13438 self.peek(),
13439 Token::DoubleString(_)
13440 | Token::BacktickString(_)
13441 | Token::SingleString(_)
13442 ))
13443 {
13444 Some(h)
13445 } else {
13446 self.pos = saved;
13447 None
13448 }
13449 } else {
13450 None
13451 }
13452 } else if let Token::ScalarVar(ref v) = self.peek().clone() {
13453 let v = v.clone();
13469 if v == "_" || matches!(self.peek_at(1), Token::LParen) {
13470 None
13471 } else {
13472 let saved = self.pos;
13473 let var_line = self.peek_line();
13474 self.advance();
13475 let next = self.peek().clone();
13476 let next_line = self.peek_line();
13477 let is_stmt_modifier = matches!(&next, Token::Ident(kw)
13478 if matches!(kw.as_str(), "if" | "unless" | "while" | "until" | "for" | "foreach"));
13479 if !is_stmt_modifier
13480 && next_line == var_line
13481 && !matches!(next, Token::LBracket | Token::LBrace)
13482 && (next.is_term_start()
13483 || matches!(
13484 next,
13485 Token::DoubleString(_)
13486 | Token::BacktickString(_)
13487 | Token::SingleString(_)
13488 ))
13489 {
13490 Some(format!("${v}"))
13492 } else {
13493 self.pos = saved;
13494 None
13495 }
13496 }
13497 } else {
13498 None
13499 };
13500 let args =
13507 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
13508 let line_topic = self.peek_line();
13509 self.advance(); self.advance(); vec![Expr {
13512 kind: ExprKind::ScalarVar("_".into()),
13513 line: line_topic,
13514 }]
13515 } else {
13516 self.parse_list_until_terminator_allow_pipe()?
13517 };
13518 Ok(Expr {
13519 kind: make(handle, args),
13520 line,
13521 })
13522 }
13523
13524 fn parse_block_list(&mut self) -> StrykeResult<(Block, Expr)> {
13525 let block = self.parse_block()?;
13526 let block_end_line = self.prev_line();
13527 self.eat(&Token::Comma);
13528 if self.in_pipe_rhs()
13532 && (matches!(
13533 self.peek(),
13534 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13535 ) || self.peek_line() > block_end_line)
13536 {
13537 let line = self.peek_line();
13538 return Ok((block, self.pipe_placeholder_list(line)));
13539 }
13540 let list = self.parse_expression()?;
13541 Ok((block, list))
13542 }
13543
13544 fn parse_comma_expr_list_with_timeout_tail(
13547 &mut self,
13548 paren: bool,
13549 ) -> StrykeResult<(Vec<Expr>, Option<Expr>)> {
13550 let mut parts = vec![self.parse_assign_expr()?];
13551 loop {
13552 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13553 break;
13554 }
13555 if paren && matches!(self.peek(), Token::RParen) {
13556 break;
13557 }
13558 if matches!(
13559 self.peek(),
13560 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13561 ) {
13562 break;
13563 }
13564 if self.peek_is_postfix_stmt_modifier_keyword() {
13565 break;
13566 }
13567 if let Token::Ident(ref kw) = self.peek().clone() {
13568 if kw == "timeout" && matches!(self.peek_at(1), Token::FatArrow) {
13569 self.advance();
13570 self.expect(&Token::FatArrow)?;
13571 let t = self.parse_assign_expr()?;
13572 return Ok((parts, Some(t)));
13573 }
13574 }
13575 parts.push(self.parse_assign_expr()?);
13576 }
13577 Ok((parts, None))
13578 }
13579
13580 fn parse_init_block_then_list_optional_progress(
13582 &mut self,
13583 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
13584 let init = self.parse_assign_expr()?;
13585 self.expect(&Token::Comma)?;
13586 let block = self.parse_block_or_bareword_block()?;
13587 self.eat(&Token::Comma);
13588 let line = self.peek_line();
13589 if let Token::Ident(ref kw) = self.peek().clone() {
13590 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13591 self.advance();
13592 self.expect(&Token::FatArrow)?;
13593 let prog = self.parse_assign_expr()?;
13594 return Ok((
13595 init,
13596 block,
13597 Expr {
13598 kind: ExprKind::List(vec![]),
13599 line,
13600 },
13601 Some(prog),
13602 ));
13603 }
13604 }
13605 if matches!(
13606 self.peek(),
13607 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13608 ) {
13609 return Ok((
13610 init,
13611 block,
13612 Expr {
13613 kind: ExprKind::List(vec![]),
13614 line,
13615 },
13616 None,
13617 ));
13618 }
13619 let mut parts = vec![self.parse_assign_expr()?];
13620 loop {
13621 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13622 break;
13623 }
13624 if matches!(
13625 self.peek(),
13626 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
13627 ) {
13628 break;
13629 }
13630 if self.peek_is_postfix_stmt_modifier_keyword() {
13631 break;
13632 }
13633 if let Token::Ident(ref kw) = self.peek().clone() {
13634 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13635 self.advance();
13636 self.expect(&Token::FatArrow)?;
13637 let prog = self.parse_assign_expr()?;
13638 return Ok((init, block, merge_expr_list(parts), Some(prog)));
13639 }
13640 }
13641 parts.push(self.parse_assign_expr()?);
13642 }
13643 Ok((init, block, merge_expr_list(parts), None))
13644 }
13645
13646 fn parse_cluster_block_then_list_optional_progress(
13648 &mut self,
13649 ) -> StrykeResult<(Expr, Block, Expr, Option<Expr>)> {
13650 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_add(1);
13653 let cluster = self.parse_assign_expr();
13654 self.suppress_scalar_hash_brace = self.suppress_scalar_hash_brace.saturating_sub(1);
13655 let cluster = cluster?;
13656 self.eat(&Token::Comma);
13658 let block = self.parse_block_or_bareword_block()?;
13659 let block_end_line = self.prev_line();
13660 self.eat(&Token::Comma);
13661 let line = self.peek_line();
13662 if let Token::Ident(ref kw) = self.peek().clone() {
13663 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13664 self.advance();
13665 self.expect(&Token::FatArrow)?;
13666 let prog = self.parse_assign_expr_stop_at_pipe()?;
13667 return Ok((
13668 cluster,
13669 block,
13670 Expr {
13671 kind: ExprKind::List(vec![]),
13672 line,
13673 },
13674 Some(prog),
13675 ));
13676 }
13677 }
13678 let empty_list_ok = matches!(
13679 self.peek(),
13680 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13681 ) || (self.in_pipe_rhs()
13682 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13683 if empty_list_ok {
13684 return Ok((
13685 cluster,
13686 block,
13687 Expr {
13688 kind: ExprKind::List(vec![]),
13689 line,
13690 },
13691 None,
13692 ));
13693 }
13694 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13695 loop {
13696 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13697 break;
13698 }
13699 if matches!(
13700 self.peek(),
13701 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13702 ) {
13703 break;
13704 }
13705 if self.peek_is_postfix_stmt_modifier_keyword() {
13706 break;
13707 }
13708 if let Token::Ident(ref kw) = self.peek().clone() {
13709 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13710 self.advance();
13711 self.expect(&Token::FatArrow)?;
13712 let prog = self.parse_assign_expr_stop_at_pipe()?;
13713 return Ok((cluster, block, merge_expr_list(parts), Some(prog)));
13714 }
13715 }
13716 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13717 }
13718 Ok((cluster, block, merge_expr_list(parts), None))
13719 }
13720
13721 fn parse_block_then_list_optional_progress(
13730 &mut self,
13731 ) -> StrykeResult<(Block, Expr, Option<Expr>)> {
13732 let block = self.parse_block_or_bareword_block()?;
13733 let block_end_line = self.prev_line();
13734 self.eat(&Token::Comma);
13735 let line = self.peek_line();
13736 if let Token::Ident(ref kw) = self.peek().clone() {
13737 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13738 self.advance();
13739 self.expect(&Token::FatArrow)?;
13740 let prog = self.parse_assign_expr_stop_at_pipe()?;
13741 return Ok((
13742 block,
13743 Expr {
13744 kind: ExprKind::List(vec![]),
13745 line,
13746 },
13747 Some(prog),
13748 ));
13749 }
13750 }
13751 let empty_list_ok = matches!(
13759 self.peek(),
13760 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13761 ) || (self.in_pipe_rhs()
13762 && (matches!(self.peek(), Token::Comma) || self.peek_line() > block_end_line));
13763 if empty_list_ok {
13764 return Ok((
13765 block,
13766 Expr {
13767 kind: ExprKind::List(vec![]),
13768 line,
13769 },
13770 None,
13771 ));
13772 }
13773 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
13774 loop {
13775 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
13776 break;
13777 }
13778 if matches!(
13779 self.peek(),
13780 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
13781 ) {
13782 break;
13783 }
13784 if self.peek_is_postfix_stmt_modifier_keyword() {
13785 break;
13786 }
13787 if let Token::Ident(ref kw) = self.peek().clone() {
13788 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
13789 self.advance();
13790 self.expect(&Token::FatArrow)?;
13791 let prog = self.parse_assign_expr_stop_at_pipe()?;
13792 return Ok((block, merge_expr_list(parts), Some(prog)));
13793 }
13794 }
13795 parts.push(self.parse_assign_expr_stop_at_pipe()?);
13796 }
13797 Ok((block, merge_expr_list(parts), None))
13798 }
13799
13800 fn parse_fan_count_and_block(
13802 &mut self,
13803 line: usize,
13804 ) -> StrykeResult<(Option<Box<Expr>>, Block)> {
13805 if matches!(self.peek(), Token::LBrace) {
13807 let block = self.parse_block()?;
13808 return Ok((None, block));
13809 }
13810 let saved = self.pos;
13811 let first = self.parse_postfix()?;
13813 if matches!(self.peek(), Token::LBrace) {
13814 let block = self.parse_block()?;
13816 Ok((Some(Box::new(first)), block))
13817 } else if matches!(self.peek(), Token::Semicolon | Token::RBrace | Token::Eof)
13818 || (matches!(self.peek(), Token::Comma)
13819 && matches!(self.peek_at(1), Token::Ident(ref kw) if kw == "progress"))
13820 {
13821 let block = self.bareword_to_no_arg_block(first);
13823 Ok((None, block))
13824 } else if matches!(first.kind, ExprKind::Integer(_)) {
13825 self.eat(&Token::Comma);
13827 let body = self.parse_fan_blockless_body(line)?;
13828 Ok((Some(Box::new(first)), body))
13829 } else {
13830 self.pos = saved;
13833 let body = self.parse_fan_blockless_body(line)?;
13834 Ok((None, body))
13835 }
13836 }
13837
13838 fn parse_fan_blockless_body(&mut self, line: usize) -> StrykeResult<Block> {
13840 if matches!(self.peek(), Token::LBrace) {
13841 return self.parse_block();
13842 }
13843 if let Token::Ident(ref name) = self.peek().clone() {
13845 if matches!(
13846 self.peek_at(1),
13847 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13848 ) {
13849 let name = name.clone();
13850 self.advance();
13851 let body = Expr {
13852 kind: ExprKind::FuncCall { name, args: vec![] },
13853 line,
13854 };
13855 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13856 }
13857 }
13858 let expr = self.parse_assign_expr_stop_at_pipe()?;
13860 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13861 }
13862
13863 fn bareword_to_no_arg_block(&self, expr: Expr) -> Block {
13866 let line = expr.line;
13867 let body = match &expr.kind {
13868 ExprKind::Bareword(name) => Expr {
13869 kind: ExprKind::FuncCall {
13870 name: name.clone(),
13871 args: vec![],
13872 },
13873 line,
13874 },
13875 _ => expr,
13876 };
13877 vec![Statement::new(StmtKind::Expression(body), line)]
13878 }
13879
13880 fn parse_block_or_bareword_block(&mut self) -> StrykeResult<Block> {
13889 if matches!(self.peek(), Token::LBrace) {
13890 return self.parse_block();
13891 }
13892 let line = self.peek_line();
13893 if let Token::Ident(ref name) = self.peek().clone() {
13896 if matches!(
13897 self.peek_at(1),
13898 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
13899 ) {
13900 let name = name.clone();
13901 self.advance();
13902 let body = Expr {
13903 kind: ExprKind::FuncCall {
13904 name,
13905 args: vec![Expr {
13906 kind: ExprKind::ScalarVar("_".to_string()),
13907 line,
13908 }],
13909 },
13910 line,
13911 };
13912 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13913 }
13914 }
13915 let expr = self.parse_assign_expr_stop_at_pipe()?;
13917 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13918 }
13919
13920 fn parse_block_or_bareword_block_no_args(&mut self) -> StrykeResult<Block> {
13925 if matches!(self.peek(), Token::LBrace) {
13926 return self.parse_block();
13927 }
13928 let line = self.peek_line();
13929 if let Token::Ident(ref name) = self.peek().clone() {
13930 if matches!(
13931 self.peek_at(1),
13932 Token::Comma
13933 | Token::Semicolon
13934 | Token::RBrace
13935 | Token::Eof
13936 | Token::PipeForward
13937 | Token::Integer(_)
13938 ) {
13939 let name = name.clone();
13940 self.advance();
13941 let body = Expr {
13942 kind: ExprKind::FuncCall { name, args: vec![] },
13943 line,
13944 };
13945 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
13946 }
13947 }
13948 let expr = self.parse_postfix()?;
13949 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
13950 }
13951
13952 fn is_known_bareword(name: &str) -> bool {
13960 Self::is_perl5_core(name) || Self::stryke_extension_name(name).is_some()
13961 }
13962
13963 fn is_try_builtin_name(name: &str) -> bool {
13969 crate::builtins::BUILTIN_ARMS
13970 .iter()
13971 .any(|arm| arm.contains(&name))
13972 }
13973
13974 fn is_perl5_core(name: &str) -> bool {
13979 matches!(
13980 name,
13981 "map" | "grep" | "sort" | "reverse" | "join" | "split"
13983 | "push" | "pop" | "shift" | "unshift" | "splice"
13984 | "splice_last" | "splice1" | "spl_last"
13985 | "pack" | "unpack"
13986 | "unpack_first" | "unpack1" | "up1"
13987 | "keys" | "values" | "each"
13989 | "chomp" | "chop" | "chr" | "ord" | "hex" | "oct"
13991 | "lc" | "uc" | "lcfirst" | "ucfirst"
13992 | "length" | "substr" | "index" | "rindex"
13993 | "sprintf" | "printf" | "print" | "say"
13994 | "pos" | "quotemeta" | "study"
13995 | "abs" | "int" | "sqrt" | "sin" | "cos" | "atan2"
13997 | "exp" | "log" | "rand" | "srand"
13998 | "time" | "localtime" | "gmtime"
14000 | "defined" | "undef" | "ref" | "scalar" | "wantarray"
14002 | "caller" | "delete" | "exists" | "bless" | "prototype"
14003 | "tie" | "untie" | "tied"
14004 | "open" | "close" | "read" | "readline" | "write" | "seek" | "tell"
14006 | "eof" | "binmode" | "getc" | "fileno" | "truncate"
14007 | "format" | "formline" | "select" | "vec"
14008 | "sysopen" | "sysread" | "sysseek" | "syswrite"
14009 | "stat" | "lstat" | "rename" | "unlink" | "utime"
14011 | "mkdir" | "rmdir" | "chdir" | "chmod" | "chown"
14012 | "glob" | "opendir" | "readdir" | "closedir"
14013 | "link" | "readlink" | "symlink"
14014 | "fcntl" | "flock" | "ioctl" | "pipe" | "dbmopen" | "dbmclose"
14016 | "msgctl" | "msgget" | "msgrcv" | "msgsnd"
14018 | "semctl" | "semget" | "semop"
14019 | "shmctl" | "shmget" | "shmread" | "shmwrite"
14020 | "system" | "exec" | "exit" | "die" | "warn" | "dump"
14022 | "fork" | "wait" | "waitpid" | "kill" | "syscall" | "alarm" | "sleep"
14023 | "chroot" | "times" | "umask" | "reset"
14024 | "getpgrp" | "setpgrp" | "getppid"
14025 | "getpriority" | "setpriority"
14026 | "socket" | "socketpair" | "connect" | "listen" | "accept" | "shutdown"
14028 | "send" | "recv" | "bind" | "setsockopt" | "getsockopt"
14029 | "getpeername" | "getsockname"
14030 | "getpwnam" | "getpwuid" | "getpwent" | "setpwent"
14032 | "getgrnam" | "getgrgid" | "getgrent" | "setgrent"
14033 | "getlogin"
14034 | "gethostbyname" | "gethostbyaddr" | "gethostent"
14035 | "getnetbyname" | "getnetent"
14036 | "getprotobyname" | "getprotoent"
14037 | "getservbyname" | "getservent"
14038 | "sethostent" | "setnetent" | "setprotoent" | "setservent"
14039 | "endpwent" | "endgrent"
14040 | "endhostent" | "endnetent" | "endprotoent" | "endservent"
14041 | "return" | "do" | "eval" | "require"
14043 | "my" | "var" | "val" | "our" | "local" | "use" | "no"
14044 | "sub" | "if" | "unless" | "while" | "until"
14045 | "for" | "foreach" | "last" | "next" | "redo" | "goto"
14046 | "not" | "and" | "or"
14047 | "qw" | "qq" | "q"
14049 | "BEGIN" | "END"
14051 )
14052 }
14053
14054 fn stryke_extension_name(name: &str) -> Option<&str> {
14057 match name {
14058 | "burp" | "god" | "swallow" | "ingest"
14070 | "congregation" | "ordain" | "muster" | "pray" | "annex"
14077 | "harvest" | "excommunicate" | "smite" | "bestow"
14078 | "enshrine" | "exhume" | "smother" | "amen"
14079 | "anoint" | "welcome" | "pilgrimage" | "bow"
14080 | "lick" | "peruse"
14081 | "chant" | "profess" | "apostatize" | "cathedral" | "cloister"
14082 | "recant" | "martyr" | "resurrect" | "divine" | "interrogate"
14083 | "ulid" | "is_ulid" | "ulid_timestamp"
14085 | "kahan_sum" | "welford_mean" | "welford_variance"
14086 | "welford_stddev" | "welford_pop_variance"
14087 | "clear" | "cls" | "whoami" | "groups"
14089 | "pushd" | "popd" | "dir_stack"
14090 | "history" | "repl_alias" | "repl_unalias" | "set_alias" | "unset_alias"
14091 | "term_size" | "term_width" | "term_height"
14092 | "set_title" | "beep" | "ring_bell" | "man" | "manpage"
14093 | "run" | "exec_script" | "source" | "src"
14094 | "rm" | "mktemp" | "mktempdir" | "whereis"
14096 | "nice" | "renice"
14097 | "tree" | "comm" | "column" | "xargs"
14098 | "openurl" | "xdg_open"
14099 | "curl_get" | "curl_post"
14100 | "iconv" | "strftime"
14101 | "tac" | "rev_lines"
14102 | "tty_raw" | "tty_cooked"
14103 | "bloom_filter" | "bloom_add" | "bloom_contains" | "bloom_len"
14105 | "bloom_clear" | "bloom_merge" | "bloom_fpr" | "bloom_bits"
14106 | "bloom_serialize" | "bloom_deserialize"
14107 | "hll" | "hyperloglog" | "hll_add" | "hll_count" | "hll_merge"
14108 | "hll_clear" | "hll_precision" | "hll_serialize" | "hll_deserialize"
14109 | "cms" | "count_min_sketch" | "cms_add" | "cms_count" | "cms_query"
14110 | "cms_merge" | "cms_clear" | "cms_serialize" | "cms_deserialize"
14111 | "topk" | "top_k_sketch" | "topk_add" | "topk_heavies" | "topk_count"
14112 | "topk_size" | "topk_merge" | "topk_clear"
14113 | "topk_serialize" | "topk_deserialize"
14114 | "t_digest" | "tdg" | "tdigest" | "td_add" | "td_quantile" | "td_count"
14115 | "td_min" | "td_max" | "td_sum" | "td_mean" | "td_merge" | "td_clear"
14116 | "td_serialize" | "td_deserialize"
14117 | "roaring" | "roaring_bitmap" | "rbm" | "rb_add" | "rb_remove" | "rb_contains"
14118 | "rb_len" | "rb_min" | "rb_max" | "rb_to_array" | "rb_rank"
14119 | "rb_or" | "rb_and" | "rb_xor" | "rb_andnot" | "rb_clear"
14120 | "rb_serialize" | "rb_deserialize"
14121 | "token_bucket" | "leaky_bucket" | "rl_try_take" | "rl_available"
14123 | "hash_ring" | "consistent_hash" | "hr_add" | "hr_remove" | "hr_get" | "hr_nodes"
14124 | "simhash" | "sh_add" | "sh_digest" | "sh_similarity"
14125 | "minhash" | "mh_add" | "mh_jaccard" | "mh_merge"
14126 | "interval_tree" | "it_insert" | "it_query_point" | "it_query_range"
14127 | "it_remove" | "it_len"
14128 | "bk_tree" | "bk_insert" | "bk_query" | "bk_len"
14129 | "rope" | "rope_insert" | "rope_delete" | "rope_substring"
14130 | "rope_to_string" | "rope_len"
14131 | "myers_diff" | "patience_diff"
14132 | "kv_open" | "kv_new" | "kv_put" | "kv_set" | "kv_get"
14134 | "kv_del" | "kv_delete" | "kv_remove" | "kv_exists" | "kv_has"
14135 | "kv_keys" | "kv_scan" | "kv_len" | "kv_count" | "kv_size"
14136 | "kv_commit" | "kv_flush" | "kv_batch" | "kv_close"
14137 | "kv_stats" | "kv_info"
14138 | "proceed" | "intercept_list" | "intercept_remove" | "intercept_clear"
14140 | "pmap" | "pmap_on" | "pflat_map" | "pflat_map_on" | "pmap_chunked"
14142 | "pgrep" | "pfor" | "psort" | "preduce" | "preduce_init" | "pmap_reduce"
14143 | "pcache" | "pchannel" | "pselect" | "puniq" | "pfirst" | "pany"
14144 | "fan" | "fan_cap" | "par_lines" | "par_walk" | "par_sed"
14145 | "par_find_files" | "par_line_count" | "pwatch" | "par_pipeline_stream"
14146 | "glob_par" | "ppool" | "barrier" | "pipeline" | "cluster"
14147 | "pmaps" | "pflat_maps" | "pgreps"
14148 | "controller" | "agent"
14150 | "mark" | "provenance" | "unmark"
14152 | "kick" | "udp_send"
14154 | "tcp_probe" | "tcp_banner" | "whois_query"
14155 | "udp_open" | "udp_send_to" | "udp_recv" | "udp_recv_from"
14157 | "udp_close" | "stun" | "stun_classify" | "punch"
14158 | "turn_allocate" | "turn_permission" | "turn_send"
14160 | "turn_recv" | "turn_refresh"
14161 | "teleport" | "arrive"
14163 | "turnbuckle" | "tb_alive" | "tb_ping" | "tb_close"
14165 | "weep"
14167 | "fore" | "e" | "ep" | "flat_map" | "flat_maps" | "maps" | "filter" | "fi" | "find_all" | "reduce" | "fold"
14169 | "inject" | "collect" | "uniq" | "distinct" | "any" | "all" | "none"
14170 | "first" | "detect" | "find" | "find_index" | "firstidx" | "first_index"
14171 | "compact" | "concat" | "chain" | "reject" | "grepv" | "flatten" | "set"
14172 | "min_by" | "max_by" | "sort_by" | "tally"
14173 | "each_with_index" | "count" | "cnt" |"len" | "group_by" | "chunk_by"
14174 | "zip" | "chunk" | "chunked" | "sliding_window" | "windowed"
14175 | "enumerate" | "with_index" | "shuffle" | "shuffled"| "heap"
14176 | "take_while" | "drop_while" | "skip_while" | "tap" | "peek" | "partition"
14177 | "zip_with" | "count_by" | "skip" | "first_or"
14178 | "getopts"
14180 | "input" | "lines" | "words" | "chars" | "cindex" | "crindex"
14182 | "digits" | "letters" | "letters_uc" | "letters_lc"
14183 | "punctuation" | "punct"
14184 | "sentences" | "sents"
14185 | "paragraphs" | "paras" | "sections" | "sects"
14186 | "numbers" | "nums" | "graphemes" | "grs" | "columns" | "cols"
14187 | "trim" | "avg" | "stddev"
14188 | "squared" | "sq" | "square" | "cubed" | "cb" | "cube" | "expt" | "pow" | "pw"
14189 | "normalize" | "snake_case" | "camel_case" | "kebab_case"
14190 | "frequencies" | "freq" | "pfrequencies" | "pfreq"
14191 | "interleave" | "ddump" | "stringify" | "str" | "top"
14192 | "to_json" | "to_csv" | "to_toml" | "to_yaml" | "to_xml"
14193 | "to_html" | "to_markdown" | "to_table" | "xopen"
14194 | "from_json" | "from_csv" | "from_toml" | "from_yaml" | "from_xml"
14195 | "clip" | "clipboard" | "paste" | "pbcopy" | "pbpaste" | "preview"
14196 | "sparkline" | "spark" | "bar_chart" | "bars" | "flame" | "flamechart"
14197 | "histo" | "gauge" | "spinner" | "spinner_start" | "spinner_stop"
14198 | "to_hash" | "to_set"
14199 | "to_file" | "read_lines" | "append_file" | "write_json" | "read_json"
14200 | "tempfile" | "tempdir" | "list_count" | "list_size" | "size"
14201 | "clamp" | "grep_v" | "select_keys" | "pluck" | "glob_match" | "which_all"
14202 | "dedup" | "nth" | "tail" | "take" | "drop" | "tee" | "range"
14203 | "inc" | "dec" | "elapsed"
14204 | "files" | "filesf" | "f" | "fr" | "dirs" | "d" | "dr" | "sym_links"
14206 | "sockets" | "pipes" | "block_devices" | "char_devices" | "exe" | "executables"
14207 | "basename" | "dirname" | "fileparse" | "realpath" | "canonpath"
14208 | "copy" | "cp" | "move" | "spurt" | "spit" | "read_bytes" | "which"
14209 | "getcwd" | "cd" | "ls" | "touch" | "gethostname" | "uname"
14210 | "file" | "xxd"
14211 | "csv_read" | "csv_write" | "dataframe" | "sqlite"
14213 | "fetch" | "fetch_json" | "fetch_async" | "fetch_async_json"
14214 | "par_fetch" | "par_csv_read" | "par_pipeline"
14215 | "json_encode" | "json_decode" | "json_jq"
14216 | "http_request" | "serve" | "ssh"
14217 | "html_parse" | "css_select" | "xml_parse" | "xpath"
14218 | "smtp_send"
14219 | "net_interfaces" | "net_ipv4" | "net_ipv6" | "net_mac"
14220 | "net_public_ip" | "net_dns" | "net_reverse_dns"
14221 | "net_ping" | "net_port_open" | "net_ports_scan"
14222 | "net_latency" | "net_download" | "net_headers"
14223 | "net_dns_servers" | "net_gateway" | "net_whois" | "net_hostname"
14224 | "git_log" | "git_status" | "git_diff" | "git_branches"
14226 | "git_tags" | "git_blame" | "git_authors" | "git_files"
14227 | "git_show" | "git_root"
14228 | "gh_get" | "gh_user" | "gh_org" | "gh_followers" | "gh_following"
14230 | "gh_repo" | "gh_repos" | "gh_org_repos" | "gh_starred"
14231 | "gh_gists" | "gh_gist"
14232 | "gh_issues" | "gh_prs" | "gh_commits" | "gh_branches"
14233 | "gh_tags" | "gh_releases" | "gh_contributors" | "gh_forks"
14234 | "gh_stargazers" | "gh_topics" | "gh_languages"
14235 | "gh_readme" | "gh_workflows" | "gh_runs"
14236 | "gh_search_repos" | "gh_search_users" | "gh_search_code" | "gh_search_issues"
14237 | "gh_rate_limit" | "gh_meta" | "gh_emojis" | "gh_zen"
14238 | "audio_convert" | "audio_info" | "id3_read" | "id3_write"
14240 | "to_pdf" | "pdf_text" | "pdf_pages"
14242 | "toml_encode" | "toml_decode"
14244 | "yaml_encode" | "yaml_decode"
14245 | "xml_encode" | "xml_decode"
14246 | "md5" | "sha1" | "sha224" | "sha256" | "sha384" | "sha512"
14248 | "sha3_256" | "s3_256" | "sha3_512" | "s3_512"
14249 | "shake128" | "shake256"
14250 | "hmac_sha256" | "hmac_sha1" | "hmac_sha384" | "hmac_sha512" | "hmac_md5"
14251 | "uuid" | "crc32"
14252 | "blake2b" | "b2b" | "blake2s" | "b2s" | "blake3" | "b3"
14253 | "ripemd160" | "rmd160" | "md4"
14254 | "xxh32" | "xxhash32" | "xxh64" | "xxhash64" | "xxh3" | "xxhash3" | "xxh3_128" | "xxhash3_128"
14255 | "murmur3" | "murmur3_32" | "murmur3_128"
14256 | "siphash" | "siphash_keyed"
14257 | "hkdf_sha256" | "hkdf" | "hkdf_sha512"
14258 | "poly1305" | "poly1305_mac"
14259 | "base32_encode" | "b32e" | "base32_decode" | "b32d"
14260 | "base58_encode" | "b58e" | "base58_decode" | "b58d"
14261 | "totp" | "totp_generate" | "totp_verify" | "hotp" | "hotp_generate"
14262 | "aes_cbc_encrypt" | "aes_cbc_enc" | "aes_cbc_decrypt" | "aes_cbc_dec"
14263 | "blowfish_encrypt" | "bf_enc" | "blowfish_decrypt" | "bf_dec"
14264 | "des3_encrypt" | "3des_enc" | "tdes_enc" | "des3_decrypt" | "3des_dec" | "tdes_dec"
14265 | "twofish_encrypt" | "tf_enc" | "twofish_decrypt" | "tf_dec"
14266 | "camellia_encrypt" | "cam_enc" | "camellia_decrypt" | "cam_dec"
14267 | "cast5_encrypt" | "cast5_enc" | "cast5_decrypt" | "cast5_dec"
14268 | "salsa20" | "salsa20_encrypt" | "salsa20_decrypt"
14269 | "xsalsa20" | "xsalsa20_encrypt" | "xsalsa20_decrypt"
14270 | "secretbox" | "secretbox_seal" | "secretbox_open"
14271 | "nacl_box_keygen" | "box_keygen" | "nacl_box" | "nacl_box_seal" | "box_seal"
14272 | "nacl_box_open" | "box_open"
14273 | "qr_ascii" | "qr" | "qr_png" | "qr_svg"
14274 | "barcode_code128" | "code128" | "barcode_code39" | "code39"
14275 | "barcode_ean13" | "ean13" | "barcode_svg"
14276 | "argon2_hash" | "argon2" | "argon2_verify"
14277 | "bcrypt_hash" | "bcrypt" | "bcrypt_verify"
14278 | "scrypt_hash" | "scrypt" | "scrypt_verify"
14279 | "pbkdf2" | "pbkdf2_derive"
14280 | "random_bytes" | "randbytes" | "random_bytes_hex" | "randhex"
14281 | "aes_encrypt" | "aes_enc" | "aes_decrypt" | "aes_dec"
14282 | "chacha_encrypt" | "chacha_enc" | "chacha_decrypt" | "chacha_dec"
14283 | "rsa_keygen" | "rsa_encrypt" | "rsa_enc" | "rsa_decrypt" | "rsa_dec"
14284 | "rsa_encrypt_pkcs1" | "rsa_decrypt_pkcs1" | "rsa_sign" | "rsa_verify"
14285 | "ecdsa_p256_keygen" | "p256_keygen" | "ecdsa_p256_sign" | "p256_sign"
14286 | "ecdsa_p256_verify" | "p256_verify"
14287 | "ecdsa_p384_keygen" | "p384_keygen" | "ecdsa_p384_sign" | "p384_sign"
14288 | "ecdsa_p384_verify" | "p384_verify"
14289 | "ecdsa_secp256k1_keygen" | "secp256k1_keygen"
14290 | "ecdsa_secp256k1_sign" | "secp256k1_sign"
14291 | "ecdsa_secp256k1_verify" | "secp256k1_verify"
14292 | "ecdh_p256" | "p256_dh" | "ecdh_p384" | "p384_dh"
14293 | "ed25519_keygen" | "ed_keygen" | "ed25519_sign" | "ed_sign"
14294 | "ed25519_verify" | "ed_verify"
14295 | "x25519_keygen" | "x_keygen" | "x25519_dh" | "x_dh"
14296 | "base64_encode" | "base64_decode"
14297 | "hex_encode" | "hex_decode"
14298 | "hide" | "reveal" | "hide_capacity"
14300 | "url_encode" | "url_decode"
14301 | "gzip" | "gunzip" | "gz" | "ugz" | "zstd" | "zstd_decode" | "zst" | "uzst"
14302 | "brotli" | "br" | "brotli_decode" | "ubr"
14303 | "xz" | "lzma" | "xz_decode" | "unxz" | "unlzma"
14304 | "bzip2" | "bz2" | "bzip2_decode" | "bunzip2" | "ubz2"
14305 | "lz4" | "lz4_decode" | "unlz4"
14306 | "snappy" | "snp" | "snappy_decode" | "unsnappy"
14307 | "lzw" | "lzw_decode" | "unlzw"
14308 | "tar_create" | "tar" | "tar_extract" | "untar" | "tar_list"
14309 | "tar_gz_create" | "tgz" | "tar_gz_extract" | "untgz"
14310 | "zip_create" | "zip_archive" | "zip_extract" | "unzip_archive" | "zip_list"
14311 | "erf" | "erfc" | "gamma" | "tgamma" | "lgamma" | "ln_gamma"
14313 | "digamma" | "psi" | "beta_fn" | "lbeta" | "ln_beta"
14314 | "betainc" | "beta_reg" | "gammainc" | "gamma_li"
14315 | "gammaincc" | "gamma_ui" | "gammainc_reg" | "gamma_lr"
14316 | "gammaincc_reg" | "gamma_ur"
14317 | "datetime_utc" | "datetime_now_tz" | "now"
14319 | "datetime_format_tz" | "datetime_add_seconds"
14320 | "datetime_from_epoch"
14321 | "datetime_parse_rfc3339" | "datetime_parse_local"
14322 | "datetime_strftime"
14323 | "dateseq" | "dategrep" | "dateround" | "datesort"
14324 | "jwt_encode" | "jwt_decode" | "jwt_decode_unsafe"
14326 | "log_info" | "log_warn" | "log_error"
14328 | "log_debug" | "log_trace" | "log_json" | "log_level"
14329 | "async" | "spawn" | "trace" | "timer" | "bench"
14331 | "eval_timeout" | "retry" | "rate_limit" | "every"
14332 | "gen" | "watch"
14333 | "cache_clear" | "cache_exists" | "cache_stats" | "cacheview"
14335 | "assert_eq" | "assert_ne" | "assert_ok" | "assert_err"
14337 | "assert_true" | "assert_false"
14338 | "assert_gt" | "assert_lt" | "assert_ge" | "assert_le"
14339 | "assert_match" | "assert_contains" | "assert_near" | "assert_dies"
14340 | "test_run" | "run_tests" | "test_skip" | "skip_test" | "skip_assert"
14341 | "mounts" | "du" | "du_tree" | "process_list"
14343 | "thread_count" | "pool_info" | "par_bench"
14344 | "perfview" | "pfv"
14345 | "docs" | "help" | "h"
14346 | "banner"
14347 | "ip_parse" | "ip_is_valid" | "ip_version" | "ip_family"
14349 | "ip_to_int" | "int_to_ip" | "ip_to_bytes" | "bytes_to_ip"
14350 | "ip_to_bits" | "bits_to_ip"
14351 | "ip_is_private" | "ip_is_loopback" | "ip_is_multicast"
14352 | "ip_is_link_local" | "ip_is_unspecified" | "ip_is_global"
14353 | "ip_is_documentation" | "ip_is_benchmarking" | "ip_is_shared"
14354 | "ip_is_reserved" | "ip_is_broadcast"
14355 | "ip_canonical" | "ip_reverse" | "ip_arpa"
14356 | "ip_compare" | "ip_sort" | "ip_random"
14357 | "ipv4_parse" | "ipv4_is_valid" | "ipv4_classful_class"
14358 | "ipv6_parse" | "ipv6_is_valid" | "ipv6_canonical"
14359 | "ipv6_expand" | "ipv6_compress" | "ipv6_strip_zone" | "ipv6_zone_id"
14360 | "ipv6_link_local" | "ipv6_unique_local" | "ipv6_solicited_node"
14361 | "ipv6_eui64_addr" | "ipv6_link_local_from_mac"
14362 | "ipv4_to_ipv6_mapped" | "ipv4_to_ipv6_6to4" | "ipv6_to_ipv4_compat"
14363 | "ipv6_is_6to4" | "ipv6_6to4_extract"
14364 | "ipv6_is_teredo" | "ipv6_teredo_extract"
14365 | "ipv6_is_isatap" | "ipv6_isatap_extract"
14366 | "cidr_parse" | "cidr_valid_subnet" | "cidr_format"
14367 | "cidr_prefix_len" | "cidr_class"
14368 | "cidr_network" | "cidr_broadcast" | "cidr_netmask"
14369 | "cidr_hostmask" | "cidr_wildcard"
14370 | "cidr_to_netmask" | "netmask_to_prefix"
14371 | "cidr_first_host" | "cidr_last_host" | "cidr_num_hosts"
14372 | "cidr_size" | "cidr_hosts" | "cidr_iterate"
14373 | "cidr_contains" | "ip_in_cidr" | "ip_in_subnet"
14374 | "cidr_subnet" | "cidr_supernet" | "cidr_subnets" | "cidr_split"
14375 | "cidr_overlaps" | "cidr_aggregate" | "cidr_summarize"
14376 | "cidr_intersection" | "cidr_difference" | "cidr_union"
14377 | "cidr_minimum_covering" | "cidr_is_aggregable"
14378 | "cidr_next" | "cidr_prev" | "cidr_distance"
14379 | "cidr_random_ip" | "ip_random_in_cidr"
14380 | "cidr_compare" | "cidr_sort"
14381 | "mac_parse" | "mac_is_valid" | "mac_normalize" | "mac_format"
14382 | "mac_to_int" | "int_to_mac" | "mac_to_bytes" | "bytes_to_mac"
14383 | "mac_oui" | "mac_vendor_lookup" | "mac_lookup_vendor"
14384 | "mac_is_unicast" | "mac_is_multicast" | "mac_is_broadcast"
14385 | "mac_is_locally_administered" | "mac_is_universally_administered"
14386 | "mac_random" | "mac_random_local" | "mac_compare"
14387 | "eui48_to_eui64" | "eui64_to_eui48" | "eui64_from_mac"
14388 | "port_name" | "port_is_well_known" | "port_is_assigned"
14389 | "port_is_registered" | "port_is_ephemeral" | "port_is_dynamic"
14390 | "port_to_service" | "port_service_lookup"
14391 | "port_parse_range" | "port_random_ephemeral"
14392 | "ws_handshake_key" | "ws_handshake_accept"
14393 | "ws_mask" | "ws_unmask" | "ws_frame_encode" | "ws_frame_decode"
14394 | "ws_close_frame"
14395 | "cookie_parse" | "cookie_format"
14396 | "cookie_jar_new" | "cookie_jar_add" | "cookie_jar_get"
14397 | "cookie_is_session" | "cookie_is_expired"
14398 | "cookie_domain_matches" | "cookie_path_matches"
14399 | "cookie_set_max_age"
14400 | "http_method_is_idempotent" | "http_method_is_safe"
14401 | "http_method_has_body"
14402 | "http_status_class"
14403 | "http_status_is_informational" | "http_status_is_success"
14404 | "http_status_is_redirect" | "http_status_is_client_error"
14405 | "http_status_is_server_error"
14406 | "http_status_text" | "http_date_parse" | "http_date_format"
14407 | "mime_type_for_extension" | "mime_extension_for_type"
14408 | "mime_is_text" | "mime_is_image" | "mime_is_audio"
14409 | "mime_is_video" | "mime_is_application"
14410 | "bandwidth_format" | "bandwidth_parse"
14411 | "latency_ms" | "packet_loss" | "jitter_ms"
14412 | "rtt_min" | "rtt_max" | "rtt_avg"
14413 | "is_alpha_only" | "is_alphanumeric_only" | "is_numeric_only"
14415 | "is_ascii_only" | "is_printable_ascii" | "is_utf8"
14416 | "is_lowercase" | "is_uppercase" | "is_titlecase"
14417 | "is_palindrome_str"
14418 | "is_hex" | "is_octal" | "is_binary" | "is_base32"
14419 | "is_md5_hash" | "is_sha1_hash" | "is_sha256_hash"
14420 | "is_ipv6" | "is_cidr" | "is_mac"
14421 | "is_url_http" | "is_url_https"
14422 | "is_uuid_v4" | "is_uuid_v7"
14423 | "is_jwt" | "is_email_strict"
14424 | "luhn_digit" | "is_imei" | "is_imsi"
14425 | "is_vin" | "vin_decode"
14426 | "is_ean13" | "is_upc"
14427 | "is_isbn" | "isbn10_to_isbn13" | "isbn13_to_isbn10"
14428 | "iban_format" | "iban_country" | "is_bic" | "is_swift"
14429 | "is_phone" | "is_phone_e164"
14430 | "is_zip_us" | "is_zip_plus4" | "is_postal_code" | "is_ssn_us"
14431 | "semver_compare" | "semver_satisfies"
14432 | "semver_increment_major" | "semver_increment_minor" | "semver_increment_patch"
14433 | "extended_gcd" | "modinverse" | "modpow" | "modular_sqrt"
14435 | "stirling_1" | "stirling_2" | "catalan_number" | "lucas_n"
14436 | "prime_count_below" | "divisor_count" | "divisor_sum" | "sigma_divisors"
14437 | "sum_digits" | "product_digits" | "collatz_steps"
14438 | "hyperoperation" | "busy_beaver"
14439 | "quadratic_residue" | "is_quadratic_residue"
14440 | "discrete_log" | "order_modulo" | "square_free"
14441 | "perfect_number" | "abundant" | "deficient"
14442 | "random_bernoulli" | "random_normal" | "random_lognormal"
14444 | "random_exponential" | "random_poisson" | "random_gamma" | "random_beta"
14445 | "random_alphanumeric" | "random_alphabetic" | "random_password"
14446 | "random_choices_weighted"
14447 | "sample_weighted_unique" | "reservoir_sample_weighted"
14448 | "seeded_rng" | "save_random_state" | "restore_random_state"
14449 | "complex_new" | "complex_real" | "complex_imag"
14451 | "complex_polar" | "complex_from_polar"
14452 | "complex_magnitude" | "complex_abs" | "complex_phase" | "complex_angle"
14453 | "complex_conjugate"
14454 | "complex_add" | "complex_sub" | "complex_mul" | "complex_div"
14455 | "complex_pow" | "complex_sqrt" | "complex_exp" | "complex_log"
14456 | "complex_sin" | "complex_cos" | "complex_tan"
14457 | "complex_sinh" | "complex_cosh" | "complex_tanh"
14458 | "complex_equal"
14459 | "point_angle"
14460 | "line_intersect" | "line_segment_intersect" | "line_distance_point"
14461 | "polygon_signed_area" | "polygon_orientation" | "polygon_reverse"
14462 | "polygon_contains_point" | "polygon_convex"
14463 | "polygon_simplify_dp" | "polygon_convex_hull_2d"
14464 | "triangle_area" | "triangle_centroid"
14465 | "triangle_circumcircle" | "triangle_incircle"
14466 | "triangle_contains_point"
14467 | "circle_circumference" | "circle_area"
14468 | "circle_intersects_line" | "circle_intersects_circle"
14469 | "rect_area" | "rect_perimeter" | "rect_intersect"
14470 | "rect_contains_point" | "rect_union"
14471 | "ellipse_area"
14472 | "sphere_surface_area" | "cylinder_surface_area"
14473 | "cone_surface_area" | "torus_surface_area"
14474 | "srgb_to_rgb" | "rgb_to_srgb"
14475 | "rgb_to_p3" | "p3_to_rgb"
14476 | "rgb_to_adobe_rgb" | "adobe_rgb_to_rgb"
14477 | "xyz_d65_to_d50" | "xyz_d50_to_d65"
14478 | "gamma_apply" | "gamma_remove"
14479 | "white_point_d65" | "white_point_d50"
14480 | "color_temperature_to_rgb" | "rgb_to_color_temperature"
14481 | "chromatic_adaptation"
14482 | "color_interpolate_rgb" | "color_interpolate_hsl"
14483 | "color_interpolate_lab" | "color_interpolate_oklab"
14484 | "color_blend_screen"
14485 | "atan2_deg" | "atan2_quadrant"
14486 | "polar_to_cartesian" | "cartesian_to_polar"
14487 | "spherical_to_cartesian" | "cartesian_to_spherical"
14488 | "cylindrical_to_cartesian" | "cartesian_to_cylindrical"
14489 | "versine_fn"
14490 | "triples" | "n_tuples" | "peekable" | "runs" | "unique_by"
14492 | "multipeek" | "lookahead_n"
14493 | "sliding_average" | "sliding_sum" | "sliding_max" | "sliding_min"
14494 | "top_n_by" | "bottom_n_by" | "all_equal" | "take_n_random"
14495 | "unzip3" | "roundrobin" | "mode_iter" | "distinct_sample"
14496 | "ranked_choice" | "boyer_moore_majority"
14497 | "quickselect_nth" | "quickselect_median"
14498 | "top_k_min_heap" | "bottom_k_max_heap"
14499 | "unique_consecutive" | "exclude" | "exclude_first" | "exclude_last"
14500 | "weave_n" | "pad_left_n" | "pad_right_n"
14501 | "collect_into_string" | "collect_into_hashset" | "collect_into_btreeset"
14502 | "collect_into_hashmap" | "collect_into_btreemap"
14503 | "foldl1_iter" | "foldr1_iter"
14504 | "sort_by_cached_key"
14505 | "position_max" | "position_min" | "position_max_by" | "position_min_by"
14506 | "group_map"
14507 | "levenshtein_normalized" | "ratcliff_obershelp" | "match_rating"
14508 | "str_lcs" | "str_lcs_length" | "str_longest_common_substring"
14509 | "str_kmp" | "str_boyer_moore" | "str_rabin_karp"
14510 | "str_aho_corasick" | "str_z_array" | "str_suffix_array"
14511 | "str_rotations" | "str_compress_rle" | "str_decompress_rle"
14512 | "str_huffman_encode" | "str_huffman_decode"
14513 | "str_compress_lzss" | "str_decompress_lzss"
14514 | "str_isogram" | "fold_case"
14515 | "bignum_new" | "bignum_from_str" | "bignum_to_str" | "bignum_to_int"
14517 | "bignum_add" | "bignum_sub" | "bignum_mul" | "bignum_div" | "bignum_mod"
14518 | "bignum_pow" | "bignum_modpow" | "bignum_gcd" | "bignum_lcm"
14519 | "bignum_factorial" | "bignum_sqrt" | "bignum_bit_length"
14520 | "bignum_set_bit" | "bignum_clear_bit" | "bignum_test_bit"
14521 | "bignum_and" | "bignum_or" | "bignum_xor" | "bignum_not"
14522 | "bignum_shl" | "bignum_shr" | "bignum_compare"
14523 | "bignum_negate" | "bignum_abs" | "bignum_sign"
14524 | "bignum_is_zero" | "bignum_is_negative" | "bignum_is_prime"
14525 | "bignum_random"
14526 | "gravity_constant" | "physics_apply_force" | "physics_apply_impulse"
14527 | "physics_collide_aabb" | "physics_collide_sphere"
14528 | "physics_raycast" | "physics_step"
14529 | "particle_emit" | "particle_update"
14530 | "vector2_new" | "vector2_add" | "vector2_sub" | "vector2_scale"
14531 | "vector2_dot" | "vector2_cross" | "vector2_length"
14532 | "vector2_normalize" | "vector2_distance" | "vector2_rotate"
14533 | "quaternion_new" | "quaternion_from_axis_angle"
14534 | "quaternion_multiply" | "quaternion_normalize" | "quaternion_to_matrix"
14535 | "freq_to_note" | "note_to_freq" | "midi_note_to_name"
14536 | "chord_notes" | "scale_notes" | "transpose_note"
14537 | "window_tukey" | "zero_crossing_rate" | "peak_db"
14538 | "audio_normalize" | "audio_fade_in" | "audio_fade_out"
14539 | "audio_to_mono" | "audio_to_stereo"
14540 | "biquad_lowpass" | "biquad_highpass" | "biquad_bandpass" | "biquad_notch"
14541 | "oscillator_sine" | "oscillator_square"
14542 | "oscillator_sawtooth" | "oscillator_triangle"
14543 | "adsr_envelope" | "ar_envelope" | "crossfade"
14544 | "fade_curve_linear" | "fade_curve_logarithmic" | "fade_curve_exponential"
14545 | "bbox_contains" | "bbox_union" | "bbox_intersect"
14546 | "bbox_center" | "bbox_area"
14547 | "mercator_unproject" | "geohash_precision"
14548 | "jq_get" | "jq_set" | "jq_delete" | "jq_select"
14550 | "jq_keys_at" | "jq_values_at" | "jq_length_at"
14551 | "jq_type" | "jq_has" | "jq_paths" | "jq_leaf_paths"
14552 | "jq_walk" | "jq_map_values" | "jq_filter"
14553 | "jq_to_entries" | "jq_from_entries" | "jq_with_entries"
14554 | "jq_recurse" | "jq_min_by" | "jq_max_by"
14555 | "jq_sort_by" | "jq_group_by" | "jq_unique_by"
14556 | "jq_any" | "jq_all" | "jq_flatten"
14557 | "jq_index" | "jq_indices" | "jq_first" | "jq_last"
14558 | "jq_split_at" | "jq_chunks" | "jq_zip" | "jq_combinations"
14559 | "json_diff" | "json_patch" | "json_merge_patch"
14560 | "json_pointer_resolve" | "json_pointer_set"
14561 | "html_to_text" | "html_pretty" | "html_minify"
14562 | "html_sanitize" | "html_strip_tags" | "html_strip_scripts" | "html_strip_styles"
14563 | "html_extract_links" | "html_extract_images" | "html_extract_text"
14564 | "html_extract_meta" | "html_extract_title"
14565 | "html_extract_headings" | "html_extract_tables"
14566 | "html_inner_text" | "html_canonical_url"
14567 | "html_meta_charset" | "html_meta_keywords" | "html_meta_description"
14568 | "html_meta_og" | "html_meta_twitter"
14569 | "html_to_markdown" | "markdown_to_html" | "markdown_render"
14570 | "xml_pretty" | "xml_minify"
14571 | "xml_namespace" | "xml_text" | "xml_attrs"
14572 | "xml_children_by_tag" | "xml_root"
14573 | "xpath_select_one" | "xpath_attribute" | "xpath_text"
14574 | "xml_to_json" | "json_to_xml" | "xml_canonicalize"
14575 | "css_parse" | "css_minify" | "css_pretty"
14576 | "css_selector_parse" | "css_rule_extract" | "css_specificity"
14577 | "css_var_resolve" | "css_property_set" | "css_property_get"
14578 | "css_url_extract" | "css_import_extract" | "css_font_extract"
14579 | "selector_to_xpath" | "xpath_to_selector"
14580 | "http_status_continue" | "http_status_switching_protocols"
14582 | "http_status_ok" | "http_status_created" | "http_status_accepted"
14583 | "http_status_no_content" | "http_status_partial_content"
14584 | "http_status_multiple_choices" | "http_status_moved_permanently"
14585 | "http_status_found" | "http_status_see_other" | "http_status_not_modified"
14586 | "http_status_temporary_redirect" | "http_status_permanent_redirect"
14587 | "http_status_bad_request" | "http_status_unauthorized"
14588 | "http_status_payment_required" | "http_status_forbidden"
14589 | "http_status_not_found" | "http_status_method_not_allowed"
14590 | "http_status_not_acceptable" | "http_status_conflict" | "http_status_gone"
14591 | "http_status_length_required" | "http_status_precondition_failed"
14592 | "http_status_payload_too_large" | "http_status_uri_too_long"
14593 | "http_status_unsupported_media_type" | "http_status_range_not_satisfiable"
14594 | "http_status_expectation_failed" | "http_status_im_a_teapot"
14595 | "http_status_unprocessable_entity" | "http_status_too_many_requests"
14596 | "http_status_internal_server_error" | "http_status_not_implemented"
14597 | "http_status_bad_gateway" | "http_status_service_unavailable"
14598 | "http_status_gateway_timeout" | "http_status_http_version_not_supported"
14599 | "http_method_get" | "http_method_post" | "http_method_put"
14600 | "http_method_delete" | "http_method_patch" | "http_method_head"
14601 | "http_method_options" | "http_method_trace" | "http_method_connect"
14602 | "dbeta" | "qbeta" | "rbeta" | "dcauchy" | "qcauchy" | "rcauchy"
14603 | "dexp" | "qexp" | "rexp" | "dgamma" | "qgamma" | "rgamma"
14604 | "dlnorm" | "qlnorm" | "rlnorm" | "dlogis" | "qlogis" | "rlogis"
14605 | "dpois" | "qpois" | "rpois" | "dweibull" | "qweibull" | "rweibull"
14606 | "qnorm" | "rnorm" | "qunif" | "runif"
14607 | "qbinom" | "rbinom" | "qgeom" | "rgeom" | "qhyper" | "rhyper"
14608 | "qchisq" | "rchisq" | "qf" | "rf" | "qt" | "rt"
14609 | "currency_format" | "currency_parse" | "currency_round"
14611 | "currency_split_thousands" | "currency_code_to_symbol"
14612 | "currency_symbol_to_code" | "currency_convert" | "currency_rate"
14613 | "currency_iso_4217" | "currency_decimal_places"
14614 | "money_add" | "money_sub" | "money_mul" | "money_div" | "money_compare"
14615 | "tokenize_simple" | "tokenize_word" | "tokenize_subword"
14616 | "tokenize_bpe" | "tokenize_sentencepiece" | "embed_text"
14617 | "cosine_similarity" | "euclidean_distance" | "manhattan_distance"
14618 | "dot_product" | "normalize_vector"
14619 | "vector_add" | "vector_sub" | "vector_scale" | "vector_mean"
14620 | "top_k_indices" | "softmax" | "sigmoid" | "log_softmax" | "cross_entropy"
14621 | "path_canonical" | "path_relative_to" | "path_components"
14622 | "path_filename" | "path_stem" | "path_extension"
14623 | "path_join_many" | "path_with_extension" | "path_with_filename"
14624 | "path_is_subdirectory" | "path_common_ancestor" | "path_strip_prefix"
14625 | "path_glob_match_regex"
14626 | "file_mime" | "file_kind" | "file_attr_get" | "file_attr_set"
14627 | "xattr_get" | "xattr_set" | "xattr_list"
14628 | "file_chmod_string" | "file_chmod_octal" | "file_locked"
14629 | "file_acl_get" | "file_acl_set"
14630 | "locale_parse" | "locale_format" | "locale_language"
14631 | "locale_region" | "locale_script" | "locale_variant" | "locale_canonical"
14632 | "bcp47_parse" | "bcp47_format" | "bcp47_validate"
14633 | "language_tag_match" | "language_tag_subtags"
14634 | "locale_likely_subtags" | "locale_minimize" | "locale_collation"
14635 | "locale_calendar" | "locale_currency"
14636 | "locale_number_format" | "locale_date_format" | "locale_time_format"
14637 | "locale_decimal_separator" | "locale_group_separator"
14638 | "locale_first_day_of_week" | "locale_measurement_system"
14639 | "country_code_alpha2" | "country_code_alpha3" | "country_code_numeric"
14640 | "country_name" | "country_phone_prefix" | "country_currency"
14641 | "country_languages"
14642 | "language_iso_639_1" | "language_iso_639_2" | "language_iso_639_3"
14643 | "language_name"
14644 | "channel_unbounded" | "channel_bounded" | "channel_sync"
14645 | "channel_send_timeout" | "channel_recv_timeout"
14646 | "channel_try_recv" | "channel_try_send"
14647 | "channel_drain" | "channel_close" | "channel_is_closed"
14648 | "broadcast_channel_new" | "broadcast_channel_subscribe"
14649 | "broadcast_channel_publish"
14650 | "mpsc_new" | "mpmc_new" | "spmc_new" | "oneshot_new"
14651 | "mutex" | "mutex_lock" | "mutex_unlock" | "mutex_try_lock" | "mutex_is_locked"
14653 | "semaphore" | "sem"
14654 | "semaphore_acquire" | "sem_acquire"
14655 | "semaphore_release" | "sem_release"
14656 | "semaphore_try_acquire" | "sem_try_acquire"
14657 | "semaphore_permits" | "sem_permits"
14658 | "semaphore_limit" | "sem_limit"
14659 | "stress_cpu" | "scpu" | "stress_mem" | "smem"
14661 | "stress_io" | "sio" | "stress_test" | "st"
14662 | "heat" | "fire" | "fire_and_forget" | "pin"
14663 | "slurp" | "cat" | "c" | "capture" | "pager" | "pg" | "less"
14665 | "stdin"
14666 | "__stryke_rust_compile"
14668 | "vec_set_value"
14669 | "p" | "rev"
14671 | "even" | "odd" | "zero" | "nonzero"
14673 | "positive" | "pos_n" | "negative" | "neg_n"
14674 | "sign" | "negate" | "double" | "triple" | "half"
14675 | "identity" | "id"
14676 | "round" | "floor" | "ceil" | "ceiling" | "trunc" | "truncn"
14677 | "gcd" | "lcm" | "min2" | "max2"
14678 | "log2" | "log10" | "hypot"
14679 | "rad_to_deg" | "r2d" | "deg_to_rad" | "d2r"
14680 | "pow2" | "abs_diff"
14681 | "factorial" | "fact" | "fibonacci" | "fib"
14682 | "is_prime" | "is_square" | "is_power_of_two" | "is_pow2"
14683 | "cbrt" | "exp2" | "percent" | "pct" | "inverse"
14684 | "median" | "mode_val" | "variance"
14685 | "is_empty" | "is_blank" | "is_numeric"
14687 | "is_upper" | "is_lower" | "is_alpha" | "is_digit" | "is_alnum"
14688 | "is_space" | "is_whitespace"
14689 | "starts_with" | "sw" | "ends_with" | "ew" | "contains"
14690 | "capitalize" | "cap" | "swap_case" | "repeat"
14691 | "title_case" | "title" | "squish"
14692 | "pad_left" | "lpad" | "pad_right" | "rpad" | "center"
14693 | "truncate_at" | "shorten" | "reverse_str" | "rev_str"
14694 | "char_count" | "word_count" | "wc" | "line_count" | "lc_lines"
14695 | "is_array" | "is_arrayref" | "is_hash" | "is_hashref"
14697 | "is_code" | "is_coderef" | "is_ref"
14698 | "is_undef" | "is_defined" | "is_def"
14699 | "is_string" | "is_str" | "is_int" | "is_integer" | "is_float"
14700 | "invert" | "merge_hash"
14702 | "hash_map_values" | "hash_filter_keys" | "hash_filter_values"
14703 | "has_key" | "hk" | "has_any_key" | "has_all_keys"
14704 | "both" | "either" | "neither" | "xor_bool" | "bool_to_int" | "b2i"
14706 | "riffle" | "intersperse" | "every_nth"
14708 | "drop_n" | "take_n" | "rotate" | "swap_pairs"
14709 | "to_bin" | "bin_of" | "to_hex" | "hex_of" | "to_oct" | "oct_of"
14711 | "from_bin" | "from_hex" | "from_oct" | "to_base" | "from_base"
14712 | "bits_count" | "popcount" | "leading_zeros" | "lz"
14713 | "trailing_zeros" | "tz" | "bit_length" | "bitlen"
14714 | "bit_and" | "bit_or" | "bit_xor" | "bit_not"
14716 | "shift_left" | "shl" | "shift_right" | "shr"
14717 | "bit_set" | "bit_clear" | "bit_toggle" | "bit_test"
14718 | "c_to_f" | "f_to_c" | "c_to_k" | "k_to_c" | "f_to_k" | "k_to_f"
14720 | "miles_to_km" | "km_to_miles" | "miles_to_m" | "m_to_miles"
14722 | "feet_to_m" | "m_to_feet" | "inches_to_cm" | "cm_to_inches"
14723 | "yards_to_m" | "m_to_yards"
14724 | "kg_to_lbs" | "lbs_to_kg" | "g_to_oz" | "oz_to_g"
14726 | "stone_to_kg" | "kg_to_stone"
14727 | "bytes_to_kb" | "b_to_kb" | "kb_to_bytes" | "kb_to_b"
14729 | "bytes_to_mb" | "mb_to_bytes" | "bytes_to_gb" | "gb_to_bytes"
14730 | "kb_to_mb" | "mb_to_gb"
14731 | "bits_to_bytes" | "bytes_to_bits"
14732 | "seconds_to_minutes" | "s_to_m" | "minutes_to_seconds" | "m_to_s"
14734 | "seconds_to_hours" | "hours_to_seconds"
14735 | "seconds_to_days" | "days_to_seconds"
14736 | "minutes_to_hours" | "hours_to_minutes"
14737 | "hours_to_days" | "days_to_hours"
14738 | "is_leap_year" | "is_leap" | "days_in_month"
14740 | "month_name" | "month_short"
14741 | "weekday_name" | "weekday_short" | "quarter_of"
14742 | "now_ms" | "now_us" | "now_ns"
14744 | "unix_epoch" | "epoch" | "unix_epoch_ms" | "epoch_ms"
14745 | "rgb_to_hex" | "hex_to_rgb"
14747 | "ansi_red" | "ansi_green" | "ansi_yellow" | "ansi_blue"
14748 | "ansi_magenta" | "ansi_cyan" | "ansi_white" | "ansi_black"
14749 | "ansi_bold" | "ansi_dim" | "ansi_underline" | "ansi_reverse"
14750 | "strip_ansi"
14751 | "red" | "green" | "yellow" | "blue" | "magenta" | "purple" | "cyan"
14752 | "white" | "black" | "bold" | "dim" | "italic" | "underline"
14753 | "strikethrough" | "ansi_off" | "off" | "gray" | "grey"
14754 | "bright_red" | "bright_green" | "bright_yellow" | "bright_blue"
14755 | "bright_magenta" | "bright_cyan" | "bright_white"
14756 | "bg_red" | "bg_green" | "bg_yellow" | "bg_blue"
14757 | "bg_magenta" | "bg_cyan" | "bg_white" | "bg_black"
14758 | "red_bold" | "bold_red" | "green_bold" | "bold_green"
14759 | "yellow_bold" | "bold_yellow" | "blue_bold" | "bold_blue"
14760 | "magenta_bold" | "bold_magenta" | "cyan_bold" | "bold_cyan"
14761 | "white_bold" | "bold_white"
14762 | "blink" | "rapid_blink" | "hidden" | "overline"
14763 | "bg_bright_red" | "bg_bright_green" | "bg_bright_yellow" | "bg_bright_blue"
14764 | "bg_bright_magenta" | "bg_bright_cyan" | "bg_bright_white"
14765 | "rgb" | "bg_rgb" | "color256" | "c256" | "bg_color256" | "bg_c256"
14766 | "ipv4_to_int" | "int_to_ipv4"
14768 | "is_valid_ipv4" | "is_valid_ipv6" | "is_valid_email" | "is_valid_url"
14769 | "path_ext" | "path_parent" | "path_join" | "path_split"
14771 | "strip_prefix" | "strip_suffix" | "ensure_prefix" | "ensure_suffix"
14772 | "const_fn" | "always_true" | "always_false"
14774 | "flip_args" | "first_arg" | "second_arg" | "last_arg"
14775 | "count_eq" | "count_ne" | "all_eq"
14777 | "all_distinct" | "all_unique" | "has_duplicates"
14778 | "sum_of" | "product_of" | "max_of" | "min_of" | "range_of"
14779 | "quote" | "single_quote" | "unquote"
14781 | "extract_between" | "ellipsis"
14782 | "coin_flip" | "dice_roll"
14784 | "random_int" | "random_float" | "random_bool"
14785 | "random_choice" | "random_between"
14786 | "random_string" | "random_alpha" | "random_digit"
14787 | "refresh_stashes"
14789 | "os_name" | "os_arch" | "num_cpus"
14791 | "pid" | "ppid" | "uid" | "gid"
14792 | "username" | "home_dir" | "temp_dir"
14793 | "mem_total" | "mem_free" | "mem_used"
14794 | "swap_total" | "swap_free" | "swap_used"
14795 | "disk_total" | "disk_free" | "disk_avail" | "disk_used"
14796 | "load_avg" | "sys_uptime" | "page_size"
14797 | "os_version" | "os_family" | "endianness" | "pointer_width"
14798 | "proc_mem" | "rss"
14799 | "transpose" | "unzip"
14801 | "run_length_encode" | "rle" | "run_length_decode" | "rld"
14802 | "sliding_pairs" | "consecutive_eq" | "flatten_deep"
14803 | "tan" | "asin" | "acos" | "atan"
14805 | "sinh" | "cosh" | "tanh" | "asinh" | "acosh" | "atanh"
14806 | "sqr" | "cube_fn"
14807 | "mod_op" | "ceil_div" | "floor_div"
14808 | "is_finite" | "is_infinite" | "is_inf" | "is_nan"
14809 | "degrees" | "radians"
14810 | "min_abs" | "max_abs"
14811 | "saturate" | "sat01" | "wrap_around"
14812 | "rot13" | "rot47" | "caesar_shift" | "reverse_words"
14814 | "count_vowels" | "count_consonants" | "is_vowel" | "is_consonant"
14815 | "first_word" | "last_word"
14816 | "left_str" | "head_str" | "right_str" | "tail_str" | "mid_str"
14817 | "lowercase" | "uppercase"
14818 | "pascal_case" | "pc_case"
14819 | "constant_case" | "upper_snake" | "dot_case" | "path_case"
14820 | "is_palindrome" | "hamming_distance"
14821 | "longest_common_prefix" | "lcp"
14822 | "ascii_ord" | "ascii_chr" | "count_char" | "indexes_of"
14823 | "replace_first" | "replace_all_str"
14824 | "contains_any" | "contains_all"
14825 | "starts_with_any" | "ends_with_any"
14826 | "is_pair" | "is_triple"
14828 | "is_sorted" | "is_asc" | "is_sorted_desc" | "is_desc"
14829 | "is_empty_arr" | "is_empty_hash"
14830 | "is_subset" | "is_superset" | "is_permutation"
14831 | "first_eq" | "last_eq"
14833 | "index_of" | "last_index_of" | "positions_of"
14834 | "batch" | "binary_search" | "bsearch" | "linear_search" | "lsearch"
14835 | "distinct_count" | "longest" | "shortest"
14836 | "array_union" | "list_union"
14837 | "array_intersection" | "list_intersection"
14838 | "array_difference" | "list_difference"
14839 | "symmetric_diff" | "group_of_n" | "chunk_n"
14840 | "repeat_list" | "cycle_n" | "random_sample" | "sample_n"
14841 | "pick_keys" | "pick" | "omit_keys" | "omit"
14843 | "map_keys_fn" | "map_values_fn"
14844 | "hash_size" | "hash_from_pairs" | "pairs_from_hash"
14845 | "hash_eq" | "keys_sorted" | "values_sorted" | "remove_keys"
14846 | "today" | "yesterday" | "tomorrow" | "is_weekend" | "is_weekday"
14848 | "json_pretty" | "json_minify" | "escape_json" | "json_escape"
14850 | "cmd_exists" | "env_get" | "env_has" | "env_keys"
14852 | "argc" | "script_name"
14853 | "has_stdin_tty" | "has_stdout_tty" | "has_stderr_tty"
14854 | "uuid_v4" | "nanoid" | "short_id" | "is_uuid" | "token"
14856 | "email_domain" | "email_local"
14858 | "url_host" | "url_path" | "url_query" | "url_scheme"
14859 | "file_size" | "fsize" | "file_mtime" | "mtime"
14861 | "file_atime" | "atime" | "file_ctime" | "ctime"
14862 | "is_symlink" | "is_readable" | "is_writable" | "is_executable"
14863 | "path_is_abs" | "path_is_rel"
14864 | "min_max" | "percentile" | "harmonic_mean" | "geometric_mean" | "zscore"
14866 | "sorted" | "sorted_desc" | "sorted_nums" | "sorted_by_length"
14867 | "reverse_list" | "list_reverse"
14868 | "without" | "without_nth" | "take_last" | "drop_last"
14869 | "pairwise" | "zipmap"
14870 | "format_bytes" | "human_bytes"
14871 | "format_duration" | "human_duration"
14872 | "format_number" | "group_number"
14873 | "format_percent" | "pad_number"
14874 | "spaceship" | "cmp_num" | "cmp_str"
14875 | "compare_versions" | "version_cmp"
14876 | "hash_insert" | "hash_update" | "hash_delete"
14877 | "matches_regex" | "re_match"
14878 | "count_regex_matches" | "regex_extract"
14879 | "regex_split_str" | "regex_replace_str"
14880 | "shuffle_chars" | "random_char" | "nth_word"
14881 | "head_lines" | "tail_lines" | "count_substring"
14882 | "is_valid_hex" | "hex_upper" | "hex_lower"
14883 | "ms_to_s" | "s_to_ms" | "ms_to_ns" | "ns_to_ms"
14884 | "us_to_ns" | "ns_to_us"
14885 | "liters_to_gallons" | "gallons_to_liters"
14886 | "liters_to_ml" | "ml_to_liters"
14887 | "cups_to_ml" | "ml_to_cups"
14888 | "newtons_to_lbf" | "lbf_to_newtons"
14889 | "joules_to_cal" | "cal_to_joules"
14890 | "watts_to_hp" | "hp_to_watts"
14891 | "pascals_to_psi" | "psi_to_pascals"
14892 | "bar_to_pascals" | "pascals_to_bar"
14893 | "match"
14895 | "fst" | "rest" | "rst" | "second" | "snd"
14897 | "last_clj" | "lastc" | "butlast" | "bl"
14898 | "ffirst" | "ffs" | "fnext" | "fne" | "nfirst" | "nfs" | "nnext" | "nne"
14899 | "cons" | "conj"
14900 | "peek_clj" | "pkc" | "pop_clj" | "popc"
14901 | "some" | "not_any" | "not_every"
14902 | "comp" | "compose" | "partial" | "constantly" | "complement" | "compl"
14903 | "fnil" | "juxt"
14904 | "memoize" | "memo" | "curry" | "once"
14905 | "deep_clone" | "dclone" | "deep_merge" | "dmerge" | "deep_equal" | "deq"
14906 | "iterate" | "iter" | "repeatedly" | "rptd" | "cycle" | "cyc"
14907 | "mapcat" | "mcat" | "keep" | "kp" | "remove_clj" | "remc"
14908 | "reductions" | "rdcs"
14909 | "partition_by" | "pby" | "partition_all" | "pall"
14910 | "split_at" | "spat" | "split_with" | "spw"
14911 | "assoc" | "dissoc" | "get_in" | "gin" | "assoc_in" | "ain" | "update_in" | "uin"
14912 | "into" | "empty_clj" | "empc" | "seq" | "vec_clj" | "vecc"
14913 | "apply" | "appl"
14914 | "divmod" | "dm" | "accumulate" | "accum" | "starmap" | "smap"
14916 | "zip_longest" | "zipl" | "zip_fill" | "zipf" | "combinations" | "comb" | "permutations" | "perm"
14917 | "cartesian_product" | "cprod" | "compress" | "cmpr" | "filterfalse" | "falf"
14918 | "islice" | "isl" | "chain_from" | "chfr" | "pairwise_iter" | "pwi"
14919 | "tee_iter" | "teei" | "groupby_iter" | "gbi"
14920 | "each_slice" | "eslice" | "each_cons" | "econs"
14921 | "one" | "none_match" | "nonem"
14922 | "find_index_fn" | "fidx" | "rindex_fn" | "ridx"
14923 | "minmax" | "mmx" | "minmax_by" | "mmxb"
14924 | "dig" | "values_at" | "vat" | "fetch_val" | "fv" | "slice_arr" | "sla"
14925 | "transform_keys" | "tkeys" | "transform_values" | "tvals"
14926 | "sum_by" | "sumb" | "uniq_by" | "uqb"
14927 | "flat_map_fn" | "fmf" | "then_fn" | "thfn" | "times_fn" | "timf"
14928 | "step" | "upto" | "downto"
14929 | "find_last" | "fndl" | "find_last_index" | "fndli"
14931 | "at_index" | "ati" | "replace_at" | "repa"
14932 | "to_sorted" | "tsrt" | "to_reversed" | "trev" | "to_spliced" | "tspl"
14933 | "flat_depth" | "fltd" | "fill_arr" | "filla" | "includes_val" | "incv"
14934 | "object_keys" | "okeys" | "object_values" | "ovals"
14935 | "object_entries" | "oents" | "object_from_entries" | "ofents"
14936 | "span_fn" | "spanf" | "break_fn" | "brkf" | "group_runs" | "gruns"
14938 | "nub" | "sort_on" | "srton"
14939 | "intersperse_val" | "isp" | "intercalate" | "ical"
14940 | "replicate_val" | "repv" | "elem_of" | "elof" | "not_elem" | "ntelm"
14941 | "lookup_assoc" | "lkpa" | "scanl" | "scanr" | "unfoldr" | "unfr"
14942 | "find_map" | "fndm" | "filter_map" | "fltm" | "fold_right" | "fldr"
14944 | "partition_either" | "peith" | "try_fold" | "tfld"
14945 | "map_while" | "mapw" | "inspect" | "insp"
14946 | "tally_by" | "talb" | "sole" | "chunk_while" | "chkw" | "count_while" | "cntw"
14948 | "insert_at" | "insa" | "delete_at" | "dela" | "update_at" | "upda"
14950 | "split_on" | "spon" | "words_from" | "wfrm" | "unwords" | "unwds"
14951 | "lines_from" | "lfrm" | "unlines" | "unlns"
14952 | "window_n" | "winn" | "adjacent_pairs" | "adjp"
14953 | "zip_all" | "zall" | "unzip_pairs" | "uzp"
14954 | "interpose" | "ipos" | "partition_n" | "partn"
14955 | "map_indexed" | "mapi" | "reduce_indexed" | "redi" | "filter_indexed" | "flti"
14956 | "group_by_fn" | "gbf" | "index_by" | "idxb" | "associate" | "assoc_fn"
14957 | "combinations_rep" | "combrep" | "inits" | "tails" | "subsequences" | "subseqs"
14959 | "nub_by" | "nubb" | "slice_when" | "slcw" | "slice_before" | "slcb" | "slice_after" | "slca"
14960 | "each_with_object" | "ewo" | "reduce_right" | "redr"
14961 | "is_sorted_by" | "issrtb" | "intersperse_with" | "ispw"
14962 | "running_reduce" | "runred" | "windowed_circular" | "wincirc"
14963 | "distinct_by" | "distb" | "average" | "mean" | "copy_within" | "cpyw"
14964 | "and_list" | "andl" | "or_list" | "orl" | "concat_map" | "cmap"
14965 | "elem_index" | "elidx" | "elem_indices" | "elidxs" | "find_indices" | "fndidxs"
14966 | "delete_first" | "delfst" | "delete_by" | "delby" | "insert_sorted" | "inssrt"
14967 | "union_list" | "unionl" | "intersect_list" | "intl"
14968 | "maximum_by" | "maxby" | "minimum_by" | "minby" | "batched" | "btch"
14969 | "match_all" | "mall" | "capture_groups" | "capg" | "is_match" | "ism"
14971 | "split_regex" | "splre" | "replace_regex" | "replre"
14972 | "is_ascii" | "isasc" | "to_ascii" | "toasc"
14973 | "char_at" | "chat" | "code_point_at" | "cpat" | "from_code_point" | "fcp"
14974 | "normalize_spaces" | "nrmsp" | "remove_whitespace" | "rmws"
14975 | "pluralize" | "plur" | "ordinalize" | "ordn"
14976 | "parse_int" | "pint" | "parse_float" | "pflt" | "parse_bool" | "pbool"
14977 | "levenshtein" | "lev" | "soundex" | "sdx" | "similarity" | "sim"
14978 | "common_prefix" | "cpfx" | "common_suffix" | "csfx"
14979 | "wrap_text" | "wrpt" | "dedent" | "ddt" | "indent" | "idt"
14980 | "lerp" | "inv_lerp" | "ilerp" | "smoothstep" | "smst" | "remap"
14982 | "dotp" | "cross_product" | "crossp"
14983 | "matrix_mul" | "matmul" | "mm"
14984 | "magnitude" | "mag" | "normalize_vec" | "nrmv"
14985 | "distance" | "dist" | "mdist"
14986 | "covariance" | "cov" | "correlation" | "corr"
14987 | "iqr" | "quantile" | "qntl" | "quantiles" | "qntls"
14988 | "lsp_completion_words" | "lsp_words"
14989 | "doctor" | "health"
14990 | "clamp_int" | "clpi"
14991 | "in_range" | "inrng" | "wrap_range" | "wrprng"
14992 | "sum_squares" | "sumsq" | "rms" | "cumsum" | "csum" | "cumprod" | "cprod_acc" | "diff"
14993 | "add_days" | "addd" | "add_hours" | "addh" | "add_minutes" | "addm"
14995 | "diff_days" | "diffd" | "diff_hours" | "diffh"
14996 | "start_of_day" | "sod" | "end_of_day" | "eod"
14997 | "start_of_hour" | "soh" | "start_of_minute" | "som"
14998 | "urle" | "urld"
15000 | "html_encode" | "htmle" | "html_decode" | "htmld"
15001 | "adler32" | "adl32" | "fnv1a" | "djb2"
15002 | "is_credit_card" | "iscc" | "is_isbn10" | "isbn10" | "is_isbn13" | "isbn13"
15004 | "is_iban" | "isiban" | "is_hex_str" | "ishex" | "is_binary_str" | "isbin"
15005 | "is_octal_str" | "isoct" | "is_json" | "isjson" | "is_base64" | "isb64"
15006 | "is_semver" | "issv" | "is_slug" | "isslug" | "slugify" | "slug"
15007 | "mode_stat" | "mstat" | "sampn" | "weighted_sample" | "wsamp"
15009 | "shuffle_arr" | "shuf" | "argmax" | "amax" | "argmin" | "amin"
15010 | "argsort" | "asrt" | "rank" | "rnk" | "dense_rank" | "drnk"
15011 | "partition_point" | "ppt" | "lower_bound" | "lbound"
15012 | "upper_bound" | "ubound" | "equal_range" | "eqrng"
15013 | "matrix_add" | "madd" | "matrix_sub" | "msub" | "matrix_mult" | "mmult"
15015 | "matrix_scalar" | "mscal" | "matrix_identity" | "mident"
15016 | "matrix_zeros" | "mzeros" | "matrix_ones" | "mones"
15017 | "matrix_diag" | "mdiag" | "matrix_trace" | "mtrace"
15018 | "matrix_row" | "mrow" | "matrix_col" | "mcol"
15019 | "matrix_shape" | "mshape" | "matrix_det" | "mdet"
15020 | "matrix_scale" | "mat_scale" | "diagonal" | "diag"
15021 | "topological_sort" | "toposort" | "bfs_traverse" | "bfs"
15023 | "dfs_traverse" | "dfs" | "shortest_path_bfs" | "spbfs"
15024 | "connected_components_graph" | "ccgraph"
15025 | "has_cycle_graph" | "hascyc" | "is_bipartite_graph" | "isbip"
15026 | "is_ipv4_addr" | "isip4" | "is_ipv6_addr" | "isip6"
15028 | "is_mac_addr" | "ismac" | "is_port_num" | "isport"
15029 | "is_hostname_valid" | "ishost"
15030 | "is_iso_date" | "isisodt" | "is_iso_time" | "isisotm"
15031 | "is_iso_datetime" | "isisodtm"
15032 | "is_phone_num" | "isphone" | "is_us_zip" | "iszip"
15033 | "word_wrap_text" | "wwrap" | "center_text" | "ctxt"
15035 | "ljust_text" | "ljt" | "rjust_text" | "rjt" | "zfill_num" | "zfill"
15036 | "remove_all_str" | "rmall" | "replace_n_times" | "repln"
15037 | "find_all_indices" | "fndalli"
15038 | "text_between" | "txbtwn" | "text_before" | "txbef" | "text_after" | "txaft"
15039 | "text_before_last" | "txbefl" | "text_after_last" | "txaftl"
15040 | "is_even_num" | "iseven" | "is_odd_num" | "isodd"
15042 | "is_positive_num" | "ispos" | "is_negative_num" | "isneg"
15043 | "is_zero_num" | "iszero" | "is_whole_num" | "iswhole"
15044 | "log_with_base" | "logb" | "nth_root_of" | "nroot"
15045 | "frac_part" | "fracp" | "reciprocal_of" | "recip"
15046 | "copy_sign" | "cpsgn" | "fused_mul_add" | "fmadd"
15047 | "floor_mod" | "fmod" | "floor_div_op" | "fdivop"
15048 | "signum_of" | "sgnum" | "midpoint_of" | "midpt"
15049 | "longest_run" | "lrun" | "longest_increasing" | "linc"
15051 | "longest_decreasing" | "ldec" | "max_sum_subarray" | "maxsub"
15052 | "majority_element" | "majority" | "kth_largest" | "kthl"
15053 | "kth_smallest" | "kths" | "count_inversions" | "cinv"
15054 | "is_monotonic" | "ismono" | "equilibrium_index" | "eqidx"
15055 | "jaccard_index" | "jaccard" | "dice_coefficient" | "dicecoef"
15057 | "overlap_coefficient" | "overlapcoef"
15058 | "power_set" | "powerset" | "cartesian_power" | "cartpow"
15059 | "is_isogram" | "isiso" | "is_heterogram" | "ishet"
15061 | "hamdist" | "jaro_similarity" | "jarosim"
15062 | "longest_common_substring" | "lcsub"
15063 | "longest_common_subsequence" | "lcseq"
15064 | "count_words" | "wcount" | "count_lines" | "lcount"
15065 | "count_chars" | "ccount" | "count_bytes" | "bcount"
15066 | "binomial" | "binom" | "catalan" | "catn" | "pascal_row" | "pascrow"
15068 | "is_coprime" | "iscopr" | "euler_totient" | "etot"
15069 | "mobius" | "mob" | "is_squarefree" | "issqfr"
15070 | "digital_root" | "digroot" | "is_narcissistic" | "isnarc"
15071 | "is_harshad" | "isharsh" | "is_kaprekar" | "iskap"
15072 | "day_of_year" | "doy" | "week_of_year" | "woy"
15074 | "days_in_month_fn" | "daysinmo" | "is_valid_date" | "isvdate"
15075 | "age_in_years" | "ageyrs"
15076 | "when_true" | "when_false" | "if_else" | "clamp_fn"
15079 | "attempt" | "try_fn" | "safe_div" | "safe_mod" | "safe_sqrt" | "safe_log"
15080 | "juxt2" | "juxt3" | "tap_val" | "debug_val" | "converge"
15081 | "iterate_n" | "unfold" | "arity_of" | "is_callable"
15082 | "coalesce" | "default_to" | "fallback"
15083 | "apply_list" | "zip_apply" | "scan"
15084 | "keep_if" | "reject_if" | "group_consecutive"
15085 | "after_n" | "before_n" | "clamp_list" | "normalize_list"
15086
15087 | "matrix_multiply" | "mat_mul"
15091 | "identity_matrix" | "eye" | "zeros_matrix" | "zeros" | "ones_matrix" | "ones"
15092
15093
15094
15095 | "vec_normalize" | "unit_vec" | "vec_add" | "vec_sub" | "vec_scale"
15096 | "linspace" | "arange"
15097 | "re_test" | "re_find_all" | "re_groups" | "re_escape"
15099 | "re_split_limit" | "glob_to_regex" | "is_regex_valid"
15100 | "cwd" | "pwd_str" | "cpu_count" | "is_root" | "uptime_secs"
15102 | "env_pairs" | "env_set" | "env_remove" | "hostname_str" | "is_tty" | "signal_name"
15103 | "stack_new" | "queue_new" | "lru_new"
15105 | "counter" | "counter_most_common" | "defaultdict" | "ordered_set"
15106 | "bitset_new" | "bitset_set" | "bitset_test" | "bitset_clear"
15107 | "abs_ceil" | "abs_each" | "abs_floor" | "ceil_each" | "dec_each"
15109 | "double_each" | "floor_each" | "half_each" | "inc_each" | "length_each"
15110 | "negate_each" | "not_each" | "offset_each" | "reverse_each" | "round_each"
15111 | "scale_each" | "sqrt_each" | "square_each" | "to_float_each" | "to_int_each"
15112 | "trim_each" | "type_each" | "upcase_each" | "downcase_each" | "bool_each"
15113 | "avogadro" | "boltzmann" | "golden_ratio" | "gravity" | "ln10" | "ln2"
15115 | "planck" | "speed_of_light" | "sqrt2"
15116 | "bmi_calc" | "compound_interest" | "dew_point" | "discount_amount"
15118 | "force_mass_acc" | "freq_wavelength" | "future_value" | "haversine"
15119 | "heat_index" | "kinetic_energy" | "margin_price" | "markup_price"
15120 | "mortgage_payment" | "ohms_law_i" | "ohms_law_r" | "ohms_law_v"
15121 | "potential_energy" | "present_value" | "simple_interest" | "speed_distance_time"
15122 | "tax_amount" | "tip_amount" | "wavelength_freq" | "wind_chill"
15123 | "angle_between_deg" | "approx_eq" | "chebyshev_distance" | "copysign"
15125 | "cube_root" | "entropy" | "float_bits" | "fma"
15126 | "int_bits" | "jaccard_similarity" | "log_base" | "mae" | "mse" | "nth_root"
15127 | "r_squared" | "reciprocal" | "relu" | "rmse" | "rotate_point" | "round_to"
15128 | "signum" | "square_root"
15129 | "cubes_seq" | "fibonacci_seq" | "powers_of_seq" | "primes_seq"
15131 | "squares_seq" | "triangular_seq"
15132 | "alternate_case" | "angle_bracket" | "bracket" | "byte_length"
15134 | "bytes_to_hex_str" | "camel_words" | "char_length" | "chars_to_string"
15135 | "chomp_str" | "chop_str" | "filter_chars" | "from_csv_line" | "hex_to_bytes"
15136 | "insert_str" | "intersperse_char" | "ljust" | "map_chars" | "mirror_string"
15137 | "normalize_whitespace" | "only_alnum" | "only_alpha" | "only_ascii"
15138 | "only_digits" | "parenthesize" | "remove_str" | "repeat_string" | "rjust"
15139 | "sentence_case" | "string_count" | "string_sort" | "string_to_chars"
15140 | "string_unique_chars" | "substring" | "to_csv_line" | "trim_left" | "trim_right"
15141 | "xor_strings"
15142 | "adjacent_difference" | "append_elem" | "consecutive_pairs" | "contains_elem"
15144 | "count_elem" | "drop_every" | "duplicate_count" | "elem_at" | "find_first"
15145 | "first_elem" | "flatten_once" | "fold_left" | "from_digits" | "from_pairs"
15146 | "group_by_size" | "hash_from_list"
15147 | "hash_merge_deep" | "hash_to_list" | "hash_zip" | "head_n" | "histogram_bins"
15148 | "index_of_elem" | "init_list" | "interleave_lists" | "last_elem" | "least_common"
15149 | "list_compact" | "list_eq" | "list_flatten_deep" | "max_list" | "mean_list"
15150 | "min_list" | "mode_list" | "most_common" | "partition_two" | "prefix_sums"
15151 | "prepend" | "product_list" | "remove_at" | "remove_elem" | "remove_first_elem"
15152 | "repeat_elem" | "running_max" | "running_min" | "sample_one" | "scan_left"
15153 | "second_elem" | "span" | "suffix_sums" | "sum_list" | "tail_n" | "take_every"
15154 | "third_elem" | "to_array" | "to_pairs" | "trimmed_mean" | "unique_count_of"
15155 | "wrap_index" | "digits_of"
15156 | "all_match" | "any_match" | "is_between" | "is_blank_or_nil" | "is_divisible_by"
15158 | "is_email" | "is_even" | "is_falsy" | "is_fibonacci" | "is_hex_color"
15159 | "is_in_range" | "is_ipv4" | "is_multiple_of" | "is_negative" | "is_nil"
15160 | "is_nonzero" | "is_odd" | "is_perfect_square" | "is_positive" | "is_power_of"
15161 | "is_prefix" | "is_present" | "is_strictly_decreasing" | "is_strictly_increasing"
15162 | "is_suffix" | "is_triangular" | "is_truthy" | "is_url" | "is_whole" | "is_zero"
15163 | "count_digits" | "count_letters" | "count_lower" | "count_match"
15165 | "count_punctuation" | "count_spaces" | "count_upper" | "defined_count"
15166 | "empty_count" | "falsy_count" | "nonempty_count" | "numeric_count"
15167 | "truthy_count" | "undef_count"
15168 | "assert_type" | "between" | "clamp_each" | "die_if" | "die_unless"
15170 | "join_colons" | "join_commas" | "join_dashes" | "join_dots" | "join_lines"
15171 | "join_pipes" | "join_slashes" | "join_spaces" | "join_tabs" | "measure"
15172 | "max_float" | "min_float" | "noop_val" | "nop" | "pass" | "pred" | "succ"
15173 | "tap_debug" | "to_bool" | "to_float" | "to_int" | "to_string" | "void"
15174 | "range_exclusive" | "range_inclusive"
15175 | "aliquot_sum" | "autocorrelation" | "bell_number" | "cagr" | "coeff_of_variation"
15177 | "collatz_length" | "collatz_sequence" | "convolution"
15178 | "depreciation_double" | "depreciation_linear" | "discount" | "divisors"
15179 | "epsilon" | "euler_number" | "exponential_moving_average"
15180 | "f64_max" | "f64_min" | "fft_magnitude" | "goldbach" | "i64_max" | "i64_min"
15181 | "kurtosis" | "linear_regression" | "look_and_say" | "lucas" | "luhn_check"
15182 | "mean_absolute_error" | "mean_squared_error" | "median_absolute_deviation"
15183 | "minkowski_distance" | "moving_average" | "multinomial" | "neg_inf" | "npv"
15184 | "num_divisors" | "partition_number" | "pascals_triangle" | "skewness"
15185 | "standard_error" | "subfactorial" | "sum_divisors" | "totient_sum"
15186 | "tribonacci" | "weighted_mean" | "winsorize"
15187 | "chi_square_stat" | "describe" | "five_number_summary"
15189 | "gini" | "gini_coefficient" | "lorenz_curve" | "outliers_iqr"
15190 | "percentile_rank" | "quartiles" | "sample_stddev" | "sample_variance"
15191 | "spearman_correlation" | "t_test_one_sample" | "t_test_two_sample"
15192 | "z_score" | "z_scores"
15193 | "abundant_numbers" | "deficient_numbers" | "is_abundant" | "is_deficient"
15195 | "is_pentagonal" | "is_perfect" | "is_smith" | "next_prime" | "nth_prime"
15196 | "pentagonal_number" | "perfect_numbers" | "prev_prime" | "prime_factors"
15197 | "prime_pi" | "primes_up_to" | "triangular_number" | "twin_primes"
15198 | "area_circle" | "area_ellipse" | "area_rectangle" | "area_trapezoid" | "area_triangle"
15200 | "bearing" | "circumference" | "cone_volume" | "cylinder_volume" | "heron_area"
15201 | "midpoint" | "perimeter_rectangle" | "perimeter_triangle" | "point_distance"
15202 | "polygon_area" | "slope" | "sphere_surface" | "sphere_volume" | "triangle_hypotenuse"
15203 | "angle_between" | "arc_length" | "bounding_box" | "centroid"
15205 | "circle_from_three_points" | "circ3" | "convex_hull" | "ellipse_perimeter" | "ellper"
15206 | "frustum_volume" | "haversine_distance" | "line_intersection"
15207 | "point_in_polygon" | "pip" | "polygon_perimeter" | "polyper" | "pyramid_volume"
15208 | "reflect_point" | "scale_point" | "sector_area"
15209 | "torus_surface" | "torus_volume" | "translate_point"
15210 | "vector_angle" | "vector_cross" | "vector_dot" | "vector_magnitude" | "vector_normalize"
15211 | "avogadro_number" | "boltzmann_constant" | "electron_mass" | "elementary_charge"
15213 | "gravitational_constant" | "phi" | "pi" | "PI" | "planck_constant"
15214 | "proton_mass" | "sol" | "tau" | "TAU" | "E"
15215 | "bac_estimate" | "bmi" | "break_even" | "margin" | "markup" | "roi" | "tax" | "tip"
15217 | "amortization_schedule" | "black_scholes_call" | "black_scholes_put"
15219 | "bond_price" | "bond_yield" | "capm" | "continuous_compound" | "ccomp"
15220 | "discounted_payback" | "duration" | "irr"
15221 | "max_drawdown" | "mdd" | "modified_duration" | "mod_dur" | "nper" | "num_periods" | "payback_period"
15222 | "pmt" | "pv" | "rule_of_72" | "sharpe_ratio" | "sortino_ratio"
15223 | "wacc" | "xirr"
15224 | "acronym" | "atbash" | "bigrams" | "camel_to_snake" | "char_frequencies"
15226 | "chunk_string" | "collapse_whitespace" | "dedent_text" | "indent_text"
15227 | "initials" | "leetspeak" | "mask_string" | "ngrams" | "pig_latin"
15228 | "remove_consonants" | "remove_vowels" | "reverse_each_word" | "snake_to_camel"
15229 | "sort_words" | "string_distance" | "string_multiply" | "strip_html"
15230 | "trigrams" | "unique_words" | "word_frequencies" | "zalgo"
15231 | "braille_encode" | "double_metaphone" | "metaphone" | "morse_decode"
15233 | "morse_encode" | "nato_phonetic" | "phonetic_digit" | "subscript" | "superscript"
15234 | "to_emoji_num"
15235 | "int_to_roman" | "roman_add" | "roman_numeral_list" | "roman_to_int"
15237 | "base_convert" | "binary_to_gray" | "gray_code_sequence" | "gray_to_binary"
15239 | "ansi_256" | "ansi_truecolor" | "color_blend" | "color_complement"
15241 | "color_darken" | "color_distance" | "color_grayscale" | "color_invert"
15242 | "color_lighten" | "hsl_to_rgb" | "hsv_to_rgb" | "random_color"
15243 | "rgb_to_hsl" | "rgb_to_hsv"
15244 | "matrix_flatten" | "matrix_from_rows" | "matrix_hadamard" | "matrix_inverse"
15246 | "matrix_map" | "matrix_max" | "matrix_min" | "matrix_power" | "matrix_sum"
15247 | "matrix_transpose"
15248 | "binary_insert" | "bucket" | "clamp_array" | "group_consecutive_by"
15250 | "histogram" | "merge_sorted" | "next_permutation" | "normalize_array"
15251 | "normalize_range" | "peak_detect" | "range_compress" | "range_expand"
15252 | "reservoir_sample" | "run_length_decode_str" | "run_length_encode_str"
15253 | "zero_crossings"
15254 | "apply_window" | "bandpass_filter" | "cross_correlation" | "dft"
15256 | "downsample" | "decimate" | "energy" | "envelope" | "hilbert_env" | "highpass_filter" | "idft"
15257 | "lowpass_filter" | "median_filter" | "normalize_signal" | "phase_spectrum"
15258 | "power_spectrum" | "psd" | "resample" | "spectral_centroid" | "spectrogram" | "stft" | "upsample" | "interpolate"
15259 | "window_blackman" | "window_hamming" | "window_hann" | "window_kaiser"
15260 | "is_anagram" | "is_balanced_parens" | "is_control" | "is_numeric_string"
15262 | "is_pangram" | "is_printable" | "is_valid_cidr" | "is_valid_cron"
15263 | "is_valid_hex_color" | "is_valid_latitude" | "is_valid_longitude" | "is_valid_mime"
15264 | "eval_rpn" | "fizzbuzz" | "game_of_life_step" | "mandelbrot_char"
15266 | "sierpinski" | "tower_of_hanoi" | "truth_table"
15267 | "byte_size" | "degrees_to_compass" | "to_string_val" | "type_of"
15269 | "quadratic_roots" | "quadratic_discriminant" | "arithmetic_series"
15271 | "geometric_series" | "stirling_approx"
15272 | "double_factorial" | "rising_factorial" | "falling_factorial"
15273 | "gamma_approx" | "erf_approx" | "normal_pdf" | "normal_cdf"
15274 | "poisson_pmf" | "exponential_pdf" | "inverse_lerp"
15275 | "map_range"
15276 | "momentum" | "impulse" | "work" | "power_phys" | "torque" | "angular_velocity"
15278 | "centripetal_force" | "escape_velocity" | "orbital_velocity" | "orbital_period"
15279 | "gravitational_force" | "coulomb_force" | "electric_field" | "capacitance"
15280 | "capacitor_energy" | "inductor_energy" | "resonant_frequency"
15281 | "rc_time_constant" | "rl_time_constant" | "impedance_rlc"
15282 | "relativistic_mass" | "lorentz_factor" | "time_dilation" | "length_contraction"
15283 | "relativistic_energy" | "rest_energy" | "de_broglie_wavelength"
15284 | "photon_energy" | "photon_energy_wavelength" | "schwarzschild_radius"
15285 | "stefan_boltzmann" | "wien_displacement" | "ideal_gas_pressure" | "ideal_gas_volume"
15286 | "projectile_range" | "projectile_max_height" | "projectile_time"
15287 | "spring_force" | "spring_energy" | "pendulum_period" | "doppler_frequency"
15288 | "decibel_ratio" | "snells_law" | "brewster_angle" | "critical_angle"
15289 | "lens_power" | "thin_lens" | "magnification_lens"
15290 | "euler_mascheroni" | "apery_constant" | "feigenbaum_delta" | "feigenbaum_alpha"
15292 | "catalan_constant" | "khinchin_constant" | "glaisher_constant"
15293 | "plastic_number" | "silver_ratio" | "supergolden_ratio"
15294 | "vacuum_permittivity" | "vacuum_permeability" | "coulomb_constant"
15296 | "fine_structure_constant" | "rydberg_constant" | "bohr_radius"
15297 | "bohr_magneton" | "nuclear_magneton" | "stefan_boltzmann_constant"
15298 | "wien_constant" | "gas_constant" | "faraday_constant" | "neutron_mass"
15299 | "atomic_mass_unit" | "earth_mass" | "earth_radius" | "sun_mass" | "sun_radius"
15300 | "astronomical_unit" | "light_year" | "parsec" | "hubble_constant"
15301 | "planck_length" | "planck_time" | "planck_mass" | "planck_temperature"
15302 | "matrix_solve" | "msolve" | "solve"
15304 | "matrix_lu" | "mlu" | "matrix_qr" | "mqr"
15305 | "matrix_eigenvalues" | "meig" | "eigenvalues" | "eig"
15306 | "matrix_norm" | "mnorm" | "matrix_cond" | "mcond" | "cond"
15307 | "matrix_pinv" | "mpinv" | "pinv"
15308 | "matrix_cholesky" | "mchol" | "cholesky"
15309 | "matrix_det_general" | "mdetg" | "det"
15310 | "welch_ttest" | "welcht" | "paired_ttest" | "pairedt"
15312 | "cohen_d" | "cohend" | "anova_oneway" | "anova" | "anova1"
15313 | "spearman_corr" | "rho" | "kendall_tau" | "kendall" | "ktau"
15314 | "confidence_interval" | "ci"
15315 | "beta_pdf" | "betapdf" | "gamma_pdf" | "gammapdf"
15317 | "chi2_pdf" | "chi2pdf" | "chi_squared_pdf"
15318 | "t_pdf" | "tpdf" | "student_pdf"
15319 | "f_pdf" | "fpdf" | "fisher_pdf"
15320 | "lognormal_pdf" | "lnormpdf" | "weibull_pdf" | "weibpdf"
15321 | "cauchy_pdf" | "cauchypdf" | "laplace_pdf" | "laplacepdf"
15322 | "pareto_pdf" | "paretopdf"
15323 | "lagrange_interp" | "lagrange" | "linterp"
15325 | "cubic_spline" | "cspline" | "spline"
15326 | "poly_eval" | "polyval" | "polynomial_fit" | "polyfit"
15327 | "trapz" | "trapezoid" | "simpson" | "simps"
15329 | "numerical_diff" | "numdiff" | "diff_array"
15330 | "cumtrapz" | "cumulative_trapz"
15331 | "bisection" | "bisect" | "newton_method" | "newton" | "newton_raphson"
15333 | "golden_section" | "golden" | "gss"
15334 | "rk4" | "runge_kutta" | "rk4_ode" | "euler_ode" | "euler_method"
15336 | "dijkstra" | "shortest_path" | "bellman_ford" | "bellmanford"
15338 | "floyd_warshall" | "floydwarshall" | "apsp"
15339 | "prim_mst" | "mst" | "prim"
15340 | "cot" | "sec" | "csc" | "acot" | "asec" | "acsc" | "sinc" | "versin" | "versine"
15342 | "leaky_relu" | "lrelu" | "elu" | "selu" | "gelu"
15344 | "silu" | "swish" | "mish" | "softplus"
15345 | "hard_sigmoid" | "hardsigmoid" | "hard_swish" | "hardswish"
15346 | "bessel_j0" | "j0" | "bessel_j1" | "j1"
15348 | "lambert_w" | "lambertw" | "productlog"
15349 | "bessel_j" | "bessel_y" | "bessel_i" | "bessel_k"
15351 | "hankel_h1" | "hankel_h2" | "bessel_j_zero"
15352 | "airy_ai" | "airy_bi" | "airy_ai_prime" | "airy_bi_prime"
15353 | "spherical_bessel_j" | "spherical_bessel_y"
15354 | "struve_h" | "struve_l" | "kelvin_ber" | "kelvin_bei"
15355 | "legendre_p" | "legendre_q" | "assoc_legendre_p"
15357 | "hermite_h" | "hermite_he" | "laguerre_l" | "assoc_laguerre_l"
15358 | "jacobi_p" | "gegenbauer_c" | "chebyshev_t" | "chebyshev_u"
15359 | "spherical_harmonic_y" | "zernike_r"
15360 | "elliptic_k" | "elliptic_e" | "elliptic_pi" | "elliptic_f"
15362 | "elliptic_e_inc" | "elliptic_pi_inc"
15363 | "carlson_rf" | "carlson_rd" | "carlson_rj"
15364 | "jacobi_sn" | "jacobi_cn" | "jacobi_dn" | "jacobi_am"
15365 | "elliptic_theta"
15366 | "weierstrass_p" | "weierstrass_zeta" | "weierstrass_sigma"
15367 | "zeta" | "riemann_zeta" | "hurwitz_zeta"
15369 | "polylog" | "dilog" | "lerch_phi"
15370 | "riemann_siegel_z" | "riemann_siegel_theta"
15371 | "dirichlet_eta" | "dirichlet_beta"
15372 | "hypergeometric_2f1" | "hyper_2f1"
15374 | "hypergeometric_1f1" | "hyper_1f1" | "kummer_m"
15375 | "hypergeometric_0f1" | "hyper_0f1"
15376 | "hypergeometric_pfq" | "hyper_pfq"
15377 | "hypergeometric_u" | "tricomi_u"
15378 | "dedekind_eta" | "klein_j" | "klein_invariant_j"
15380 | "modular_lambda" | "ramanujan_tau"
15381 | "sin_integral" | "si_int" | "cos_integral" | "ci_int"
15383 | "sinh_integral" | "shi_int" | "cosh_integral" | "chi_int"
15384 | "exp_integral_e" | "ei_n" | "exp_integral_ei" | "ei_int"
15385 | "log_integral" | "li_int" | "fresnel_s" | "fresnel_c"
15386 | "jacobi_symbol" | "kronecker_symbol"
15388 | "primitive_root" | "multiplicative_order"
15389 | "mangoldt_lambda" | "von_mangoldt" | "carmichael_lambda"
15390 | "squares_r" | "thue_morse" | "rudin_shapiro"
15391 | "farey_sequence" | "farey"
15392 | "frobenius_number" | "frobenius_solve" | "stern_brocot"
15393 | "stirling_s1" | "stirling_first" | "bell_polynomial_b" | "bell_y"
15395 | "clebsch_gordan" | "three_j_symbol" | "wigner_3j"
15396 | "six_j_symbol" | "wigner_6j" | "nine_j_symbol" | "wigner_9j"
15397 | "debruijn_sequence" | "debruijn" | "wigner_d"
15398 | "q_pochhammer" | "q_factorial" | "q_binomial"
15400 | "q_hypergeometric_pfq"
15401 | "mittag_leffler_e" | "mittag_leffler"
15402 | "coulomb_wave_f" | "coulomb_wave_g"
15403 | "inverse_erf" | "erfinv" | "inverse_erfc" | "erfcinv"
15405 | "inverse_gamma_regularized" | "gamma_lr_inv"
15406 | "inverse_beta_regularized" | "beta_reg_inv"
15407 | "inverse_jacobi_sn"
15408 | "dirac_delta" | "heaviside_theta" | "heaviside"
15410 | "unit_box" | "unit_triangle"
15411 | "square_wave" | "triangle_wave" | "sawtooth_wave" | "dirac_comb"
15412 | "liouville_lambda" | "jordan_totient" | "ramanujan_sum"
15414 | "cyclotomic_polynomial" | "cyclotomic" | "legendre_symbol"
15415 | "pythagorean_triple_q" | "gen_pythagorean_triple"
15416 | "sophie_germain_q" | "mersenne_q"
15417 | "lucas_lehmer_test" | "lucas_lehmer"
15418 | "continued_fraction" | "from_continued_fraction" | "convergents"
15419 | "best_rational_approximation" | "best_rational"
15420 | "motzkin_number" | "motzkin"
15422 | "narayana_number" | "narayana"
15423 | "delannoy_number" | "delannoy"
15424 | "schroder_number" | "schroder" | "large_schroder"
15425 | "small_schroder_number" | "small_schroder"
15426 | "eulerian_number"
15427 | "bernoulli_polynomial" | "euler_polynomial"
15428 | "pell_number" | "pell" | "pell_lucas_number" | "pell_lucas"
15429 | "perrin_number" | "perrin" | "padovan_number" | "padovan"
15430 | "kronecker_product" | "tensor_product" | "tensor_contract"
15432 | "matrix_rank" | "mrank"
15433 | "companion_matrix" | "companion"
15434 | "characteristic_polynomial" | "charpoly"
15435 | "singular_values" | "svals"
15436 | "nullspace" | "null_space" | "kernel"
15437 | "polynomial_gcd" | "polygcd"
15439 | "polynomial_quotient" | "polyquot"
15440 | "polynomial_remainder" | "polyrem"
15441 | "polynomial_resultant" | "resultant"
15442 | "polynomial_discriminant" | "discriminant"
15443 | "polynomial_roots" | "polyroots"
15444 | "gumbel_pdf" | "gumbel_cdf" | "gumbel_quantile"
15446 | "frechet_pdf" | "frechet_cdf" | "frechet_quantile"
15447 | "logistic_pdf" | "logistic_cdf" | "logistic_quantile"
15448 | "rayleigh_pdf" | "rayleigh_cdf" | "rayleigh_quantile"
15449 | "inverse_gamma_pdf" | "inverse_gamma_cdf" | "inverse_gamma_quantile"
15450 | "kumaraswamy_pdf" | "kumaraswamy_cdf" | "kumaraswamy_quantile"
15451 | "mathieu_a" | "mathieu_characteristic_a"
15453 | "mathieu_ce" | "mathieu_se"
15454 | "heun_g"
15456 | "haar_transform" | "haar" | "haar_inverse" | "ihaar"
15458 | "daubechies_db4" | "db4" | "daubechies_db4_inverse" | "idb4"
15459 | "topo_sort_adj"
15461 | "scc_tarjan" | "tarjan_scc" | "strongly_connected"
15462 | "bipartite_q" | "is_bipartite"
15463 | "max_flow_edmonds_karp" | "max_flow" | "edmonds_karp"
15464 | "min_cut" | "eccentricity"
15465 | "graph_diameter" | "graph_radius"
15466 | "stieltjes_constant" | "stieltjes"
15468 | "gauss_sum" | "kloosterman_sum"
15469 | "eta_quotient" | "root_approximant"
15470 | "numerical_gradient" | "ngrad"
15472 | "numerical_jacobian" | "njac"
15473 | "numerical_hessian" | "nhess"
15474 | "numerical_divergence" | "ndiv"
15475 | "numerical_curl" | "ncurl"
15476 | "numerical_laplacian" | "nlap"
15477 | "nelder_mead" | "simplex_min"
15479 | "gradient_descent" | "gd_min"
15480 | "bfgs_minimize" | "bfgs"
15481 | "levenberg_marquardt" | "lev_marq" | "lm_min"
15482 | "conjugate_gradient" | "cg_solve"
15483 | "least_squares" | "lstsq"
15484 | "romberg" | "romberg_int"
15486 | "gauss_legendre_quad" | "glquad" | "gl_quad"
15487 | "monte_carlo_integrate" | "mc_int"
15488 | "adaptive_simpson" | "asimp"
15489 | "lu_decompose" | "ludec"
15491 | "qr_decompose" | "qrdec"
15492 | "householder_reflector" | "householder"
15493 | "givens_rotation" | "givens"
15494 | "forward_substitute" | "fwdsub"
15495 | "back_substitute" | "backsub"
15496 | "hessenberg_reduce" | "hessen"
15497 | "poly_derivative" | "polyder"
15499 | "poly_integrate" | "polyint"
15500 | "poly_compose" | "poly_eval_horner" | "horner"
15501 | "pade_approximant" | "pade"
15502 | "quat_mul" | "quat_conj" | "quat_norm" | "quat_inv"
15504 | "quat_from_axis_angle" | "axis_angle_to_quat"
15505 | "quat_to_axis_angle"
15506 | "quat_to_matrix" | "quat_from_matrix" | "matrix_to_quat"
15507 | "quat_slerp" | "slerp"
15508 | "euler_zyx_to_matrix" | "matrix_to_euler_zyx"
15509 | "rotate_3d_vec"
15510 | "kl_divergence" | "kl_div"
15512 | "js_divergence" | "js_div"
15513 | "mutual_information" | "mi"
15514 | "cross_entropy_arr" | "cross_entropy_dist"
15515 | "renyi_entropy" | "tsallis_entropy"
15516 | "pauli_x" | "pauli_y" | "pauli_z"
15518 | "pauli_id" | "pauli_i" | "pauli_identity"
15519 | "ket_bra" | "density_matrix" | "expectation_value" | "expval"
15520 | "commutator" | "anticommutator"
15521 | "partial_trace" | "ptrace"
15522 | "von_neumann_entropy" | "vn_entropy"
15523 | "bose_einstein" | "fermi_dirac"
15525 | "maxwell_boltzmann_speed" | "mb_speed"
15526 | "partition_function" | "z_partition"
15527 | "helmholtz_free_energy" | "free_energy_f"
15528 | "boltzmann_factor"
15529 | "einstein_specific_heat" | "einstein_cv"
15530 | "fresnel_reflection_te" | "fresnel_reflection_tm"
15532 | "fresnel_transmission_te" | "fresnel_transmission_tm"
15533 | "abcd_thin_lens" | "abcd_free_space"
15534 | "gaussian_beam_q"
15535 | "kepler_solve"
15537 | "true_to_eccentric" | "eccentric_to_mean"
15538 | "julian_date" | "j_date"
15539 | "jd_to_gregorian" | "jd_to_date"
15540 | "sidereal_time_gmst" | "gmst"
15541 | "vis_viva" | "orbital_period_kepler"
15542 | "orbital_elements_to_state" | "elem_to_state"
15543 | "kalman_step" | "kalman_filter"
15545 | "exponential_smoothing" | "exp_smooth"
15546 | "holt_winters" | "arma_yw_fit" | "ar_yw"
15547 | "pagerank" | "betweenness_centrality" | "closeness_centrality"
15549 | "eigenvector_centrality" | "degree_centrality" | "triangle_count"
15550 | "rgumbel" | "rfrechet" | "rrayleigh"
15552 | "rlogistic" | "rkumaraswamy" | "rinverse_gamma" | "rinvgamma"
15553 | "graham_scan" | "convex_hull_2d"
15555 | "line_line_intersect_2d" | "ll_intersect_2d"
15556 | "point_segment_distance" | "p_seg_dist"
15557 | "forward_diff" | "fdiff"
15559 | "forward_diff_grad" | "fdiff_grad"
15560 | "bartlett_test" | "levene_test"
15562 | "fishers_exact_test_2x2" | "fishers_exact"
15563 | "mcnemar_test"
15564 | "runs_test" | "wald_wolfowitz"
15565 | "friedman_test" | "kruskal_wallis_test" | "kruskal"
15566 | "sign_test"
15567 | "anderson_darling_normality" | "ad_normality"
15568 | "jarque_bera_test" | "jb_test"
15569 | "ljung_box_test" | "ljung_box"
15570 | "durbin_watson_stat" | "durbin_watson"
15571 | "mahalanobis_distance" | "mahalanobis_dist"
15573 | "cosine_distance" | "canberra_distance"
15574 | "bray_curtis_distance" | "bray_curtis"
15575 | "l1_distance"
15576 | "chi_squared_distance"
15577 | "multivariate_normal_pdf" | "mvn_pdf"
15579 | "multivariate_normal_sample" | "rmvn"
15580 | "dirichlet_pdf" | "dirichlet_sample" | "rdirichlet"
15581 | "skellam_pmf"
15582 | "inverse_gaussian_pdf" | "wald_pdf"
15583 | "inverse_gaussian_cdf" | "wald_cdf"
15584 | "inverse_gaussian_sample" | "rwald"
15585 | "non_central_chi2_pdf" | "ncchi2_pdf"
15586 | "matrix_exp" | "expm" | "matrix_log" | "logm"
15588 | "matrix_sqrt" | "sqrtm" | "matrix_sin" | "sinm"
15589 | "matrix_cos" | "cosm"
15590 | "rk45_dormand_prince" | "rk45" | "dopri5"
15592 | "midpoint_step" | "ode_midpoint"
15593 | "heun_step" | "ode_heun"
15594 | "verlet_step" | "ode_verlet"
15595 | "logistic_regression" | "logit_fit"
15597 | "poisson_regression"
15598 | "ridge_regression" | "ridge"
15599 | "lasso_coord" | "lasso"
15600 | "bootstrap_mean_ci" | "boot_mean_ci"
15602 | "jackknife_estimate" | "jackknife"
15603 | "permutation_test_diff" | "perm_test_diff"
15604 | "acf_at_lag" | "diff_op" | "lag_op"
15606 | "decompose_classical" | "decompose_ts"
15607 | "combinations_list" | "permutations_list"
15609 | "cyclic_permutations" | "subsets_of_size"
15610 | "longest_increasing_subseq" | "lis"
15612 | "knapsack_01" | "knapsack"
15613 | "subset_sum_target" | "subset_sum"
15614 | "coin_change_min" | "coin_change_minimum"
15615 | "edit_distance_levenshtein" | "edit_distance"
15616 | "one_hot_encode" | "onehot" | "label_encode"
15618 | "categorical_cross_entropy" | "cce"
15619 | "classification_metrics" | "binary_metrics"
15620 | "roc_auc" | "auroc"
15621 | "gaussian_blur_kernel" | "sobel_x" | "sobel_y"
15623 | "prewitt_x" | "prewitt_y"
15624 | "laplacian_of_gaussian" | "log_kernel"
15625 | "brownian_path" | "wiener_path"
15627 | "geometric_brownian_path" | "gbm_path"
15628 | "poisson_process" | "random_walk_1d"
15629 | "lempel_ziv_complexity" | "lz_complexity"
15631 | "huffman_code_lengths" | "huffman"
15632 | "shannon_entropy_rate" | "block_entropy_rate"
15633 | "planck_blackbody" | "blackbody"
15635 | "rayleigh_jeans" | "compton_shift"
15636 | "rydberg_energy"
15637 | "hydrogen_radial_wavefunction" | "h_rad_psi"
15638 | "integer_log" | "ilog"
15640 | "aks_primality" | "aks"
15641 | "elliptic_curve_add" | "ec_add"
15642 | "berlekamp_massey" | "bm_lfsr"
15643 | "bezout_coefficients" | "bezout" | "extended_euclid"
15644 | "factor_quadratic" | "complete_square"
15646 | "partial_fraction_simple" | "partial_fraction"
15647 | "gauss_chebyshev_quad" | "gc_quad"
15649 | "gauss_hermite_quad" | "gh_quad"
15650 | "gauss_laguerre_quad" | "glag_quad"
15651 | "clenshaw_curtis_quad" | "cc_quad"
15652 | "tanh_sinh_quad" | "ts_quad"
15653 | "gauss_legendre_2d" | "gl_2d"
15654 | "monte_carlo_2d" | "mc_2d"
15655 | "simulated_annealing" | "sa_min"
15657 | "simplex_lp" | "lp_simplex"
15658 | "particle_swarm" | "pso_min"
15659 | "gev_pdf" | "gev_cdf" | "gev_sample" | "rgev"
15661 | "gen_pareto_pdf" | "gen_pareto_cdf"
15662 | "gen_pareto_sample" | "rgenpareto"
15663 | "skew_normal_pdf" | "skew_normal_cdf"
15664 | "mixture_normal_pdf"
15665 | "categorical_sample" | "rcat"
15666 | "multinomial_pmf" | "multinomial_sample" | "rmultinom"
15667 | "truncated_normal_pdf"
15668 | "truncated_normal_sample" | "rtnorm"
15669 | "dbscan" | "gmm_em_1d" | "gmm_1d"
15671 | "silhouette_score"
15672 | "davies_bouldin_index" | "db_index"
15673 | "calinski_harabasz_index" | "ch_index"
15674 | "mds_2d" | "pcoa_2d" | "mean_shift"
15675 | "batch_norm" | "layer_norm"
15677 | "dropout_mask"
15678 | "max_pool_1d" | "avg_pool_1d"
15679 | "attention_softmax" | "positional_encoding"
15680 | "glorot_init" | "xavier_init"
15681 | "he_init" | "kaiming_init"
15682 | "adam_step" | "rmsprop_step"
15683 | "ewma" | "ccf" | "periodogram"
15685 | "welch_psd" | "welch"
15686 | "lag_features"
15687 | "median_filter_2d"
15689 | "threshold_otsu" | "otsu"
15690 | "histogram_equalize" | "hist_eq"
15691 | "erode_2d" | "dilate_2d"
15692 | "mse_loss" | "mae_loss" | "huber_loss"
15694 | "vincenty_distance" | "vincenty"
15696 | "mercator_project"
15697 | "destination_from_bearing" | "dest_bearing"
15698 | "recaman" | "recaman_seq"
15700 | "sylvester" | "sylvester_seq"
15701 | "happy_q" | "is_happy"
15702 | "amicable_pair_q"
15703 | "aliquot_sequence"
15704 | "magic_constant"
15705 | "clustering_coefficient_local" | "cc_local"
15707 | "clustering_coefficient_global" | "cc_global"
15708 | "assortativity" | "common_neighbors" | "jaccard_neighbors"
15709 | "adamic_adar"
15710 | "preferential_attachment_score" | "pa_score"
15711 | "triangle_3d_normal" | "triangle_3d_area"
15713 | "tetrahedron_volume"
15714 | "plane_from_3_points" | "plane_from_pts"
15715 | "point_to_plane_distance" | "pt_plane_dist"
15716 | "ray_triangle_intersect" | "moller_trumbore"
15717 | "ray_sphere_intersect" | "aabb_overlap"
15718 | "gauss_seidel"
15720 | "jacobi_iteration" | "jacobi_solve"
15721 | "sor_solve" | "sor"
15722 | "thomas_tridiag_solve" | "thomas"
15723 | "richardson_extrapolation" | "richardson"
15724 | "finite_difference_5pt" | "fd5pt"
15725 | "tonelli_shanks_sqrt" | "tonelli_shanks"
15727 | "baby_step_giant_step" | "bsgs"
15728 | "pollard_rho_factor" | "pollard_rho"
15729 | "modular_lcm" | "mlcm"
15730 | "crt_general" | "crt_arbitrary"
15731 | "van_der_waals_p" | "vdw_pressure"
15733 | "nernst_equation" | "nernst"
15734 | "arrhenius_rate" | "arrhenius"
15735 | "reduced_mass"
15736 | "ph_to_concentration" | "ph_to_h"
15737 | "metropolis_hastings" | "mh_sampler"
15739 | "gibbs_sampler_step" | "gibbs_step"
15740 | "euler_maruyama" | "em_sde"
15741 | "milstein" | "milstein_sde"
15742 | "ornstein_uhlenbeck_path" | "ou_path"
15743 | "hmm_forward" | "hmm_viterbi" | "hmm_backward"
15744 | "kaplan_meier" | "km_estimator" | "log_rank_test"
15746 | "needleman_wunsch" | "nw_align"
15747 | "smith_waterman" | "sw_align"
15748 | "gibbs_free_energy" | "delta_g"
15750 | "henderson_hasselbalch" | "hh_eq"
15751 | "radioactive_decay"
15752 | "half_life_to_constant" | "hl_to_lambda"
15753 | "pid_step"
15755 | "transfer_function_eval" | "tf_eval"
15756 | "bode_magnitude_db" | "bode_mag_db"
15757 | "bode_phase_deg"
15758 | "lqr_2x2"
15759 | "nash_eq_2x2" | "nash_2x2"
15761 | "shapley_value" | "expected_utility"
15762 | "hungarian_assignment" | "hungarian"
15764 | "tsp_nearest_neighbor" | "tsp_nn"
15765 | "vertex_cover_2approx" | "vc_2approx"
15766 | "heat_eq_1d" | "wave_eq_1d"
15768 | "laplace_2d_jacobi" | "laplace_jacobi"
15769 | "beta_binomial_update"
15771 | "normal_normal_update"
15772 | "gamma_poisson_update"
15773 | "dirichlet_multinomial_update"
15774 | "hadamard_gate" | "h_gate"
15776 | "cnot_gate" | "cx_gate"
15777 | "swap_gate" | "cz_gate"
15778 | "qft_matrix" | "phase_gate"
15779 | "s_gate" | "t_gate"
15780 | "bezier_eval"
15782 | "catmull_rom_eval" | "cmr_eval"
15783 | "cubic_hermite_eval" | "ch_eval"
15784 | "bspline_basis" | "nik_basis"
15785 | "freq_to_midi" | "midi_to_freq"
15787 | "equal_temperament_freq"
15788 | "cents_difference" | "cents_diff"
15789 | "redshift_z" | "hubble_distance" | "luminosity_distance"
15791 | "reynolds_number" | "mach_number"
15793 | "prandtl_number" | "bernoulli_velocity"
15794 | "negative_binomial_pmf" | "nb_pmf"
15796 | "hypergeometric_pmf"
15797 | "beta_binomial_pmf" | "bb_pmf"
15798 | "von_mises_pdf" | "vmf_pdf"
15799 | "erdos_renyi_random" | "erdos_renyi"
15801 | "barabasi_albert_random" | "barabasi_albert"
15802 | "watts_strogatz_random" | "watts_strogatz"
15803 | "rgb_to_lab" | "lab_to_rgb"
15805 | "kelvin_to_rgb" | "color_temp_rgb"
15806 | "bell_triangle" | "surjection_count"
15808 | "distinct_partition_count" | "q_partition"
15809 | "fibonacci_q" | "is_fib_number"
15810 | "bonferroni_correction" | "bonferroni"
15812 | "benjamini_hochberg" | "bh_fdr"
15813 | "tukey_hsd"
15814 | "hellinger_distance"
15815 | "wasserstein_1d" | "earth_movers_1d"
15816 | "chi_squared_divergence"
15817 | "beta_geometric_pmf"
15818 | "generalized_gamma_pdf" | "gengamma_pdf"
15819 | "zip_pmf" | "zero_inflated_poisson_pmf"
15820 | "stefan_boltzmann_luminosity" | "stellar_luminosity"
15821 | "photon_momentum" | "photon_energy_ev"
15822 | "dipole_radiation_power" | "larmor_power"
15823 | "parallax_to_distance" | "hawking_temperature"
15824 | "roche_limit" | "apparent_magnitude" | "distance_modulus"
15825 | "beer_lambert" | "absorbance"
15826 | "rate_law_n"
15827 | "freezing_point_depression" | "fpd"
15828 | "mixed_nash_2x2" | "minimax_2x2"
15829 | "barycentric_coords_2d" | "barycentric_2d"
15831 | "bresenham_line" | "bilinear_interp_2d"
15832 | "point_in_polygon_2d"
15833 | "hilbert_transform" | "cepstrum"
15834 | "butterworth_lowpass_coeffs" | "butter_lp"
15835 | "savitzky_golay_coeffs" | "sg_coeffs"
15836 | "savitzky_golay_filter" | "sg_filter"
15837 | "canny_edge_intensity" | "canny_intensity"
15838 | "bilateral_filter_basic" | "bilateral_filter"
15839 | "kmeans_pp_init" | "kpp_init"
15840 | "elbow_score" | "wcss"
15841 | "young_tableaux_count" | "syt_count"
15842 | "euler_alt_permutation" | "euler_zigzag"
15843 | "genocchi_number" | "lattice_paths_count"
15844 | "tetration"
15845 | "ackermann_limited" | "ackermann"
15846 | "perfect_power_q" | "b_smooth_q"
15847 | "k_core"
15849 | "rich_club_coefficient" | "rich_club"
15850 | "rsa_basic_encrypt" | "rsa_enc_int"
15851 | "rsa_basic_decrypt" | "rsa_dec_int"
15852 | "dh_shared_secret"
15853 | "bell_state_phi_plus" | "bell_phi_plus"
15854 | "bell_state_psi_minus" | "bell_psi_minus"
15855 | "density_matrix_purity" | "rho_purity"
15856 | "concurrence_2qubit"
15857 | "point_in_circle"
15858 | "circle_circle_intersect_2d"
15859 | "polygon_centroid"
15860 | "sutherland_hodgman_clip" | "sh_clip"
15861 | "kalman_rts_smoother" | "rts_smoother"
15862 | "gc_content" | "codon_to_aa"
15864 | "reverse_complement_dna" | "rev_comp_dna"
15865 | "hamming_dna"
15866 | "blosum62_pair_score" | "blosum62"
15867 | "kmer_count"
15868 | "great_circle_bearing" | "gc_bearing"
15870 | "midpoint_lat_lon" | "mid_geo"
15871 | "utm_zone_for"
15872 | "area_polygon_lat_lon" | "geo_polygon_area"
15873 | "crr_binomial_option" | "crr_option"
15875 | "bond_price_clean"
15876 | "bond_yield_to_maturity" | "bond_ytm"
15877 | "modified_duration_bond"
15878 | "convexity_bond" | "bond_convexity"
15879 | "ssim" | "psnr" | "mssim"
15881 | "db_spl_from_pa" | "db_spl"
15883 | "a_weighting_factor" | "a_weight"
15884 | "octave_band_center" | "octave_center"
15885 | "semitone_ratio"
15886 | "hardy_weinberg"
15888 | "expected_heterozygosity" | "het_e"
15889 | "fst_simple"
15890 | "allele_frequencies"
15891 | "sir_step" | "sir_r0" | "doubling_time"
15893 | "theil_index"
15895 | "herfindahl_hirschman" | "hhi"
15896 | "atkinson_index"
15897 | "lorenz_curve_points"
15898 | "iota_range" | "iota"
15900 | "reshape_array" | "reshape"
15901 | "grade_up" | "grade_asc"
15902 | "grade_down" | "grade_desc"
15903 | "plasma_frequency" | "omega_p"
15905 | "debye_length" | "lambda_d"
15906 | "cyclotron_frequency" | "omega_c"
15907 | "larmor_radius" | "gyroradius"
15908 | "jaro_winkler_similarity" | "jaro_winkler"
15910 | "metaphone_simple"
15911 | "elo_rating_update" | "elo"
15913 | "glicko_rating_update" | "glicko"
15914 | "dice_sum_pmf"
15915 | "cohens_d" | "effect_size_d"
15917 | "cliff_delta"
15918 | "vargha_delaney_a12" | "a12"
15919 | "step_response_2nd_order" | "step_2nd"
15921 | "overshoot_2nd_order" | "overshoot_pct"
15922 | "frobenius_norm"
15924 | "spectral_norm" | "operator_norm_2"
15925 | "trace_matrix" | "tr_mat"
15926 | "homophily_index" | "homophily"
15928 | "dyad_census" | "triad_census"
15929 | "sigmoid_inverse" | "logit"
15931 | "partition_at" | "drop_at" | "insert_at_idx"
15933 | "replace_at_index" | "set_at"
15934 | "swap_indices" | "nth_largest" | "nth_smallest"
15935 | "position_of_all_matching" | "positions_of_all"
15936 | "string_take_first" | "string_take_last"
15937 | "string_drop_first" | "string_drop_last"
15938 | "pluralize_simple"
15939 | "singularize_simple" | "singularize"
15940 | "capitalize_words" | "title_words"
15941 | "format_table_simple" | "ascii_table"
15942 | "days_between" | "weeks_between"
15943 | "months_between" | "years_between"
15944 | "first_of_month" | "last_of_month"
15945 | "day_of_week_iso" | "iso_dow"
15946 | "easter_sunday" | "chinese_zodiac"
15947 | "iso_week_number" | "iso_week"
15948 | "relative_luminance" | "wcag_luminance"
15949 | "contrast_ratio_wcag" | "wcag_contrast"
15950 | "delta_e_76" | "delta_e"
15951 | "color_blend_t" | "lerp_color"
15952 | "chord_to_freqs" | "scale_to_intervals"
15953 | "interval_semitones"
15954 | "transpose_freq_semitones" | "transpose_semi"
15955 | "bpm_to_period" | "midi_to_pitch_class"
15956 | "key_signature_for" | "circle_of_fifths_step"
15957 | "moon_phase" | "equation_of_time"
15958 | "solar_declination" | "sidereal_day_period" | "ecliptic_obliquity"
15959 | "permutation_order"
15960 | "permutation_parity" | "perm_sign"
15961 | "identity_permutation"
15962 | "permutation_compose" | "perm_mul"
15963 | "flesch_reading_ease" | "flesch_kincaid_grade"
15964 | "gunning_fog"
15965 | "automated_readability_index" | "ari"
15966 | "lix"
15967 | "adjusted_r_squared" | "adj_r2"
15968 | "aic" | "bic"
15969 | "residuals_compute" | "compute_residuals"
15970 | "composition_count" | "weak_composition_count"
15971 | "necklace_count" | "bracelet_count"
15972 | "multiset_permutations_count" | "multinomial_count"
15973 | "pearson_hash_byte" | "pearson_hash"
15974 | "xorshift32_step" | "lcg_next_u32"
15975 | "fisher_yates_shuffle"
15976 | "tetrahedral_number" | "square_pyramidal_number"
15978 | "octahedral_number" | "pentagonal_pyramidal_number"
15979 | "cake_number" | "cuban_number" | "centered_hexagonal_number"
15980 | "carmichael_q" | "is_carmichael"
15981 | "sphenic_q" | "is_sphenic"
15982 | "seven_smooth_q" | "is_7_smooth"
15983 | "cartesian_product_n" | "cart_n"
15984 | "multiset_union" | "multiset_intersection" | "multiset_difference"
15985 | "polynomial_roots_dk" | "durand_kerner"
15986 | "lin_bairstow_step" | "bairstow"
15987 | "heap_sift_down"
15988 | "fenwick_build" | "bit_build"
15989 | "fenwick_query" | "bit_query"
15990 | "segment_tree_sum" | "seg_sum"
15991 | "kmp_failure" | "kmp"
15992 | "z_array" | "z_func"
15993 | "suffix_array_naive"
15994 | "manacher_radii" | "manacher"
15995 | "rabin_karp_hash" | "lcp_array"
15996 | "regex_escape_simple"
15997 | "horspool_search" | "bm_horspool"
15998 | "lpt_schedule" | "lpt"
15999 | "johnsons_rule" | "johnson_2m"
16000 | "bit_reverse_32" | "bit_reverse"
16001 | "bin_to_gray" | "gray_to_bin"
16002 | "swap_bits_pos" | "swap_bits"
16003 | "hamming_weight" | "popcnt"
16004 | "hamming_distance_int" | "hamdist_int"
16005 | "internal_rate_of_return"
16006 | "modified_irr" | "mirr"
16007 | "payback_period_simple" | "payback_simple"
16008 | "rfc3339_format" | "rfc3339"
16009 | "rfc3339_parse"
16010 | "iso_ordinal_date" | "ordinal_date"
16011 | "lazy_caterer" | "central_polygonal"
16013 | "centered_square" | "centered_triangular" | "centered_pentagonal"
16014 | "star_number" | "dodecahedral_number" | "icosahedral_number"
16015 | "pronic_number" | "squared_triangular"
16016 | "woodall_number" | "cullen_number"
16017 | "repunit" | "repdigit" | "kaprekar_routine_step"
16018 | "smith_q"
16019 | "keith_q" | "is_keith"
16020 | "armstrong_q" | "is_armstrong"
16021 | "fnv1a_hash" | "djb2_hash"
16022 | "jenkins_one_at_a_time" | "jenkins_oat"
16023 | "murmurhash3_x32"
16024 | "adler32_hash" | "crc16_ccitt"
16025 | "vec_dot"
16026 | "l1_norm" | "l2_norm" | "vec_l2"
16027 | "linf_norm" | "max_norm" | "lp_norm"
16028 | "unit_vector"
16029 | "vector_project" | "proj" | "vector_reject"
16030 | "orthogonalize_vectors" | "gram_schmidt"
16031 | "outer_product" | "vec_outer"
16032 | "matrix_diagonal" | "mdiagvec"
16033 | "matrix_anti_diagonal"
16034 | "matrix_symmetric_q" | "matrix_orthogonal_q"
16035 | "geometric_mean_arr" | "harmonic_mean_arr"
16036 | "quadratic_mean_arr" | "lehmer_mean"
16037 | "running_mean" | "running_variance"
16038 | "outlier_iqr_q" | "z_score_robust"
16039 | "geometric_sequence" | "arithmetic_sequence"
16040 | "log_sum_exp" | "lse"
16041 | "log_sigmoid" | "log1p_exp"
16042 | "string_chars"
16043 | "string_words_count" | "word_count_simple"
16044 | "string_lines_count" | "line_count_simple"
16045 | "string_intersperse" | "string_replicate"
16046 | "string_uniq_chars" | "string_letter_frequency"
16047 | "anagram_q" | "is_anagram_q"
16048 | "string_take_while" | "string_drop_while"
16049 | "string_split_at_first" | "string_partition_at_word"
16050 | "relativistic_kinetic"
16052 | "lorentz_factor_v" | "doppler_relativistic"
16053 | "drag_force_quadratic" | "terminal_velocity"
16054 | "carnot_efficiency" | "otto_efficiency"
16055 | "brayton_efficiency" | "diesel_efficiency"
16056 | "specific_heat_const_v" | "speed_of_sound_ideal"
16057 | "kepler_period_au" | "synodic_period"
16058 | "hill_radius" | "jeans_length"
16059 | "chandrasekhar_mass" | "eddington_luminosity"
16060 | "schwarzschild_radius_m" | "gravity_at_radius"
16061 | "gravitational_pe"
16062 | "freefall_time" | "pendulum_freq" | "spring_period"
16063 | "centripetal_accel" | "lens_focal_length"
16064 | "avogadros_number" | "boltzmann_const"
16065 | "planck_const_h" | "gas_constant_r"
16066 | "concentration_dilute" | "partial_pressure"
16067 | "mole_fraction" | "molarity" | "molality"
16068 | "normality_chem" | "ionic_strength"
16069 | "titration_volume"
16070 | "atomic_radius_pm" | "de_broglie_wavelength_kg"
16071 | "lotka_volterra_step"
16072 | "michaelis_menten" | "hill_equation"
16073 | "lineweaver_burk" | "eadie_hofstee_y"
16074 | "arrhenius_temp_q10"
16075 | "body_surface_area_dubois" | "bsa_dubois"
16076 | "bmr_harris_benedict_male" | "bmr_harris_benedict_female"
16077 | "max_heart_rate" | "target_heart_rate"
16078 | "vo2_max_estimate" | "pulse_pressure"
16079 | "mean_arterial_pressure" | "map_bp"
16080 | "dew_point_magnus" | "heat_index_celsius"
16081 | "wind_chill_celsius" | "pressure_altitude_m"
16082 | "density_altitude_m" | "saturation_vapor_pressure"
16083 | "humidex" | "utci_simple"
16084 | "resistance_parallel" | "r_parallel"
16085 | "resistance_series" | "r_series"
16086 | "capacitance_parallel" | "c_parallel"
16087 | "capacitance_series" | "c_series"
16088 | "inductance_parallel" | "l_parallel"
16089 | "inductance_series" | "l_series"
16090 | "voltage_divider" | "current_divider"
16091 | "lc_resonant" | "q_factor_rlc"
16092 | "skin_depth" | "wire_resistance"
16093 | "motor_torque" | "efficiency_ratio"
16094 | "dB_voltage" | "db_voltage"
16095 | "dB_power" | "db_power"
16096 | "bfs_distances" | "dfs_preorder" | "connected_components"
16098 | "graph_is_tree" | "graph_density"
16099 | "graph_average_degree" | "graph_max_degree" | "graph_min_degree"
16100 | "graph_complement"
16101 | "in_degree_directed" | "out_degree_directed"
16102 | "graph_eccentricity_all" | "is_connected"
16103 | "articulation_points" | "bridges_edges"
16104 | "eulerian_path_q" | "hamiltonian_brute"
16105 | "string_to_charcodes" | "charcodes_to_string"
16106 | "string_xor"
16107 | "string_camel_to_snake" | "string_snake_to_camel"
16108 | "string_kebab_to_snake" | "string_snake_to_kebab"
16109 | "palindromic_q" | "substring_count"
16110 | "string_truncate_ellipsis" | "string_expand_tabs"
16111 | "string_normalize_spaces"
16112 | "days_in_year" | "quarter_of_year"
16113 | "zeller_day_of_week" | "age_from_birthdate"
16114 | "business_days_between" | "unix_epoch_to_iso"
16115 | "loan_payment_pmt" | "loan_balance"
16116 | "amortization_total_interest"
16117 | "apr_to_apy" | "apy_to_apr"
16118 | "compound_interest_periods" | "simple_interest_compute"
16119
16120 | "perpetuity_value" | "growing_perpetuity"
16121 | "annuity_present_value" | "annuity_future_value"
16122 | "capm_expected_return"
16123 | "treynor_ratio"
16124 | "jensens_alpha" | "information_ratio"
16125 | "friction_factor_laminar" | "swamee_jain_factor"
16126 | "pipe_pressure_drop" | "orifice_velocity"
16127 | "chezy_velocity" | "manning_velocity"
16128 | "froude_number" | "weber_number" | "grashof_number"
16129 | "nusselt_dittus_boelter"
16130 | "mollweide_project" | "robinson_project" | "sinusoidal_project"
16132 | "equirectangular_project" | "lambert_azimuthal_project" | "albers_conic_project"
16133 | "geohash_encode" | "geohash_decode" | "geohash_neighbor" | "geohash_bbox"
16134 | "gabor_kernel" | "unsharp_mask_kernel" | "emboss_kernel"
16135 | "box_blur_kernel" | "motion_blur_kernel" | "sharpen_kernel"
16136 | "edge_detect_kernel" | "sobel_diagonal_kernel" | "haar_2d_step"
16137 | "db4_coeffs" | "db6_coeffs" | "sym4_coeffs" | "coif1_coeffs"
16138 | "aes_sbox_byte" | "aes_inv_sbox_byte"
16139 | "chacha20_qround" | "xtea_round" | "speck_round" | "simon_round"
16140 | "kepler_hyperbolic" | "hohmann_dv1" | "hohmann_dv2" | "hohmann_total"
16141 | "bielliptic_total" | "lambert_simple"
16142 | "horizon_distance" | "solar_zenith_angle" | "air_mass_kasten"
16143 | "solar_constant" | "julian_centuries_j2000"
16144 | "mean_solar_longitude" | "mean_solar_anomaly" | "lst_to_solar"
16145 | "ra_dec_to_az_alt" | "ecliptic_to_equatorial" | "equatorial_to_galactic"
16146 | "orbital_eccentricity" | "semi_major_axis"
16147 | "specific_orbital_energy" | "specific_angular_momentum"
16148 | "toffoli_gate" | "ccx_gate" | "fredkin_gate" | "cswap_gate"
16149 | "iswap_gate" | "sqrt_swap_gate"
16150 | "rx_gate" | "ry_gate" | "rz_gate"
16151 | "ghz_state_n" | "w_state_n"
16152 | "depolarizing_channel" | "dephasing_channel" | "amplitude_damping_channel"
16153 | "quantum_fidelity_pure" | "trace_distance"
16154 | "bell_inequality_chsh" | "pauli_decomposition_2x2"
16155 | "quantum_relative_entropy" | "qft_4_real"
16156 | "bwt_encode" | "bwt_decode" | "mtf_encode" | "mtf_decode"
16157
16158 | "lyndon_factorize" | "christoffel_word" | "sturmian_word"
16159 | "z_function_alt" | "period_of_string" | "borders_of_string"
16160 | "thue_morse_string" | "fibonacci_word"
16161 | "mann_kendall_tau" | "theil_sen_slope" | "hodges_lehmann"
16162 | "huber_m_estimator" | "winsorized_variance_arr"
16163 | "bowley_skewness" | "pearson_skewness_2"
16164 | "concordance_correlation" | "quantile_p"
16165 | "label_propagation_step" | "modularity_q"
16166 | "clique_count_3" | "local_efficiency" | "global_efficiency"
16167 | "diameter_unweighted"
16168 | "aitken_delta_squared" | "wynn_epsilon"
16169 | "shanks_transform" | "levin_t_transform"
16170 | "harmonic_seq_sum" | "alternating_seq_sum"
16171 | "sparse_csr_build" | "sparse_csr_mul_vec" | "sparse_density"
16173 | "lower_triangular_q" | "upper_triangular_q"
16174 | "diagonal_dominance_q" | "matrix_zero_q" | "matrix_identity_q"
16175 | "matrix_random_uniform" | "matrix_random_normal"
16176 | "andrew_monotone_chain" | "polygon_area_signed"
16177 | "polygon_convex_q" | "iou_2d_axis_aligned" | "hausdorff_distance_2d"
16178 | "minkowski_sum_simple" | "circle_3_points"
16179 | "polygon_winding_number" | "segment_length"
16180 | "segments_parallel_q" | "segments_perpendicular_q"
16181 | "burr_xii_pdf" | "burr_xii_cdf" | "dagum_pdf" | "lomax_pdf"
16182 | "birnbaum_saunders_pdf" | "tukey_lambda_quantile"
16183 | "half_cauchy_pdf" | "half_logistic_pdf" | "reciprocal_pdf"
16184 | "levy_pdf" | "voigt_profile_simple"
16185 | "gompertz_pdf" | "inverse_weibull_pdf"
16186 | "log_gamma_simple" | "inverse_chi2_pdf"
16187 | "poly1305_block_step" | "x25519_field_mul" | "curve25519_mul_simple"
16188 | "secp256k1_y_recover" | "hmac_step_xor"
16189 | "pkcs7_pad" | "pkcs7_unpad" | "xor_byte_string"
16190 | "atbash_cipher"
16191 | "vigenere_encrypt" | "vigenere_decrypt" | "xor_brute_keylen"
16192 | "arima_diff" | "seasonal_diff"
16193 | "garch_step" | "egarch_step"
16194 | "realized_volatility" | "max_drawdown_arr"
16195 | "calmar_ratio" | "omega_ratio" | "kelly_criterion"
16196 | "var_historical" | "cvar_historical"
16197 | "graph_degree_distribution" | "graph_count_edges"
16198 | "graph_bipartite_match_simple" | "graph_count_triangles"
16199 | "graph_avg_clustering" | "graph_transitivity"
16200 | "graph_max_clique_brute" | "graph_independent_set_brute"
16201 | "graph_count_paths_length_k" | "graph_pagerank_simple"
16202 | "boole_rule" | "boole_int"
16204 | "gauss_legendre_5" | "gl5"
16205 | "gauss_kronrod_15" | "gk15"
16206
16207 | "midpoint_rule"
16208 | "adams_bashforth_4" | "ab4"
16209 | "heun_method" | "rk45_cash_karp" | "rkck"
16210 | "milne_pc" | "milne"
16211 | "modified_midpoint_ode" | "modmidpoint"
16212 | "backward_euler" | "implicit_euler"
16213 | "crank_nicolson_ode" | "cn_ode"
16214 | "brent_root" | "brent" | "ridders_root" | "ridders"
16215 | "steffensen_root" | "steffensen" | "halley_root" | "halley"
16216 | "householder_root" | "muller_root" | "muller"
16217 | "regula_falsi" | "false_position"
16218 | "secant_root" | "secant"
16219 | "anderson_step" | "aberth_step" | "inverse_quad_interp"
16220 | "lm_step" | "gradient_descent_step"
16221 | "nesterov_step" | "adagrad_step"
16222 | "cg_beta_pr" | "cg_beta_fr" | "bfgs_h_update_1d"
16223 | "wolfe_strong_q" | "dogleg_step"
16224 | "nelder_mead_reflect" | "nelder_mead_expand" | "nelder_mead_contract"
16225 | "sa_accept_prob" | "sa_boltzmann_temp" | "sa_cauchy_temp"
16226 | "sa_geometric_temp" | "acceptance_target"
16227 | "bs_call" | "blackscholes_call" | "bs_put" | "blackscholes_put"
16229 | "bs_theta_call" | "bs_rho_call"
16230 | "bachelier_call" | "black76_call"
16231 | "crr_american_call" | "crr_american_put" | "jr_european_call"
16232 | "trinomial_call" | "heston_price_simple" | "sabr_implied_vol"
16233 | "merton_jump_call" | "asian_call_mc" | "barrier_up_out_call"
16234 | "digital_call" | "lookback_call"
16235 | "macaulay_duration" | "forward_rate"
16236 | "discount_continuous" | "ytm_newton"
16237 | "vasicek_bond" | "cir_bond" | "hull_white_drift"
16238 | "cds_upfront" | "black_karasinski_drift" | "quanto_adjustment"
16239 | "fx_forward" | "garman_kohlhagen_call" | "margrabe" | "stulz_min_call"
16240 | "sharpe_annualized"
16241 | "jensen_alpha" | "modified_sharpe"
16242 | "ph_from_h" | "poh_from_oh" | "pka_from_ka"
16244 | "henderson_base"
16245 | "arrhenius_k" | "eyring_k"
16246 | "first_order_concentration" | "first_order_half_life"
16247 | "second_order_concentration" | "second_order_half_life"
16248 | "zero_order_concentration"
16249
16250 | "ideal_gas_n" | "redlich_kwong_p"
16251 | "compressibility_z"
16252 | "kc_from_rates" | "kp_from_kc" | "reaction_quotient" | "rxn_q"
16253 | "le_chatelier_dir"
16254 | "dg_from_k" | "k_from_dg" | "vant_hoff" | "clausius_clapeyron" | "antoine_p"
16255 | "emf_from_half_cells" | "faraday_mass_deposited"
16256 | "transmittance" | "ksp_from_concs"
16257 | "debye_huckel"
16258 | "cp_monatomic_ideal" | "cv_monatomic_ideal"
16259 | "heat_capacity_q" | "calorimeter_dt" | "enthalpy_reaction"
16260 | "avogadro_count" | "moles_from_mass"
16261 | "dilution_v2" | "raoult_law" | "bp_elevation" | "fp_depression"
16262 | "osmotic_pressure" | "rydberg_lambda" | "bohr_radius_n"
16263 | "bohr_energy_ev" | "photon_energy_freq" | "photon_energy_lambda"
16264 | "de_broglie"
16265 | "logistic_growth_step" | "logistic_growth_analytic"
16267 | "gompertz_growth_step" | "allee_growth_step"
16268 | "growth_rate_from_ratio"
16269 | "seir_step" | "seird_step" | "sis_step"
16270 | "r0_basic" | "rt_effective" | "herd_immunity_threshold" | "generation_time"
16271 | "inverse_simpson"
16272 | "pielou_evenness" | "margalef_richness" | "menhinick_richness"
16273 | "berger_parker" | "sorensen_dice"
16274 | "rao_quadratic_entropy"
16275 | "selection_step" | "nei_genetic_distance"
16276 | "effective_pop_size" | "carrying_capacity_from_data"
16277 | "petersen_estimator" | "chapman_estimator"
16278 | "lv_competition_step"
16279 | "holling_type1" | "holling_type2" | "holling_type3"
16280 | "leslie_step" | "net_reproductive_rate" | "generation_time_demo"
16281 | "finite_rate_lambda" | "kleibers_law" | "bergmann_adjust"
16282 | "q10" | "species_area" | "intrinsic_growth_rate"
16283 | "macarthur_wilson_immigration" | "macarthur_wilson_extinction"
16284 | "island_equilibrium"
16285 | "efield_point" | "epotential_point"
16287 | "capacitor_charge"
16288 | "ohm_voltage" | "power_vi" | "power_i2r"
16289
16290 | "capacitance_parallel_sum"
16291 | "bfield_wire" | "bfield_solenoid" | "lorentz_force_mag"
16292 | "faraday_emf"
16293 | "lc_frequency" | "lc_omega"
16294 | "rc_tau" | "rl_tau"
16295 | "poynting_magnitude" | "em_intensity" | "radiation_pressure"
16296 | "em_wavelength" | "em_frequency"
16297 | "snell_theta2"
16298 | "index_from_speed" | "fresnel_reflection_normal"
16299 | "fresnel_rs" | "fresnel_rp"
16300 | "lensmaker" | "thin_lens_v" | "mirror_equation_v"
16301 | "lens_magnification" | "diffraction_grating_angle"
16302 | "single_slit_min" | "rayleigh_resolution"
16303 | "lorentz_gamma"
16304 | "rel_momentum" | "rel_ke" | "rel_total_energy" | "rel_energy_pm"
16305 | "relativistic_doppler" | "rel_velocity_add"
16306
16307 | "wave_string_speed" | "sound_solid" | "sound_gas"
16308 | "doppler_classical" | "standing_wave_fundamental"
16309 | "open_pipe_harmonic" | "closed_pipe_harmonic"
16310 | "sound_db"
16311 | "alfven_speed"
16312 | "grav_time_dilation" | "grav_redshift"
16313 | "kosaraju_scc" | "bridges"
16315 | "max_flow_ek" | "min_cut_value" | "hopcroft_karp"
16316
16317 | "katz_centrality" | "hits_simple"
16318 | "pagerank_damped" | "cc_count" | "cc_labels"
16319 | "topological_sort_kahn" | "has_cycle_directed" | "has_cycle_undirected"
16320 | "diameter_bfs" | "radius_bfs"
16321 | "num_edges" | "k_coreness"
16322 | "greedy_coloring" | "chromatic_number_greedy"
16323 | "sum_degrees" | "avg_degree" | "max_degree"
16324 | "is_tree" | "girth"
16325 | "hamming_window" | "hann_window" | "blackman_window"
16327 | "blackman_harris_window" | "bartlett_window" | "welch_window"
16328 | "kaiser_window" | "tukey_window" | "gaussian_window"
16329 | "hilbert_envelope"
16330 | "biquad_step" | "biquad_lowpass_coeffs" | "biquad_highpass_coeffs"
16331 | "biquad_bandpass_coeffs" | "biquad_notch_coeffs" | "biquad_allpass_coeffs"
16332 | "biquad_peak_coeffs" | "biquad_lowshelf_coeffs" | "biquad_highshelf_coeffs"
16333 | "butterworth_prewarp" | "butterworth_order"
16334 | "fir_moving_average" | "fir_lowpass_design"
16335 | "spectrogram_simple"
16336 | "zero_pad" | "resample_nearest" | "resample_linear" | "quantize"
16337 | "mu_law_encode" | "mu_law_decode" | "a_law_encode" | "a_law_decode"
16338 | "chirp_linear"
16339 | "fnv1a_32" | "fnv1a_64" | "sdbm_hash"
16341 | "siphash24"
16342 | "pbkdf2_hmac_step" | "scrypt_round" | "bcrypt_cost_iters"
16343 | "argon2_block_mix" | "hkdf_expand_step"
16344 | "lfsr_galois_step" | "mt19937_temper" | "xorshift64" | "xorshift32"
16345 | "pcg32_step" | "lcg_numrec_step" | "splitmix64_step" | "wyhash_mix"
16346
16347 | "xor_cipher_byte"
16348 | "railfence_encrypt" | "beaufort" | "affine_encrypt" | "substitution_encrypt"
16349 | "letter_frequency" | "english_chi2" | "index_of_coincidence" | "kasiski_repeats"
16350 | "deterministic_prime" | "dh_shared" | "rsa_encrypt_simple"
16351 | "monobit_test" | "approximate_entropy"
16352 | "gini_impurity" | "entropy_bits" | "information_gain" | "gain_ratio"
16354 | "nb_gaussian_likelihood" | "nb_bernoulli_likelihood" | "nb_multinomial_log_likelihood"
16355 | "adaboost_alpha" | "hinge_loss" | "squared_hinge"
16356 | "logistic_loss"
16357 | "sigmoid_grad" | "tanh_grad"
16358 | "relu_grad"
16359 | "softsign" | "prelu" | "threshold_act"
16360 | "confusion_counts" | "mcc" | "f_beta" | "specificity"
16361 | "balanced_accuracy" | "cohen_kappa" | "brier_score" | "log_loss"
16362 | "tversky" | "mahalanobis_1d"
16363 | "one_hot" | "topk_indices"
16364 | "minmax_scale" | "zscore_norm" | "robust_scale"
16365 | "triangle_area_heron" | "triangle_area_pts"
16367 | "triangle_inradius" | "triangle_circumradius"
16368 | "regular_ngon_area" | "regular_ngon_inradius" | "regular_ngon_circumradius"
16369 | "n_ball_volume"
16370 | "cylinder_surface" | "cone_surface"
16371
16372 | "ellipsoid_volume" | "ellipsoid_surface_approx"
16373 | "dist_point_line_2d" | "dist_point_plane_3d" | "closest_pt_segment_2d"
16374 | "bbox_from_points"
16375 | "euclidean_distance_nd"
16376 | "hamming_distance_str"
16377 | "great_circle_law_of_cos"
16378 | "initial_bearing" | "midpoint_great_circle"
16379 | "shoelace_area" | "polygon_is_convex" | "convex_hull_jarvis"
16380 | "euler_characteristic" | "genus_from_euler"
16381 | "spherical_triangle_area" | "polygon_with_holes_area" | "picks_theorem"
16382 | "centroid_nd" | "covariance_matrix_pts" | "simplex_volume_3d"
16383 | "hyper2f1" | "hyper1f1" | "hyper0f1" | "pochhammer"
16385 | "mathieu_ce0" | "mathieu_se1" | "parabolic_d0" | "parabolic_d1"
16386 | "whittaker_m" | "struve_h0" | "struve_h1"
16387 | "lambert_w0" | "wright_omega"
16388 | "sinhc" | "cosh_minus1_over_x2"
16389 | "sine_integral_si" | "cosine_integral_ci" | "exp_integral_e1"
16390 | "dawson_function" | "owen_t"
16391 | "spherical_bessel_j0" | "spherical_bessel_j1"
16392 | "spherical_bessel_y0" | "spherical_bessel_y1"
16393 | "mod_sph_bessel_i0" | "mod_sph_bessel_i1" | "mod_sph_bessel_k0"
16394 | "coulomb_f0"
16395 | "polylog_li2" | "polylog_n"
16396
16397 | "ti2" | "clausen_cl2"
16398 | "bose_einstein_g" | "fermi_dirac_int"
16399 | "theta3" | "theta2"
16400 | "jacobi_sn_small_q" | "jacobi_cn_small_q" | "jacobi_dn_small_q"
16401 | "riemann_xi" | "bessel_jn_general" | "bessel_in_general"
16402 | "absolute_magnitude"
16404 | "pc_to_ly" | "ly_to_pc" | "pc_to_au" | "au_to_m"
16405 | "solar_mass_to_kg" | "solar_luminosity_to_w"
16406 | "hubble_distance_mpc" | "comoving_distance_approx" | "critical_density"
16407 | "et_freq_ratio" | "midi_to_hz" | "hz_to_midi" | "cents_between"
16408 | "just_intonation_ratio" | "pythagorean_ratio"
16409 | "beat_frequency" | "bpm_to_spb" | "note_name_to_midi"
16410 | "rgb_to_yiq" | "rgb_to_yuv601"
16411 | "srgb_to_xyz" | "xyz_to_lab" | "delta_e_94"
16412
16413
16414 | "feet_to_meters" | "meters_to_feet"
16415 | "lb_to_kg" | "kg_to_lb"
16416 | "mph_to_kmh" | "kmh_to_mph" | "mps_to_kmh" | "kmh_to_mps" | "knots_to_kmh"
16417 | "atm_to_pa" | "pa_to_atm" | "mmhg_to_pa"
16418 | "ev_to_joules" | "joules_to_ev" | "btu_to_joules" | "kwh_to_joules"
16419 | "bpm_to_midi_tick_us" | "iso226_phon_adjustment"
16420 | "db_to_amp" | "amp_to_db"
16421 | "roman_encode" | "roman_decode" | "number_to_english"
16422 | "hubble_lcdm" | "hubble_time" | "hubble_distance_si" | "critical_density_si"
16424 | "comoving_distance" | "angular_diameter_distance"
16425 | "lookback_time" | "age_at_z" | "scale_factor" | "redshift_from_a"
16426 | "omega_m_at_z" | "lcdm_eos" | "cpl_w" | "deceleration_q"
16427 | "schwarzschild_radius_kg" | "kerr_ergosphere_eq" | "kerr_horizon"
16428 | "bh_entropy" | "bh_evaporation_time"
16429 | "schwarzschild_isco" | "photon_sphere_radius"
16430 | "tidal_force" | "grav_dilation_factor" | "lense_thirring_omega"
16431 | "gw_strain_amplitude" | "chirp_mass" | "grav_binding_energy"
16432 | "roche_limit_rigid" | "roche_limit_fluid"
16433 | "lagrange_l1" | "sphere_of_influence"
16434 | "freefall_velocity_schwarzschild" | "einstein_ring_radius"
16435 | "microlensing_magnification" | "cosmic_distance_modulus_si"
16436 | "cmb_temperature" | "cmb_temperature_at_z"
16437 | "stefan_boltzmann_si" | "planck_spectral_radiance"
16438 | "schwarzschild_g_tt" | "schwarzschild_g_rr" | "kretschmann_schwarzschild"
16439 | "hill_velocity" | "vacuum_energy_density"
16440 | "sound_horizon_recomb" | "bao_scale_today" | "sigma8_default"
16441 | "lensing_convergence" | "sigma_crit"
16442 | "perihelion_precession" | "shapiro_delay" | "light_deflection_angle"
16443 | "tov_mass_limit"
16444 | "main_sequence_lifetime" | "schwarzschild_freefall_time"
16445 | "friedmann_density_total" | "cosmological_constant"
16446
16447 | "planck_energy"
16448 | "pure_state_density" | "purity"
16450 | "linear_entropy" | "quantum_mutual_info"
16451 | "eof_from_concurrence"
16452 | "bell_state_index" | "chsh_expectation" | "tsirelson_bound"
16453 | "pauli_real_part" | "pauli_y_imag"
16454 | "bloch_to_density_real" | "bloch_purity_check"
16455 | "fidelity_pure_real" | "l1_coherence" | "relative_entropy_coherence"
16456 | "kraus_apply" | "bit_flip_prob" | "phase_flip_prob"
16457 | "depolarizing_density_2x2" | "amplitude_damping_excited"
16458 | "quantum_fisher_info" | "cramer_rao_bound" | "squeezing_db" | "heisenberg_min"
16459 | "coherent_mean_photons" | "thermal_mean_photons" | "poisson_photon_pmf"
16460 | "bose_einstein_pmf" | "mandel_q" | "g2_zero"
16461 | "free_particle_energy" | "infinite_well_energy" | "harmonic_oscillator_energy"
16462 | "hydrogen_energy_n" | "stark_shift_linear"
16463 | "zeeman_energy" | "larmor_frequency" | "rabi_frequency"
16464 | "schrodinger_step_real" | "probability_density" | "state_norm" | "state_normalize"
16465 | "quantum_variance" | "spin_casimir"
16466 | "cg_simple" | "wigner_3j_bound" | "qho_ground_state"
16467 | "tunneling_prob" | "gamow_factor" | "compton_wavelength" | "uncertainty_position"
16468 | "berry_phase_spin_half" | "zeno_survival" | "decoherence_time"
16469 | "ramsey_visibility" | "fermi_golden_rule"
16470 | "needleman_wunsch_score" | "smith_waterman_score" | "pam250_score"
16472 | "tanimoto_bits" | "translate_dna" | "transcribe_dna_rna" | "reverse_transcribe"
16473 | "at_content" | "tm_wallace" | "tm_marmur" | "codon_adaptation_index"
16474 | "kmer_jaccard" | "sequence_shannon_info" | "pwm_score"
16475 | "msa_column_entropy" | "seq_logo_information"
16476 | "damerau_levenshtein" | "lcs_length"
16477 | "hirschberg_lcs_length" | "common_kmers"
16478 | "jukes_cantor_distance" | "kimura_2p_distance" | "felsenstein_step"
16479 | "branch_length_substitutions" | "num_unrooted_trees" | "bayes_posterior"
16480 | "hw_expected_counts" | "allele_frequency" | "ld_d" | "ld_r_squared"
16481 | "heterozygosity" | "ne_from_variance"
16482 | "expected_coverage" | "lander_waterman_gaps"
16483 | "bh_adjusted_p" | "zscore_count"
16484 | "go_enrichment_p" | "blosum45_score"
16485 | "henikoff_weight" | "hamming_protein" | "codon_usage_variance"
16486 | "dnds_ratio" | "mutation_rate" | "tajimas_d" | "wattersons_theta"
16487 | "coalescent_expected_time" | "coalescent_tree_length" | "nm_from_fst"
16488 | "bdf1_step" | "bdf2_step" | "bdf3_step" | "bdf4_step" | "bdf5_step" | "bdf6_step"
16490 | "ab1_step" | "ab2_step" | "ab3_step"
16491 | "am2_step" | "am3_step" | "am4_step"
16492 | "ros2_step" | "imex_euler_step" | "symplectic_euler_step"
16493 | "leapfrog_step" | "stormer_verlet_step"
16494 | "rk4_single" | "dopri5_combine" | "rkf45_error"
16495 | "lobatto_iiia_2" | "lobatto_iiic_3" | "gauss_irk_2_stage" | "magnus_1st"
16496 | "euler_lte" | "trapezoidal_lte" | "pi_step_size"
16497 | "stiffness_ratio" | "spectral_radius"
16498 | "heun_euler_step" | "bogacki_shampine_step" | "verner_8_combine"
16499 | "rk_combine" | "ab_coeff_sum"
16500 | "newmark_beta_step" | "wilson_theta_step"
16501 | "strang_split" | "lie_split"
16502 | "exp_euler_step" | "etd_rk2" | "dde_euler_step"
16503 | "em_step" | "milstein_step" | "heun_sde_step" | "stratonovich_correction"
16504 | "predictor_corrector" | "numerical_jacobian_col"
16505 | "cn_coefficient" | "imex_theta_split" | "bulirsch_stoer_step"
16506 | "cfl_number" | "diffusion_stability"
16507 | "lax_friedrichs_flux" | "lax_wendroff_flux"
16508 | "van_leer_limiter" | "minmod_limiter" | "superbee_limiter" | "mc_limiter"
16509 | "pollard_p_minus_1" | "fermat_factor"
16511 | "trial_smallest_factor" | "bsgs_discrete_log"
16512 | "mertens" | "liouville"
16513 | "is_b_smooth" | "primorial_n"
16514 | "pseudoprime_base2" | "strong_pseudoprime"
16515 | "aks_witness_count" | "qs_relation"
16516 | "index_calculus_naive" | "lll_2x2_step" | "coppersmith_bound"
16517 | "shor_period_prob" | "rsa_d_from_e" | "dh_secret"
16518 | "elgamal_encrypt" | "ecc_point_double" | "continued_fraction_sqrt"
16519 | "pell_fundamental" | "sum_two_squares" | "class_number_bound"
16520 | "smith_normal_2x2_step" | "regulator_naive"
16521 | "power_residue_check" | "wieferich_check" | "wilson_test"
16522 | "goldbach_pair" | "english_likeness" | "xor_break_singlebyte"
16523 | "bit_reverse_64"
16524 | "gf256_multiply" | "hash_combine"
16525 | "arch_lm_test" | "breusch_pagan_test" | "white_robust_se"
16527 | "newey_west_se" | "hansen_j_test" | "gmm_moment_condition"
16528 | "hausman_test" | "breusch_godfrey_test" | "box_pierce_test"
16529 | "adf_test_stat" | "pp_test_stat" | "kpss_test_stat"
16530 | "dickey_fuller_critical" | "engle_granger_step"
16531 | "johansen_trace_step" | "vecm_alpha_beta"
16532 | "panel_within_estimator" | "panel_between_estimator"
16533 | "panel_random_effects" | "arellano_bond_step"
16534 | "ols_estimator" | "ols_residual_variance" | "ols_r_squared"
16535 | "ols_adjusted_r2" | "akaike_info_crit" | "bayesian_info_crit"
16536 | "hannan_quinn_ic" | "f_statistic_pooled" | "breusch_pagan_lm"
16537 | "ramsey_reset_test" | "chow_test_stat" | "white_test_stat"
16538 | "goldfeld_quandt" | "wald_test_stat" | "score_test_stat"
16539 | "likelihood_ratio_test" | "two_sls_iv" | "iv_estimator"
16540 | "mle_normal_log_lik" | "mle_exponential_log_lik"
16541 | "mle_poisson_log_lik" | "gmm_moment_function"
16542 | "pooling_test_stat" | "heteroskedasticity_test"
16543 | "robust_se_huber_white" | "bootstrap_se_estimate"
16544 | "heckman_correction" | "tobit_log_likelihood"
16545 | "probit_log_likelihood" | "logit_log_likelihood"
16546 | "multinomial_logit_prob" | "ordered_probit_threshold"
16547 | "panel_var_step" | "impulse_response_step"
16548 | "variance_decomposition" | "granger_causality_chi2"
16549 | "cointegration_residual" | "error_correction_step"
16550 | "random_walk_innovation" | "random_walk_drift_step"
16551 | "ar_model_likelihood" | "ma_model_likelihood"
16552 | "arma_model_innovation"
16553 | "euler_char_complex" | "betti_zero" | "betti_one" | "betti_two"
16555 | "genus_surface" | "chern_first_2d" | "genus_curve_arith"
16556 | "genus_curve_geo" | "hodge_diamond_value" | "poincare_duality"
16557 | "fundamental_group_zn" | "homology_rank" | "cohomology_rank"
16558 | "homotopy_group_sphere_pi" | "mapping_class_torus"
16559 | "linking_number_two" | "writhe_polygon" | "torsion_coefficient"
16560 | "simplex_volume_n" | "simplicial_volume" | "nerve_complex_count"
16561 | "cech_zero_cohomology" | "de_rham_zero"
16562 | "poincare_polynomial_eval" | "chromatic_homology_rank"
16563 | "khovanov_q_grading" | "hochschild_zero" | "cyclic_homology_step"
16564 | "group_cohomology_dim" | "group_homology_dim"
16565 | "abelianization_quotient" | "free_group_rank_lower"
16566 | "nilpotency_class_lower" | "solvable_length_upper"
16567 | "schreier_index" | "todd_genus_eval" | "hirzebruch_signature"
16568 | "chern_simons_action" | "gauss_bonnet_total"
16569 | "seifert_genus_lower" | "alexander_polynomial_at_one"
16570 | "jones_polynomial_at_minus_one" | "jones_polynomial_at_i"
16571 | "homfly_evaluation" | "kauffman_bracket_eval"
16572 | "cabling_pair_signature" | "seifert_form_2x2"
16573 | "turaev_alexander_step" | "v_polynomial_eval"
16574 | "polynomial_jones_skein" | "delta_complex_count"
16575 | "poset_zeta_two" | "mobius_poset_two" | "mobius_function_pair"
16576 | "mobius_inversion_step" | "incidence_algebra_dim"
16577 | "quiver_path_count" | "representation_dim_step"
16578 | "weyl_group_order" | "root_system_count"
16579 | "cartan_determinant_a2" | "cartan_matrix_b2"
16580 | "killing_form_su2" | "casimir_eigenvalue_su2"
16581 | "universal_enveloping_dim" | "verma_character_step"
16582 | "plethystic_substitution_value" | "schur_polynomial_eval"
16583 | "hall_inner_product_two" | "plactic_class_size"
16584 | "robinson_schensted_pair" | "yamanouchi_word_count"
16585 | "rsk_size" | "character_su2" | "character_sun"
16586 | "quantum_dimension_su2" | "quantum_dimension_q"
16587 | "fusion_rule_su2_step" | "modular_data_s_value"
16588 | "modular_data_t_value" | "verlinde_count_step"
16589 | "quantum_invariant_eval" | "operad_count_two"
16590 | "moduli_dimension_curves" | "hodge_polynomial_eval"
16591 | "mirror_symmetry_check" | "gromov_witten_invariant"
16592 | "donaldson_invariant" | "seiberg_witten_value"
16593 | "floer_homology_rank" | "khovanov_rasmussen_s"
16594 | "ozsvath_szabo_tau" | "heegaard_genus_lower"
16595 | "fintushel_stern_step" | "bauer_furuta_step"
16596 | "geometric_intersection_number"
16597 | "algebraic_intersection_number"
16598 | "nernst_potential_full" | "electrode_potential_step"
16600 | "exchange_current_density" | "butler_volmer_current"
16601 | "tafel_anodic_current" | "tafel_cathodic_current"
16602 | "mass_transport_overpotential" | "limiting_current_density"
16603 | "diffusion_layer_thickness" | "faradaic_efficiency"
16604 | "coulombic_efficiency_cell" | "energy_efficiency_cell"
16605 | "voltaic_efficiency" | "charge_capacity_battery"
16606 | "energy_density_battery" | "power_density_battery"
16607 | "specific_capacity_active" | "columbic_capacity_lihalfcell"
16608 | "ragone_point" | "peukert_capacity" | "peukert_exponent_fit"
16609 | "shepherd_voltage_step" | "nernst_planck_flux"
16610 | "debye_length_electrolyte" | "debye_huckel_activity"
16611 | "gouy_chapman_potential" | "stern_layer_capacitance"
16612 | "double_layer_capacitance" | "helmholtz_capacitance"
16613 | "zeta_potential_estimate" | "electroosmotic_velocity"
16614 | "hagen_poiseuille_eo" | "diffuse_layer_thickness"
16615 | "poisson_boltzmann_step" | "linearized_pb_step"
16616 | "electrochem_impedance_z" | "randles_circuit_z"
16617 | "warburg_impedance" | "cole_cole_eis" | "nyquist_phase"
16618 | "charge_transfer_resistance" | "solution_resistance_estimate"
16619 | "ionic_conductivity_arrhenius" | "nernst_einstein_diffusivity"
16620 | "walden_product" | "kohlrausch_law"
16621 | "onsager_relation_two_species" | "trasatti_voltammetry_charge"
16622 | "randles_sevcik_peak" | "levich_current_rde"
16623 | "koutecky_levich_intercept" | "mott_schottky_capacitance"
16624 | "flat_band_potential" | "schottky_barrier_height"
16625 | "photocurrent_density" | "quantum_efficiency_photo"
16626 | "overall_efficiency_pec" | "fuel_cell_polarization"
16627 | "electrolyzer_voltage" | "faraday_efficiency_h2"
16628 | "overpotential_oer" | "overpotential_her"
16629 | "electrocrystallization_step" | "nucleation_rate_constant"
16630 | "metal_corrosion_rate" | "pourbaix_line_value"
16631 | "mixed_potential_step" | "electrochemiluminescence_yield"
16632 | "solid_electrolyte_capacity" | "ionic_liquid_viscosity_step"
16633 | "lithium_ion_diffusivity" | "soc_estimate_coulomb"
16634 | "soh_capacity_fade" | "ocv_lithium_ion_step"
16635 | "state_of_charge_kalman" | "thermal_runaway_threshold"
16636 | "joule_heating_battery" | "calorimetric_heat_battery"
16637 | "abuse_test_voltage" | "swelling_strain_step"
16638 | "sei_resistance_growth" | "binder_content_optimal"
16639 | "porosity_active_layer" | "tortuosity_estimate_bruggeman"
16640 | "electrolyte_decomposition_temp" | "gibbs_thomson_undercooling"
16641 | "nernst_diffusion_layer" | "diff_coeff_aqueous_estimate"
16642 | "salt_activity_coefficient" | "mean_activity_coeff_pitzer"
16643 | "osmotic_coefficient_pitzer" | "debye_huckel_screening_factor"
16644 | "ph_at_isoelectric" | "buffer_capacity_acid_base"
16645 | "henderson_hasselbalch_solve" | "titration_endpoint_index"
16646 | "tensor_contract_two" | "tensor_outer_two" | "tensor_trace_index"
16648 | "tensor_symmetrize_two" | "tensor_antisymmetrize_two"
16649 | "levi_civita_three" | "levi_civita_four"
16650 | "kronecker_three" | "kronecker_four"
16651 | "metric_minkowski_eta_step" | "metric_schwarzschild_step"
16652 | "metric_kerr_step_simple" | "metric_frw_lapse"
16653 | "christoffel_first_kind_step" | "christoffel_second_kind_step"
16654 | "riemann_tensor_step_zero" | "riemann_curvature_normal_form"
16655 | "ricci_tensor_step_zero" | "scalar_curvature_step"
16656 | "einstein_tensor_step" | "weyl_tensor_step_zero"
16657 | "schouten_tensor_step" | "geodesic_equation_step_zero"
16658 | "parallel_transport_step" | "covariant_derivative_step"
16659 | "christoffel_symbol_normalize" | "ricci_identity_step"
16660 | "bianchi_first_identity_check" | "bianchi_second_identity_check"
16661 | "killing_vector_lie_step" | "lie_derivative_scalar_step"
16662 | "lie_derivative_vector_step" | "exterior_derivative_one_form"
16663 | "hodge_star_one_form" | "codifferential_step"
16664 | "laplace_de_rham_step" | "volume_form_riemannian"
16665 | "hodge_inner_product_one" | "sectional_curvature_two_plane"
16666 | "gauss_codazzi_step" | "mainardi_codazzi_step"
16667 | "weingarten_map_step" | "shape_operator_eig"
16668 | "mean_curvature_step" | "gaussian_curvature_step"
16669 | "extrinsic_principal_curv" | "intrinsic_principal_curv"
16670 | "geodesic_curvature_step" | "darboux_frame_step"
16671 | "fermi_normal_step" | "synge_world_function"
16672 | "raychaudhuri_step" | "expansion_scalar_step"
16673 | "shear_tensor_step" | "twist_tensor_step"
16674 | "optical_scalars_step" | "peeling_step_psi4"
16675 | "ads_metric_step" | "de_sitter_metric_step"
16676 | "warped_product_step_zero" | "kaluza_klein_step"
16677 | "brans_dicke_step" | "horndeski_step"
16678 | "einstein_dilaton_step" | "gauss_bonnet_term_2d"
16679 | "chern_pontryagin_4d_step" | "adm_mass_step"
16680 | "komar_mass_step" | "bondi_mass_step"
16681 | "brown_york_quasilocal" | "isolated_horizon_charge"
16682 | "trapped_surface_check" | "apparent_horizon_step"
16683 | "event_horizon_check" | "cosmological_constant_term"
16684 | "de_sitter_radius_step" | "anti_de_sitter_radius_step"
16685 | "penrose_diagram_factor" | "conformal_compactification_step"
16686 | "schwarzschild_kruskal_step" | "gullstrand_painleve_step"
16687 | "kerr_newman_charge_term" | "boyer_lindquist_step"
16688 | "hartle_thorne_metric" | "oppenheimer_volkoff_step"
16689 | "post_newtonian_step" | "shapiro_delay_step"
16690 | "mercury_perihelion_advance"
16691 | "gravitational_wave_quadrupole"
16692 | "plus_polarization_amp" | "cross_polarization_amp"
16693 | "chirp_mass_inspiral_step" | "isco_radius_kerr_step"
16694 | "spin_orbit_coupling_term" | "spin_spin_coupling_term"
16695 | "hawking_area_increase" | "unruh_temperature_full"
16696 | "bekenstein_entropy_step" | "holographic_entanglement_step"
16697 | "ryu_takayanagi_step" | "swampland_distance_check"
16698 | "conditional_entropy_step" | "joint_entropy_step"
16700 | "relative_entropy_kl" | "mutual_information_step"
16701 | "chain_rule_entropy" | "fano_inequality_bound"
16702 | "data_processing_inequality" | "arithmetic_coding_interval"
16703 | "range_coding_step" | "golomb_rice_code"
16704 | "elias_gamma_code" | "elias_delta_code" | "exp_golomb_code"
16705 | "fibonacci_code" | "shannon_fano_elias_code"
16706 | "huffman_balanced_step" | "arithmetic_decode_interval"
16707 | "range_decode_step" | "universal_code_length"
16708 | "ziv_lempel_estimate" | "lz77_match_length"
16709 | "lz78_dictionary_growth" | "lzw_step_dict"
16710 | "ppm_predict_prob" | "deflate_huffman_lit"
16711 | "brotli_distance_code_count" | "zstd_window_size_log"
16712 | "mpeg_quant_value" | "jpeg_zig_zag_index"
16713 | "jpeg_dct_8x8_quant" | "hadamard_walsh_transform_step"
16714 | "karhunen_loeve_step" | "discrete_haar_step"
16715 | "db4_wavelet_step" | "biorthogonal_step"
16716 | "beylkin_wavelet_step" | "coiflet_wavelet_step"
16717 | "mallat_pyramid_step" | "threshold_soft_value"
16718 | "threshold_hard_value" | "median_filter_window"
16719 | "mean_filter_window" | "gaussian_filter_window"
16720 | "unsharp_mask_step" | "sobel_kernel_value"
16721 | "prewitt_kernel_value" | "roberts_kernel_value"
16722 | "laplacian_kernel_value" | "canny_threshold_step"
16723 | "hough_accumulator_step" | "ransac_iteration_count"
16724 | "optical_flow_lk_step" | "horn_schunck_step"
16725 | "kalman_predict_state" | "kalman_update_state"
16726 | "particle_filter_resample" | "unscented_sigma_point"
16727 | "ekf_jacobian_step" | "markov_decision_value"
16728 | "bellman_equation_step" | "q_learning_update"
16729 | "policy_iteration_step" | "value_iteration_step"
16730 | "sarsa_update" | "double_q_learning_step"
16731 | "ucb1_action_value" | "thompson_sample_beta"
16732 | "boltzmann_softmax_action" | "explore_exploit_epsilon"
16733 | "montecarlo_returns_step" | "td_zero_update"
16734 | "td_lambda_update" | "gradient_temporal_diff"
16735 | "deep_q_target" | "ddpg_critic_loss_step"
16736 | "ppo_clip_term" | "trpo_kl_constraint"
16737 | "a3c_advantage_step" | "ppo_advantage_step"
16738 | "gae_advantage_step" | "generalized_advantage"
16739 | "information_bottleneck_step" | "free_energy_principle"
16740 | "fisher_info_metric" | "kullback_jensen_div"
16741 | "hellinger_distance_step" | "total_variation_distance"
16742 | "bhattacharyya_coefficient" | "wasserstein_dist_emp"
16743 | "chisquare_metric" | "hellinger_kernel"
16744 | "jensen_shannon_div" | "renyi_divergence_step"
16745 | "amari_alpha_div" | "csiszar_phi_div"
16746 | "sinkhorn_iteration_step" | "sliced_wasserstein"
16747 | "gromov_wasserstein_step" | "spectral_signature_match"
16748 | "mfcc_coeff_step" | "chroma_feature_step"
16749 | "tsp_lower_bound_mst" | "tsp_held_karp_step"
16751 | "christofides_ratio_bound" | "two_opt_swap_delta"
16752 | "or_opt_delta" | "three_opt_delta" | "lin_kernighan_step"
16753 | "nearest_neighbor_tour_step" | "greedy_edge_tour"
16754 | "nearest_insertion_step" | "farthest_insertion_step"
16755 | "cheapest_insertion_step" | "max_flow_ford_fulkerson_step"
16756 | "edmonds_karp_step" | "dinic_blocking_flow"
16757 | "push_relabel_step" | "boykov_kolmogorov_step"
16758 | "mincut_stoer_wagner" | "gomory_hu_step"
16759 | "karger_contract_edge" | "karger_min_cut_count"
16760 | "maximum_bipartite_matching" | "hopcroft_karp_phase"
16761 | "blossom_match_step" | "weighted_match_kuhn_step"
16762 | "hungarian_method_step" | "ap_jonker_volgenant_step"
16763 | "assignment_lower_bound" | "job_shop_makespan_lower"
16764 | "flow_shop_johnson_step" | "parallel_machine_lpt"
16765 | "parallel_machine_spt" | "list_scheduling_step"
16766 | "graham_2approx_bound" | "chc_bound_makespan"
16767 | "bin_packing_first_fit" | "bin_packing_best_fit"
16768 | "bin_packing_next_fit" | "bin_packing_lower_bound_l1"
16769 | "multidim_packing_step" | "knapsack_01_dp_value"
16770 | "knapsack_unbounded_dp" | "knapsack_fractional_step"
16771 | "knapsack_branch_bound" | "knapsack_lp_relaxation"
16772 | "multi_knapsack_step" | "quadratic_assignment_step"
16773 | "qap_lower_bound" | "graph_coloring_dsatur_step"
16774 | "graph_coloring_welsh_powell"
16775 | "graph_coloring_brooks_bound" | "graph_coloring_lp_bound"
16776 | "fractional_chromatic_lower" | "list_coloring_step"
16777 | "edge_coloring_vizing_step" | "clique_number_lower"
16778 | "independence_number_upper" | "vertex_cover_lp_round"
16779 | "dominating_set_greedy_step" | "dominating_set_lp_bound"
16780 | "set_cover_greedy_step" | "set_cover_lp_round"
16781 | "hitting_set_greedy" | "weighted_set_cover_step"
16782 | "matroid_greedy_step" | "matroid_intersection_step"
16783 | "submodular_greedy_step" | "submodular_curvature_bound"
16784 | "nemhauser_wolsey_bound" | "lp_relax_round"
16785 | "branch_and_bound_step" | "cutting_plane_step"
16786 | "gomory_cut_step" | "chvatal_gomory_cut"
16787 | "mixed_integer_round_up" | "mixed_integer_round_down"
16788 | "sos_constraint_check" | "column_generation_step"
16789 | "benders_decomposition_step" | "dantzig_wolfe_step"
16790 | "lagrangian_relax_step" | "lagrangian_dual_step"
16791 | "subgradient_step_size" | "nonlinear_dual_step"
16792 | "augmented_lagrangian_step" | "admm_primal_step"
16793 | "admm_dual_step" | "proximal_gradient_step"
16794 | "nesterov_accelerate_step" | "fista_step" | "ista_step"
16795 | "mirror_descent_step" | "frank_wolfe_step"
16796 | "conditional_gradient_step" | "greedy_set_cover_round"
16797 | "local_search_swap_step" | "tabu_search_move_score"
16798 | "simulated_annealing_step" | "genetic_crossover_one_point"
16799 | "mutation_bit_flip_prob" | "roulette_wheel_select_index"
16800 | "stefan_boltzmann_radiation" | "emissivity_grey_body"
16802 | "albedo_blackbody_balance" | "solar_constant_at_distance"
16803 | "total_solar_irradiance_step" | "absorbed_short_wave"
16804 | "emitted_long_wave" | "clausius_clapeyron_full"
16805 | "relative_humidity_step" | "dewpoint_temperature_full"
16806 | "wet_bulb_potential" | "virtual_temperature_full"
16807 | "density_altitude_full" | "geopotential_height_full"
16808 | "geometric_height_full" | "adiabatic_lapse_rate_dry"
16809 | "adiabatic_lapse_rate_moist" | "brunt_vaisala_full"
16810 | "richardson_number_step" | "gradient_richardson_full"
16811 | "flux_richardson_full" | "turbulent_kinetic_energy_step"
16812 | "mixing_length_prandtl" | "monin_obukhov_length"
16813 | "similarity_function_phi" | "log_law_wind_profile"
16814 | "power_law_wind_profile" | "ekman_layer_depth"
16815 | "ekman_pumping_step" | "geostrophic_wind_step"
16816 | "gradient_wind_step" | "thermal_wind_step"
16817 | "quasi_geostrophic_omega" | "omega_equation_step"
16818 | "potential_temperature_step" | "equivalent_potential_temp"
16819 | "saturation_equivalent_pt" | "ipv_potential_vorticity"
16820 | "ertel_pv_step" | "absolute_vorticity_step"
16821 | "relative_vorticity_step" | "divergence_omega_step"
16822 | "streamfunction_step" | "velocity_potential_step"
16823 | "helmholtz_decomp_step" | "courant_friedrichs_lewy"
16824 | "peclet_number_step" | "prandtl_number_step"
16825 | "reynolds_full_number" | "schmidt_number_step"
16826 | "sherwood_number_step" | "nusselt_full_number"
16827 | "grashof_number_step" | "rayleigh_number_step"
16828 | "weber_number_step" | "froude_number_step"
16829 | "strouhal_full" | "mach_full_step"
16830 | "biot_number_step" | "fourier_number_step"
16831 | "turbulence_intensity_step" | "hurst_exponent_estimate"
16832 | "detrended_fluct_alpha" | "power_spectrum_slope"
16833 | "spectral_kappa_minus53" | "batchelor_scale_step"
16834 | "kolmogorov_microscale" | "taylor_microscale_step"
16835 | "integral_length_scale" | "turbulent_dissipation_eps"
16836 | "isotropic_relation_check" | "sst_anomaly_step"
16837 | "enso_index_step" | "amo_index_step" | "nao_index_step"
16838 | "soi_oscillation_index" | "pdo_index_step" | "mjo_phase_step"
16839 | "walker_circulation_step" | "hadley_cell_max_lat"
16840 | "ferrel_cell_step" | "itcz_position_lat" | "trade_wind_speed"
16841 | "westerlies_jet_speed" | "polar_vortex_radius"
16842 | "arctic_oscillation_step" | "indian_monsoon_index"
16843 | "african_monsoon_index" | "qbo_oscillation_step"
16844 | "solar_cycle_phase" | "sunspot_relative_number"
16845 | "geomagnetic_kp_index" | "ozone_dobson_total"
16846 | "chlorine_radical_decay" | "montreal_protocol_track"
16847 | "co2_growth_rate_step" | "methane_growth_rate"
16848 | "aerosol_optical_depth" | "ice_age_milankovitch"
16849 | "greenhouse_forcing_step"
16850 | "game_two_player_value" | "nash_equilibrium_pair"
16852 | "mixed_strategy_value" | "zero_sum_minmax"
16853 | "saddle_point_check" | "correlated_equilibrium_value"
16854 | "shapley_value_two_step" | "banzhaf_index_two"
16855 | "nucleolus_lp_step" | "core_membership_check"
16856 | "imputation_efficient_check" | "imputation_individual_rational"
16857 | "prisoners_dilemma_payoff" | "matching_pennies_payoff"
16858 | "chicken_game_payoff" | "stag_hunt_payoff"
16859 | "battle_sexes_payoff" | "public_goods_game_payoff"
16860 | "tragedy_commons_metric" | "ultimatum_acceptance_prob"
16861 | "dictator_game_share" | "trust_game_repayment"
16862 | "cooperative_game_value" | "characteristic_function"
16863 | "bargaining_set_check" | "kalai_smorodinsky_step"
16864 | "nash_bargaining_solution" | "egalitarian_solution"
16865 | "utilitarian_solution" | "social_welfare_sum"
16866 | "arrow_impossibility_check" | "gibbard_satterthwaite_check"
16867 | "borda_count_step" | "condorcet_winner_check"
16868 | "plurality_winner_step" | "kemeny_score_step"
16869 | "dodgson_swap_count" | "coombs_runoff_step"
16870 | "single_transferable_vote" | "range_voting_score"
16871 | "approval_voting_max" | "schulze_method_step"
16872 | "copeland_score_step" | "black_method_winner"
16873 | "median_voter_step" | "hotelling_location_step"
16874 | "arrow_pareto_check" | "fair_division_envy_free"
16875 | "proportional_share" | "maximin_share"
16876 | "egalitarian_split" | "nash_social_welfare"
16877 | "divisible_goods_proportional" | "indivisible_envy_free_check"
16878 | "adjusted_winner_pct" | "sealed_bid_first_price"
16879 | "sealed_bid_second_price" | "english_auction_step"
16880 | "dutch_auction_step" | "all_pay_auction_step"
16881 | "vcg_payment_step" | "revenue_equivalence_check"
16882 | "truthful_mechanism_check" | "incentive_compatibility_check"
16883 | "mechanism_design_obj" | "double_auction_step"
16884 | "combinatorial_auction_step" | "posted_price_offer_accept"
16885 | "matching_market_step" | "deferred_acceptance_step"
16886 | "boston_mechanism_step" | "top_trading_cycles_step"
16887 | "school_choice_match" | "roommate_match_step"
16888 | "network_formation_step" | "coordination_game_payoff"
16889 | "evolutionary_stable_strategy" | "replicator_dynamics_step"
16890 | "hawk_dove_payoff" | "fictitious_play_step"
16891 | "best_response_dynamic" | "quantal_response_logit"
16892 | "level_k_step" | "cognitive_hierarchy_step"
16893 | "sequential_eq_check" | "subgame_perfect_eq"
16894 | "stackelberg_step" | "cournot_quantity_step"
16895 | "bertrand_price_step" | "hotelling_price_step"
16896 | "collusion_payoff_step" | "folk_theorem_value"
16897 | "repeated_game_avg_payoff" | "discount_factor_step"
16898 | "trigger_strategy_payoff" | "grim_trigger_step"
16899 | "tit_for_tat_step" | "prisoners_repeated_eq"
16900 | "mertens_zamir_step" | "ex_post_value_check"
16901 | "ex_ante_value_check" | "common_knowledge_iterations"
16902 | "cas_simplify_term" | "cas_expand_two_terms"
16904 | "cas_factor_quadratic" | "cas_partial_fraction_simple"
16905 | "cas_polynomial_gcd_step" | "cas_polynomial_div_step"
16906 | "cas_lagrange_interpolate" | "cas_chebyshev_eval"
16907 | "cas_legendre_eval" | "cas_hermite_eval"
16908 | "cas_laguerre_eval" | "cas_jacobi_eval"
16909 | "cas_gegenbauer_eval" | "cas_taylor_coefficient"
16910 | "cas_padé_diagonal" | "cas_continued_fraction_step"
16911 | "cas_resultant_two" | "cas_subresultant_two"
16912 | "cas_groebner_lt_step" | "cas_buchberger_step"
16913 | "cas_macaulay_matrix_step" | "cas_modular_inverse"
16914 | "cas_extended_euclid_step" | "cas_smith_normal_step"
16915 | "cas_hermite_normal_step" | "cas_radical_simplify"
16916 | "cas_minimal_polynomial" | "cas_gcd_polynomial_step"
16917 | "cas_resultant_x_y" | "cas_solve_linear"
16918 | "cas_solve_quadratic" | "cas_solve_cubic"
16919 | "cas_solve_quartic" | "cas_solve_polynomial_n"
16920 | "cas_root_isolate_step" | "cas_sturm_sequence_step"
16921 | "cas_descartes_rule_count" | "cas_companion_matrix_root"
16922 | "cas_polynomial_roots_kahan"
16923 | "cas_eigenvalue_inverse_iteration" | "cas_qr_iteration_step"
16924 | "cas_jacobi_eigen_step" | "cas_lanczos_iteration_step"
16925 | "cas_arnoldi_iteration_step" | "cas_givens_rotation_apply"
16926 | "cas_householder_reflection" | "cas_modified_gram_schmidt"
16927 | "cas_classical_gram_schmidt" | "cas_rank_revealing_qr"
16928 | "cas_pivoted_lu_step" | "cas_block_lu_step"
16929 | "cas_cholesky_step" | "cas_modified_cholesky"
16930 | "cas_ldlt_step" | "cas_bunch_kaufman_step"
16931 | "cas_woodbury_identity" | "cas_matrix_pencil_step"
16932 | "cas_generalized_eigen" | "cas_singular_value_step"
16933 | "cas_truncated_svd_value" | "cas_pseudoinverse_step"
16934 | "cas_polar_decomposition" | "cas_schur_decomposition_step"
16935 | "cas_quasi_triangular" | "cas_riccati_continuous_step"
16936 | "cas_riccati_discrete_step" | "cas_lyapunov_continuous_step"
16937 | "cas_lyapunov_discrete_step" | "cas_sylvester_equation_step"
16938 | "cas_kronecker_product_step" | "cas_vec_operator_step"
16939 | "cas_matrix_function_step" | "cas_matrix_log_step"
16940 | "cas_matrix_exp_pade" | "cas_matrix_sqrt_step"
16941 | "cas_drazin_inverse_step" | "cas_moore_penrose_step"
16942 | "cas_least_squares_solve" | "cas_total_least_squares"
16943 | "cas_constrained_ls_step" | "cas_truncated_lsq"
16944 | "cas_regularized_lsq_tikhonov" | "cas_basis_pursuit_step"
16945 | "cas_lasso_soft_threshold" | "cas_elastic_net_step"
16946 | "cas_omp_step" | "cas_iht_iteration"
16947 | "cas_cosamp_step" | "cas_admm_lasso_step"
16948 | "cas_proximal_l1_step" | "cas_proximal_l2_step"
16949 | "cas_proximal_l_inf_step" | "cas_indicator_simplex_proj"
16950 | "cas_proj_l1_ball" | "cas_proj_l2_ball"
16951 | "cas_proj_box" | "cas_proj_psd_cone"
16952 | "cas_proj_soc_step" | "cas_proj_exp_cone"
16953 | "cas_dykstra_step" | "cas_alternating_projection"
16954 | "cas_polya_enumeration_step" | "cas_burnside_count_step"
16955 | "ml_relu_step" | "ml_leaky_relu_step" | "ml_elu_step"
16957 | "ml_selu_step" | "ml_gelu_step" | "ml_swish_step"
16958 | "ml_mish_step" | "ml_softplus_step" | "ml_softsign_step"
16959 | "ml_hard_sigmoid" | "ml_hard_tanh" | "ml_prelu_step"
16960 | "ml_celu_step" | "ml_silu_step" | "ml_logsumexp_step"
16961 | "ml_log_softmax_step" | "ml_log_sigmoid"
16962 | "ml_glu_step" | "ml_geglu_step" | "ml_swiglu_step"
16963 | "ml_attention_score_step" | "ml_scaled_dot_product"
16964 | "ml_multihead_avg" | "ml_softmax_temperature"
16965 | "ml_dropout_mask_prob" | "ml_layer_norm_step"
16966 | "ml_batch_norm_step" | "ml_group_norm_step"
16967 | "ml_rms_norm_step" | "ml_instance_norm_step"
16968 | "ml_weight_norm_step" | "ml_spectral_norm_step"
16969 | "ml_l2_normalize_step" | "ml_huber_loss_step"
16970 | "ml_smooth_l1_loss" | "ml_focal_loss_step"
16971 | "ml_dice_loss_step" | "ml_iou_loss_step"
16972 | "ml_giou_loss_step" | "ml_diou_loss_step"
16973 | "ml_ciou_loss_step" | "ml_contrastive_loss"
16974 | "ml_triplet_loss_step" | "ml_arcface_loss_step"
16975 | "ml_center_loss_step" | "ml_kl_divergence_loss"
16976 | "ml_cross_entropy_loss" | "ml_binary_cross_entropy"
16977 | "ml_label_smoothing" | "ml_mixup_lambda"
16978 | "ml_cutmix_box_iou" | "ml_random_erasing_step"
16979 | "ml_cosine_lr_schedule" | "ml_warmup_lr_step"
16980 | "ml_step_lr_schedule" | "ml_exponential_lr"
16981 | "ml_polynomial_lr" | "ml_one_cycle_lr"
16982 | "ml_inverse_sqrt_lr" | "ml_cyclic_lr_step"
16983 | "ml_sgd_step" | "ml_momentum_step"
16984 | "ml_nesterov_momentum" | "ml_adagrad_step"
16985 | "ml_rmsprop_step" | "ml_adam_step"
16986 | "ml_adamw_step" | "ml_adamax_step"
16987 | "ml_nadam_step" | "ml_radam_step"
16988 | "ml_lookahead_step" | "ml_lamb_step"
16989 | "ml_lars_step" | "ml_yogi_step"
16990 | "ml_amsgrad_step" | "ml_adabelief_step"
16991 | "ml_shampoo_step" | "ml_lion_step"
16992 | "ml_sophia_step" | "ml_gradient_clip_norm"
16993 | "ml_gradient_clip_value" | "ml_gradient_accumulate"
16994 | "ml_gradient_centralize" | "ml_weight_decay_step"
16995 | "ml_he_init_value" | "ml_xavier_init_value"
16996 | "ml_glorot_init_value" | "ml_orthogonal_init"
16997 | "ml_truncnormal_init" | "ml_kaiming_init"
16998 | "ml_lecun_init_value" | "ml_zero_init"
16999 | "ml_constant_init" | "ml_uniform_init"
17000 | "ml_one_hot_index" | "ml_label_to_id"
17001 | "ml_id_to_label_step" | "ml_token_logit_top_k"
17002 | "ml_topk_argmax" | "ml_nucleus_sample_p"
17003 | "ml_temperature_decay" | "ml_repetition_penalty"
17004 | "ml_eos_logit_boost"
17005 | "nlp_bm25_score" | "nlp_tf_idf_step" | "nlp_okapi_score"
17007 | "nlp_word_freq_value" | "nlp_doc_freq_step"
17008 | "nlp_inverse_doc_freq" | "nlp_cosine_similarity_two"
17009 | "nlp_jaccard_similarity_two" | "nlp_overlap_coefficient"
17010 | "nlp_dice_coefficient_two" | "nlp_simpson_coefficient"
17011 | "nlp_levenshtein_dist" | "nlp_damerau_levenshtein"
17012 | "nlp_jaro_distance" | "nlp_jaro_winkler"
17013 | "nlp_hamming_distance" | "nlp_lcs_length" | "nlp_lcs_ratio"
17014 | "nlp_meteor_score" | "nlp_bleu_score_n"
17015 | "nlp_rouge_score_n" | "nlp_chrf_score" | "nlp_ter_score"
17016 | "nlp_wer_score" | "nlp_cer_score" | "nlp_perplexity_value"
17017 | "nlp_bits_per_character" | "nlp_char_ngram_count"
17018 | "nlp_word_ngram_count" | "nlp_skip_gram_count"
17019 | "nlp_byte_pair_merge_step" | "nlp_wordpiece_score"
17020 | "nlp_unigram_lm_score" | "nlp_kneser_ney_step"
17021 | "nlp_witten_bell_step" | "nlp_good_turing_count"
17022 | "nlp_laplace_smoothing" | "nlp_lidstone_smoothing"
17023 | "nlp_jelinek_mercer" | "nlp_dirichlet_smoothing"
17024 | "nlp_query_likelihood_step" | "nlp_kl_lm_div"
17025 | "nlp_pmi_score" | "nlp_npmi_score"
17026 | "nlp_chi2_collocation" | "nlp_loglikelihood_collocation"
17027 | "nlp_t_score_collocation" | "nlp_dunning_log_likelihood"
17028 | "nlp_lda_alpha_step" | "nlp_lda_beta_step"
17029 | "nlp_lda_topic_dist" | "nlp_plsa_step"
17030 | "nlp_word2vec_skipgram_loss" | "nlp_word2vec_cbow_loss"
17031 | "nlp_glove_loss_step" | "nlp_fasttext_subword_count"
17032 | "nlp_byte_level_bpe_step" | "nlp_sentencepiece_score"
17033 | "nlp_unigram_subword_loss" | "nlp_subword_regularization"
17034 | "nlp_pointwise_attn_score" | "nlp_relative_position_bias"
17035 | "nlp_alibi_position_bias" | "nlp_rope_rotary_angle"
17036 | "nlp_rope_apply_step" | "nlp_position_encoding_sin"
17037 | "nlp_position_encoding_cos" | "nlp_pe_freq_band"
17038 | "nlp_max_seq_len_check" | "nlp_token_drop_rate"
17039 | "nlp_byte_frequency" | "nlp_char_frequency"
17040 | "nlp_punct_ratio" | "nlp_uppercase_ratio"
17041 | "nlp_digit_ratio" | "nlp_emoji_ratio"
17042 | "nlp_url_count" | "nlp_email_count" | "nlp_phone_count"
17043 | "nlp_hashtag_count" | "nlp_mention_count"
17044 | "nlp_token_overlap_two" | "nlp_word_mover_dist"
17045 | "nlp_sif_weight_step" | "nlp_doc_embedding_avg"
17046 | "nlp_attention_pool_step" | "nlp_max_pool_step"
17047 | "nlp_avg_pool_step" | "nlp_sum_pool_step"
17048 | "nlp_self_attn_compute_step" | "nlp_cross_attn_compute_step"
17049 | "nlp_window_attn_step" | "nlp_strided_attn_step"
17050 | "nlp_block_attn_step" | "nlp_sliding_window_step"
17051 | "nlp_local_attn_step" | "nlp_dilated_attn_step"
17052 | "nlp_global_attn_step" | "nlp_sparse_attn_score"
17053 | "nlp_linformer_step" | "nlp_performer_step"
17054 | "nlp_reformer_step" | "nlp_longformer_step"
17055 | "nlp_bigbird_step" | "nlp_routing_attn_step"
17056 | "gfx_perspective_proj_x" | "gfx_perspective_proj_y"
17058 | "gfx_orthographic_proj" | "gfx_view_matrix_step"
17059 | "gfx_lookat_forward" | "gfx_lookat_right" | "gfx_lookat_up"
17060 | "gfx_quat_to_axis_angle" | "gfx_axis_angle_to_quat"
17061 | "gfx_quat_slerp_step" | "gfx_quat_nlerp_step"
17062 | "gfx_quat_dot_two" | "gfx_quat_inverse_step"
17063 | "gfx_quat_to_euler_pitch" | "gfx_quat_to_euler_yaw"
17064 | "gfx_quat_to_euler_roll" | "gfx_euler_to_quat_x"
17065 | "gfx_euler_to_quat_y" | "gfx_euler_to_quat_z"
17066 | "gfx_euler_to_quat_w" | "gfx_rotation_matrix_xx"
17067 | "gfx_rotation_matrix_yy" | "gfx_rotation_matrix_zz"
17068 | "gfx_translation_matrix_step" | "gfx_scale_matrix_step"
17069 | "gfx_shear_matrix_xy" | "gfx_homogeneous_divide"
17070 | "gfx_screen_space_x" | "gfx_screen_space_y"
17071 | "gfx_ndc_to_screen_x" | "gfx_ndc_to_screen_y"
17072 | "gfx_screen_to_ndc_x" | "gfx_screen_to_ndc_y"
17073 | "gfx_clip_polygon_step" | "gfx_sutherland_hodgman"
17074 | "gfx_cohen_sutherland_code" | "gfx_liang_barsky_t"
17075 | "gfx_bresenham_step_x" | "gfx_bresenham_step_y"
17076 | "gfx_xiaolin_wu_intensity" | "gfx_aabb_intersect_check"
17077 | "gfx_obb_overlap_step" | "gfx_sphere_intersect_t"
17078 | "gfx_ray_triangle_t" | "gfx_ray_plane_t" | "gfx_ray_box_t"
17079 | "gfx_ray_sphere_t" | "gfx_ray_disk_t"
17080 | "gfx_ray_cylinder_t" | "gfx_ray_cone_t"
17081 | "gfx_ray_ellipsoid_t" | "gfx_ray_torus_t_approx"
17082 | "gfx_barycentric_alpha" | "gfx_barycentric_beta"
17083 | "gfx_barycentric_gamma" | "gfx_phong_diffuse_step"
17084 | "gfx_phong_specular_step" | "gfx_phong_ambient_step"
17085 | "gfx_blinn_specular_step" | "gfx_lambert_term"
17086 | "gfx_oren_nayar_term" | "gfx_cook_torrance_d_ggx"
17087 | "gfx_cook_torrance_g_smith" | "gfx_cook_torrance_f_schlick"
17088 | "gfx_disney_principled_d" | "gfx_microfacet_brdf_step"
17089 | "gfx_subsurface_scattering_term" | "gfx_translucent_falloff"
17090 | "gfx_normal_distribution_ggx"
17091 | "gfx_geometric_attenuation_smith"
17092 | "gfx_fresnel_dielectric_step" | "gfx_fresnel_conductor_step"
17093 | "gfx_index_of_refraction" | "gfx_snells_law_angle"
17094 | "gfx_total_internal_reflection" | "gfx_refract_direction_x"
17095 | "gfx_reflect_direction_x" | "gfx_environment_map_uv_u"
17096 | "gfx_environment_map_uv_v" | "gfx_cube_map_face_index"
17097 | "gfx_octahedral_encode_x" | "gfx_octahedral_encode_y"
17098 | "gfx_spherical_harmonic_y00" | "gfx_spherical_harmonic_y10"
17099 | "gfx_spherical_harmonic_y11" | "gfx_spherical_harmonic_y20"
17100 | "gfx_zonal_harmonic_step" | "gfx_irradiance_sh_eval"
17101 | "gfx_radiance_sh_eval" | "gfx_skybox_uv_u" | "gfx_skybox_uv_v"
17102 | "gfx_tonemap_reinhard" | "gfx_tonemap_aces"
17103 | "gfx_tonemap_uncharted2" | "gfx_tonemap_filmic"
17104 | "gfx_gamma_correct_step" | "gfx_srgb_to_linear"
17105 | "gfx_linear_to_srgb" | "gfx_dither_bayer_4x4"
17106 | "gfx_dither_floyd_steinberg" | "gfx_oklab_l_step"
17107 | "gfx_oklab_a_step" | "gfx_oklab_b_step"
17108 | "gfx_oklch_chroma" | "gfx_oklch_hue"
17109 | "gfx_pcg_hash_step" | "gfx_xorshift_step"
17110 | "gfx_halton_step" | "gfx_sobol_step"
17111 | "gfx_van_der_corput" | "gfx_low_discrepancy_step"
17112 | "gfx_blue_noise_value" | "gfx_perlin_noise_step"
17113 | "gfx_simplex_noise_step" | "gfx_fbm_noise_step"
17114 | "gfx_worley_noise_step" | "gfx_voronoi_distance"
17115 | "gfx_curl_noise_step" | "gfx_gradient_noise_step"
17116 | "gfx_value_noise_step" | "gfx_signed_distance_box"
17117 | "gfx_signed_distance_sphere" | "gfx_signed_distance_capsule"
17118 | "db_b_tree_split" | "db_b_tree_merge"
17120 | "db_lsm_compaction_step" | "db_skiplist_height_pick"
17121 | "db_bloom_filter_bit_index" | "db_cuckoo_filter_fingerprint"
17122 | "db_quotient_filter_canonical" | "db_count_min_sketch_bin"
17123 | "db_hyperloglog_register_max" | "db_min_hash_value"
17124 | "db_simhash_bit" | "db_consistent_hash_index"
17125 | "db_rendezvous_hash_score" | "db_jump_hash_bucket"
17126 | "db_maglev_hash_step" | "db_lru_cache_eviction_age"
17127 | "db_lfu_cache_decay" | "db_arc_cache_score"
17128 | "db_clock_cache_hand" | "db_tinylfu_admit_score"
17129 | "db_w_tinylfu_freq" | "db_buffer_pool_score"
17130 | "db_query_plan_cost_step" | "db_join_selectivity_step"
17131 | "db_index_seek_cost" | "db_seq_scan_cost"
17132 | "db_index_scan_cost" | "db_sort_cost_estimate"
17133 | "db_hash_join_cost" | "db_merge_join_cost"
17134 | "db_nested_loop_cost" | "db_query_cardinality"
17135 | "db_histogram_bucket_index" | "db_quantile_estimate_p99"
17136 | "db_t_digest_centroid" | "db_kll_quantile_step"
17137 | "db_dd_sketch_bin" | "db_reservoir_sample_index"
17138 | "db_chao_estimator_step" | "db_jaccard_minhash_estimate"
17139 | "db_distinct_estimate_lpc" | "db_distinct_estimate_hll"
17140 | "db_throttle_token_step" | "db_leaky_bucket_step"
17141 | "db_token_bucket_step" | "db_circuit_breaker_step"
17142 | "db_two_phase_commit_step" | "db_three_phase_commit_step"
17143 | "db_paxos_propose_id" | "db_raft_term_advance"
17144 | "db_raft_log_match_check" | "db_zab_epoch_step"
17145 | "db_chubby_lease_step" | "db_logical_clock_step"
17146 | "db_lamport_timestamp" | "db_vector_clock_merge"
17147 | "db_hybrid_logical_clock" | "db_crdt_g_counter_merge"
17148 | "db_crdt_pn_counter_merge" | "db_crdt_lww_register_merge"
17149 | "db_crdt_set_or_merge" | "db_consensus_quorum_size"
17150 | "db_replication_lag_step" | "db_partitions_for_n"
17151 | "db_consistent_lookup_id" | "db_chord_finger_index"
17152 | "db_kademlia_xor_distance" | "db_pastry_routing_step"
17153 | "db_dht_replicate_factor" | "db_partition_failure_check"
17154 | "db_byzantine_quorum_size" | "db_pbft_view_change"
17155 | "db_honey_badger_step" | "db_avalanche_query_step"
17156 | "db_quorum_intersection_check" | "db_anti_entropy_step"
17157 | "db_merkle_node_hash" | "db_merkle_path_verify"
17158 | "db_gossip_fanout_step" | "db_anti_entropy_pull_step"
17159 | "db_split_brain_check" | "db_clock_skew_estimate"
17160 | "db_freshness_score" | "db_read_repair_step"
17161 | "db_hinted_handoff_step" | "db_compaction_score"
17162 | "db_levelled_compaction_step" | "db_size_tiered_compaction"
17163 | "db_universal_compaction_step" | "db_write_amplification"
17164 | "db_read_amplification" | "db_space_amplification"
17165 | "db_block_cache_hit_rate" | "db_page_cache_eviction_age"
17166 | "db_wal_fsync_cost" | "db_group_commit_count"
17167 | "db_replica_lag_threshold" | "db_synchronous_commit_check"
17168 | "db_async_commit_check" | "db_eventual_consistency_check"
17169 | "db_strong_consistency_check" | "db_linearizability_check"
17170 | "db_causal_consistency_check"
17171 | "net_tcp_cwnd_step" | "net_tcp_ssthresh_update"
17173 | "net_tcp_reno_step" | "net_tcp_cubic_step"
17174 | "net_tcp_bbr_step" | "net_tcp_vegas_step"
17175 | "net_tcp_westwood_step" | "net_tcp_compound_step"
17176 | "net_tcp_dctcp_step" | "net_tcp_yeah_step"
17177 | "net_tcp_htcp_step" | "net_tcp_hybla_step"
17178 | "net_tcp_illinois_step" | "net_tcp_lp_step"
17179 | "net_tcp_scalable_step" | "net_tcp_veno_step"
17180 | "net_aiad_step" | "net_aimd_step"
17181 | "net_miad_step" | "net_mimd_step"
17182 | "net_aqm_red_drop_prob" | "net_aqm_codel_target"
17183 | "net_aqm_pie_drop_rate" | "net_aqm_fq_codel_step"
17184 | "net_aqm_blue_step" | "net_aqm_choke_step"
17185 | "net_aqm_sfq_step" | "net_aqm_drr_step"
17186 | "net_aqm_wrr_step" | "net_token_rate_limit"
17187 | "net_traffic_shaper_step" | "net_priority_queue_index"
17188 | "net_packet_loss_estimate" | "net_jitter_estimate"
17189 | "net_latency_avg" | "net_rtt_smoothed"
17190 | "net_rtt_variation" | "net_rto_compute"
17191 | "net_bandwidth_delay_product" | "net_path_capacity_kleinrock"
17192 | "net_loss_rate_to_throughput" | "net_throughput_padhye"
17193 | "net_throughput_mathis" | "net_throughput_response"
17194 | "net_router_buffer_size" | "net_drop_tail_check"
17195 | "net_burst_size_compute" | "net_packet_pacing_step"
17196 | "net_link_capacity_share" | "net_proportional_fair_share"
17197 | "net_max_min_fair_step" | "net_alpha_fair_step"
17198 | "net_kelly_pricing_step" | "net_network_utility_max"
17199 | "net_lyapunov_drift_plus_penalty" | "net_backpressure_step"
17200 | "net_max_weight_match" | "net_qcsma_propose"
17201 | "net_csma_back_off" | "net_alohanet_throughput"
17202 | "net_slotted_aloha_throughput" | "net_csma_efficiency"
17203 | "net_token_ring_efficiency" | "net_polling_efficiency"
17204 | "net_radio_path_loss" | "net_friis_received_power"
17205 | "net_two_ray_ground_loss" | "net_okumura_hata_loss"
17206 | "net_log_distance_path" | "net_shadowing_normal"
17207 | "net_rician_k_factor" | "net_rayleigh_envelope"
17208 | "net_doppler_shift" | "net_capacity_shannon"
17209 | "net_mimo_capacity_step" | "net_zero_forcing_beam"
17210 | "net_mmse_beam_step" | "net_water_filling_power"
17211 | "net_amc_threshold_index" | "net_harq_combining_gain"
17212 | "net_turbo_decode_iter" | "net_ldpc_iteration_step"
17213 | "net_polar_decode_step" | "net_viterbi_step"
17214 | "net_bcjr_step" | "net_outage_probability"
17215 | "net_diversity_gain" | "net_array_gain"
17216 | "net_multiplexing_gain" | "net_coding_gain"
17217 | "net_pruning_gain" | "net_macro_diversity_step"
17218 | "net_micro_diversity_step" | "net_handoff_threshold"
17219 | "net_call_admission_check" | "net_blocking_probability"
17220 | "net_erlang_b_formula" | "net_erlang_c_formula"
17221 | "net_engset_formula" | "net_little_law_l"
17222 | "net_throughput_law" | "net_response_time_law"
17223 | "net_utilization_law" | "net_forced_flow_law"
17224 | "os_priority_aging_step" | "os_mlfq_demote_step"
17226 | "os_mlfq_promote_step" | "os_round_robin_quantum"
17227 | "os_completely_fair_vruntime" | "os_lottery_ticket_count"
17228 | "os_stride_pass_step" | "os_eevdf_eligible"
17229 | "os_cfs_load_balance_step" | "os_eas_energy_estimate"
17230 | "os_smt_threading_share" | "os_numa_node_distance"
17231 | "os_cpu_affinity_score" | "os_thread_migration_cost"
17232 | "os_load_average_decay" | "os_runqueue_depth"
17233 | "os_io_scheduler_deadline" | "os_io_scheduler_cfq_step"
17234 | "os_io_scheduler_noop_step" | "os_io_scheduler_bfq_step"
17235 | "os_io_scheduler_kyber_step" | "os_io_scheduler_mq_deadline"
17236 | "os_anticipation_window" | "os_elevator_step"
17237 | "os_disk_seek_time" | "os_disk_rotational_lat"
17238 | "os_disk_transfer_time" | "os_pre_fetch_window"
17239 | "os_buffer_cache_pages" | "os_dirty_page_threshold"
17240 | "os_writeback_step" | "os_swappiness_factor"
17241 | "os_kswapd_wake_threshold" | "os_oom_score_step"
17242 | "os_page_replacement_lru" | "os_page_replacement_clock"
17243 | "os_page_replacement_2q" | "os_working_set_size"
17244 | "os_thrashing_threshold" | "os_demand_paging_step"
17245 | "os_copy_on_write_check" | "os_zero_page_optimization"
17246 | "os_huge_page_threshold" | "os_transparent_hugepage"
17247 | "os_kasan_shadow_offset" | "os_kfence_check"
17248 | "os_kfence_alloc_index" | "os_slub_object_size_round"
17249 | "os_slab_color_offset" | "os_per_cpu_cache_size"
17250 | "os_buddy_order_pick" | "os_compact_memory_step"
17251 | "os_kvm_vmcs_field_offset" | "os_apic_irq_priority"
17252 | "os_msi_x_vector_count" | "os_iommu_domain_step"
17253 | "os_pci_bus_address" | "os_acpi_state_transition"
17254 | "os_cpufreq_governor_step" | "os_intel_pstate_target"
17255 | "os_amd_pstate_target" | "os_thermal_zone_trip"
17256 | "os_throttle_temperature" | "os_battery_capacity_pct"
17257 | "os_powertop_score" | "os_idle_state_select"
17258 | "os_c_state_residency" | "os_p_state_voltage"
17259 | "os_dvfs_step" | "os_voltage_scaling_step"
17260 | "os_frequency_scaling_step" | "os_inotify_event_count"
17261 | "os_epoll_ctl_count" | "os_io_uring_sqe_count"
17262 | "os_io_uring_cqe_count" | "os_kqueue_event_count"
17263 | "os_systemd_journal_size" | "os_dmesg_severity_level"
17264 | "os_audit_event_priority" | "os_apparmor_profile_active"
17265 | "os_selinux_context_match" | "os_smack_label_compare"
17266 | "os_capability_check" | "os_seccomp_filter_step"
17267 | "os_namespace_isolation" | "os_cgroup_v1_count"
17268 | "os_cgroup_v2_count" | "os_pid_max_value"
17269 | "os_thread_max_value" | "os_file_max_value"
17270 | "os_open_files_count" | "os_socket_max_value"
17271 | "os_inotify_max_watches" | "os_oom_kill_score"
17272 | "os_zswap_compress_ratio" | "os_zram_compress_ratio"
17273 | "os_swap_pressure_score" | "os_pressure_stall_step"
17274 | "os_psi_avg10_step" | "os_psi_avg60_step"
17275 | "os_psi_avg300_step" | "os_load_proc_avg"
17276 | "os_load_user_avg" | "os_load_iowait_avg"
17277 | "sec_argon2_memcost" | "sec_argon2_timecost"
17279 | "sec_argon2_parallelism" | "sec_argon2_block_step"
17280 | "sec_pbkdf2_iter" | "sec_scrypt_n_param"
17281 | "sec_scrypt_r_param" | "sec_scrypt_p_param"
17282 | "sec_balloon_hash_step" | "sec_yescrypt_step"
17283 | "sec_bcrypt_cost_factor" | "sec_bcrypt_round_step"
17284 | "sec_password_strength_zxcvbn" | "sec_haveibeenpwned_check"
17285 | "sec_diceware_word_index" | "sec_xkcd_passphrase_score"
17286 | "sec_passphrase_entropy" | "sec_chosen_charset_strength"
17287 | "sec_keystroke_timing_var" | "sec_2fa_totp_window"
17288 | "sec_totp_drift_check" | "sec_hotp_counter_step"
17289 | "sec_yubikey_otp_check" | "sec_webauthn_attestation_check"
17290 | "sec_fido2_assertion_check" | "sec_certificate_chain_depth"
17291 | "sec_revocation_ocsp_check" | "sec_crl_age_seconds"
17292 | "sec_pki_path_validate" | "sec_x509_subject_match"
17293 | "sec_san_match_count" | "sec_basic_constraints_ca"
17294 | "sec_pinning_compare" | "sec_certificate_transparency"
17295 | "sec_dane_tlsa_match" | "sec_hpkp_pin_match"
17296 | "sec_csp_directive_match" | "sec_csrf_token_match"
17297 | "sec_cors_origin_match" | "sec_xss_filter_score"
17298 | "sec_html_escape_check" | "sec_url_safe_encode_check"
17299 | "sec_path_traversal_detect" | "sec_sqli_pattern_score"
17300 | "sec_xxe_pattern_score" | "sec_xxe_dtd_check"
17301 | "sec_command_injection_score" | "sec_idor_check"
17302 | "sec_jwt_alg_safe" | "sec_jwt_kid_match"
17303 | "sec_jwt_signature_verify" | "sec_oauth2_state_validate"
17304 | "sec_oauth2_pkce_step" | "sec_oauth_nonce_check"
17305 | "sec_session_lifetime" | "sec_idle_timeout_step"
17306 | "sec_login_throttle_step" | "sec_account_lockout_step"
17307 | "sec_password_history_check" | "sec_complexity_policy_score"
17308 | "sec_dictionary_attack_check" | "sec_brute_force_attempts"
17309 | "sec_credential_stuffing_score" | "sec_kerberos_ticket_age"
17310 | "sec_kerberos_pac_check" | "sec_kerberos_pre_auth"
17311 | "sec_ldap_bind_step" | "sec_radius_auth_step"
17312 | "sec_diameter_avp_step" | "sec_saml_assertion_age"
17313 | "sec_oidc_id_token_age" | "sec_acme_dns_challenge"
17314 | "sec_dnssec_signature_check" | "sec_spf_pass_check"
17315 | "sec_dkim_signature_check" | "sec_dmarc_policy_check"
17316 | "sec_arc_chain_step" | "sec_smtp_ssl_check"
17317 | "sec_imap_starttls_check" | "sec_pop3_security_step"
17318 | "sec_tls_alert_severity" | "sec_tls13_handshake_step"
17319 | "sec_tls12_handshake_step" | "sec_tls11_deprecation_check"
17320 | "sec_ssl3_disabled_check" | "sec_cipher_suite_strength"
17321 | "sec_cbc_mac_block_count" | "sec_gcm_iv_unique_check"
17322 | "sec_chachapoly_nonce_check" | "sec_x25519_clamping_step"
17323 | "sec_ed25519_signature_step" | "sec_ed448_signature_step"
17324 | "sec_p384_curve_step" | "sec_secp256k1_step"
17325 | "sec_blake3_chunk_step" | "sec_keccak_round_step"
17326 | "sec_sha3_padding_step" | "sec_argon2_state_advance"
17327 | "sec_chacha20_quarterround" | "sec_aes_round_step"
17328 | "sec_aes_keyschedule_step" | "sec_des_round_step"
17329 | "sec_blowfish_round_step" | "sec_serpent_round_step"
17330 | "sec_twofish_round_step"
17331 | "fixed_from_gregorian" | "gregorian_from_fixed"
17333 | "fixed_from_julian" | "julian_from_fixed"
17334 | "iso_week_date" | "hebrew_leap_year"
17335 | "hebrew_year_length" | "fixed_from_hebrew"
17336 | "islamic_leap_year" | "fixed_from_islamic"
17337 | "persian_arithmetic_leap" | "fixed_from_persian"
17338 | "coptic_from_fixed" | "ethiopic_from_fixed"
17339 | "french_revolutionary_leap" | "fixed_from_french"
17340 | "chinese_year_zodiac" | "chinese_lunation_winter"
17341 | "hindu_solar_year" | "hindu_lunisolar_month"
17342 | "maya_long_count_from_fixed" | "mayan_haab_from_fixed"
17343 | "mayan_tzolkin_from_fixed" | "badi_year_from_fixed"
17344 | "bahai_from_fixed" | "easter_gregorian_year"
17345 | "easter_orthodox_year" | "easter_julian_year"
17346 | "day_of_week_zeller" | "iso_day_number"
17347 | "weekday_name_short" | "leap_year_gregorian"
17348
17349 | "dnorm" | "dt" | "df_dist" | "dchisq"
17351 | "glm" | "aov" | "shapiro_wilk" | "anderson_darling"
17352 | "kolmogorov_smirnov" | "spearmanr" | "kendalltau" | "pearsonr"
17353 | "mannwhitneyu" | "wilcoxon" | "kruskal_h"
17354
17355 | "iota_n" | "reduce_axis" | "scan_axis" | "fold_axis"
17357 | "rotate_axis" | "transpose_axis" | "reshape_dim"
17358 | "encode_base" | "decode_base" | "nub_list" | "nub_count"
17359 | "membership_idx" | "deal_n_k" | "roll_n"
17360 | "permute_idx" | "invert_perm"
17361
17362 | "julian_day" | "jd_to_calendar" | "tt_to_tdb"
17364 | "ra_dec_to_alt_az" | "alt_az_to_ra_dec"
17365 | "precession_iau2006" | "nutation_iau2000a"
17366 | "aberration_annual" | "proper_motion_apply"
17367 | "parallax_correction" | "sun_position_low" | "sun_distance_au"
17368 | "moon_position_low" | "moon_phase_age" | "lunation_index"
17369 | "eclipse_magnitude" | "saros_cycle" | "metonic_cycle"
17370 | "orbit_kepler3" | "orbital_period_au" | "orbit_eccentric_anomaly"
17371 | "escape_velocity_body" | "hill_sphere_radius" | "tisserand_param"
17372 | "tle_mean_motion" | "sgp4_propagate_step" | "airy_disk_radius"
17373 | "rayleigh_criterion" | "strehl_ratio" | "au_to_km"
17374
17375 | "elo_expected" | "elo_update" | "glicko_rating"
17377 | "trueskill_update" | "trueskill_match_quality"
17378 | "pythagorean_expectation" | "war_above_replacement"
17379 | "woba_weight" | "wrc_plus" | "ops_plus" | "era_plus"
17380 | "fip" | "xfip" | "siera" | "babip" | "wpa"
17381 | "win_probability" | "leverage_index" | "clutch_score"
17382 | "shooting_pct" | "save_pct" | "corsi_for" | "fenwick_for"
17383 | "goals_above_avg" | "tackle_efficiency" | "yards_per_attempt"
17384 | "qbr_metric" | "epa_per_play"
17385
17386 | "vlookup" | "hlookup" | "xlookup" | "index_match"
17388 | "indirect" | "choose" | "offset"
17389 | "sumif" | "countif" | "averageif"
17390 | "sumifs" | "countifs" | "averageifs"
17391 | "sumproduct" | "rank_eq" | "rank_avg" | "percentrank"
17392 | "quartile_inc" | "quartile_exc"
17393 | "xnpv" | "ppmt" | "ipmt" | "rate"
17394 | "macauley_duration" | "convexity" | "yield_to_maturity"
17395 | "accrued_interest" | "clean_price" | "dirty_price"
17396 | "coupon_count" | "skill_score" | "reliability_diagram"
17397 | "taylor_diagram_score"
17398
17399 | "geohash_neighbors" | "h3_index" | "h3_geo_to_h3"
17401 | "h3_h3_to_geo" | "h3_k_ring" | "h3_neighbor" | "h3_resolution"
17402 | "s2_cell_id" | "s2_cell_at_lat_lng" | "s2_cell_neighbors"
17403 | "utm_from_lat_lng" | "utm_to_lat_lng"
17404 | "mgrs_encode" | "mgrs_decode"
17405 | "lat_lng_to_xy_mercator" | "lat_lng_to_xy_lambert"
17406 | "haversine_dist" | "vincenty_dist" | "andoyer_dist"
17407 | "rhumb_line_bearing"
17408 | "destination_point" | "tile_xyz_to_lat_lng" | "lat_lng_to_tile_xyz"
17409 | "polygon_winding_order" | "point_in_polygon_ray"
17410 | "point_in_polygon_winding" | "segment_intersection"
17411 | "segment_distance_point" | "convex_hull_chan"
17412
17413 | "pid_anti_windup" | "pid_ziegler_nichols"
17415 | "smith_predictor_step" | "lqr_gain_continuous"
17416 | "lqr_gain_discrete" | "lqg_step" | "h_infinity_norm"
17417 | "bode_gain_margin" | "bode_phase_margin"
17418 | "nyquist_encirclement" | "nichols_chart_step"
17419 | "servo_position_velocity" | "servo_torque_step"
17420 | "imu_madgwick_step" | "imu_mahony_step" | "quaternion_from_imu"
17421 | "denavit_hartenberg_h" | "forward_kinematics_dh"
17422 | "inverse_kinematics_2link" | "jacobian_2dof"
17423 | "manipulability_yoshikawa" | "singularity_check_2link"
17424 | "path_dubins_lsl" | "path_dubins_rsr" | "path_reeds_shepp"
17425 | "rrt_extend" | "rrt_star_rewire" | "prm_node_connect"
17426
17427 | "life_expectancy_e0" | "force_of_mortality" | "select_ultimate"
17429 | "annuity_due_an" | "annuity_immediate_an"
17430 | "term_life_a_n_t" | "whole_life_a"
17431 | "endowment_pure_e" | "endowment_combined_a"
17432 | "premium_net" | "level_premium"
17433 | "reserve_prospective" | "reserve_retrospective"
17434 | "gross_premium_load" | "experience_factor"
17435 | "mortality_table_q" | "select_period_step"
17436 | "multi_decrement_q" | "multi_state_pij"
17437 | "credibility_buhlmann" | "loss_severity_lognormal"
17438 | "loss_frequency_poisson" | "ruin_probability_lundberg"
17439 | "cramer_lundberg_step" | "bornhuetter_ferguson"
17440 | "chain_ladder_step" | "ibnr_estimate" | "run_off_triangle_step"
17441
17442 | "r_naught_basic" | "r_effective_t" | "doubling_time_growth"
17444 | "sirs_step" | "seirs_step" | "susceptible_to_infected"
17445 | "attack_rate" | "vaccination_coverage_required"
17446 | "cfr_case_fatality" | "ifr_infection_fatality"
17447 | "dalys_disability_weight" | "qaly_lifetime" | "ylll_pml"
17448 | "rt_serial_interval" | "generation_time_step"
17449 | "gini_inequality_health" | "standardized_mortality_smr"
17450 | "indirect_age_adjusted" | "direct_age_adjusted"
17451 | "odds_ratio_2x2" | "risk_ratio_2x2" | "number_needed_to_treat"
17452 | "attributable_fraction_pop" | "preventive_fraction"
17453 | "contact_tracing_eff" | "cluster_attack_rate"
17454 | "transmission_pair_index"
17455
17456 | "tar_header_checksum" | "tar_pad_512" | "tar_member_record"
17458 | "zip_local_header" | "zip_central_dir" | "zip_eocd"
17459 | "gzip_member_step" | "gzip_crc32_init" | "gzip_isize"
17460 | "deflate_dynamic_huffman" | "deflate_static_block"
17461 | "lz4_block_step" | "lz4_match_offset"
17462 | "zstd_frame_header" | "brotli_huffman_table"
17463 | "brotli_meta_block" | "lzma_range_step"
17464 | "quoted_printable_encode" | "uuencode_step"
17465 | "modhex_encode" | "percent_encode_full"
17466 | "punycode_encode" | "idn_to_ascii" | "idn_to_unicode"
17467 | "msgpack_pack_int" | "msgpack_pack_str"
17468 | "cbor_encode_uint" | "cbor_encode_str"
17469
17470 | "molecular_weight_compound" | "molarity_dilution"
17472 | "gas_constant_value" | "eyring_rate" | "van_t_hoff_kp"
17473 | "henderson_buffer" | "titration_ph_endpoint"
17474 | "isoelectric_point_protein" | "ka_to_pka" | "pkb_to_kb"
17475 | "amphoteric_check" | "oxidation_number"
17476 | "half_reaction_balance" | "redox_potential_cell"
17477 | "electrolysis_mass" | "spectrophotometer_beer_lambert"
17478 | "epsilon_extinction" | "transmittance_to_a"
17479 | "crystal_field_ligand" | "jahn_teller_check"
17480 | "vsepr_geometry" | "lewis_dot_count"
17481 | "formal_charge" | "resonance_count"
17482 | "ramachandran_phi_psi" | "rg_radius_of_gyration"
17483 | "spectroscopic_factor" | "avogadro_constant"
17484
17485 | "cents_between_freqs" | "note_name_from_midi"
17487 | "interval_quality_size" | "scale_pitches_major"
17488 | "scale_pitches_minor" | "mode_pitches_dorian"
17489 | "mode_pitches_phrygian" | "mode_pitches_lydian"
17490 | "chord_root_inversion" | "chord_quality_classify"
17491 | "chord_voicing_close" | "key_signature_sharps"
17492 | "key_signature_flats" | "tempo_to_ms" | "beat_to_seconds"
17493 | "time_sig_subdivision" | "equal_tempered_freq"
17494 | "just_intonation_freq" | "pythagorean_freq"
17495 | "mean_tone_freq" | "werckmeister_iii" | "kirnberger_iii"
17496 | "dynamics_db_level" | "harmonics_partial"
17497
17498 | "moment_magnitude_mw" | "richter_local_ml"
17500 | "surface_wave_ms" | "body_wave_mb"
17501 | "gutenberg_richter_b" | "omori_aftershock"
17502 | "pga_attenuation" | "arias_intensity" | "shake_map_pga"
17503 | "liquefaction_potential_index" | "spt_n_correction"
17504 | "mineral_mohs_hardness" | "streak_color_index"
17505 | "specific_gravity_water" | "feldspar_classify"
17506 | "silicate_classify" | "igneous_qapf"
17507 | "metamorphic_grade" | "crustal_density_depth"
17508 | "pwave_velocity_depth" | "swave_velocity_depth"
17509 | "gradient_geothermal" | "heat_flow_radiogenic"
17510
17511 | "dgemm" | "sgemm" | "zgemm" | "cgemm"
17513 | "dgemv" | "sgemv" | "dtrsm" | "strsm"
17514 | "dgesv" | "dgetrf" | "dgeqrf" | "dgesvd"
17515 | "dsyevd" | "dpotrf" | "daxpy" | "ddot"
17516 | "dnrm2" | "dscal" | "dasum" | "idamax"
17517 | "dsyrk" | "dgerqf" | "dorgqr" | "dorglq"
17518 | "drot" | "drotg" | "dpbsv" | "dgbsv"
17519 | "dtbsv" | "dtrsv" | "ddrot" | "dgemm3m"
17520 | "dgels" | "dgelsd"
17521
17522 | "cnf_unit_propagate" | "cnf_pure_literal_elim"
17524 | "cnf_dpll_branch" | "dpll_clause_learning"
17525 | "two_watched_literals" | "walksat_step"
17526 | "resolution_step" | "subsumption_check"
17527 | "tableau_branch_close" | "sequent_left_intro"
17528 | "sequent_right_intro" | "nbe_normalize"
17529 | "church_numeral_n" | "encode_pair" | "encode_succ"
17530 | "simply_typed_check" | "hindley_milner_step"
17531 | "unification_robinson" | "bdd_apply" | "bdd_restrict"
17532 | "bdd_quantify" | "aig_simplify_step"
17533 | "smt_qf_lia_solve_step" | "smt_qf_uf_combine"
17534 | "model_checking_ctl" | "model_checking_ltl"
17535 | "bisimulation_step" | "coq_tactic_apply"
17536 | "coq_unify_term" | "refl_check" | "sym_check" | "trans_check"
17537
17538 | "nfa_to_dfa" | "subset_construction"
17540 | "dfa_minimize_hopcroft" | "regex_to_nfa_thompson"
17541 | "glushkov_construction" | "brzozowski_derivative"
17542 | "ll1_first_set" | "ll1_follow_set" | "ll1_predict_table"
17543 | "lr0_items_step" | "lalr_lookahead_compute"
17544 | "lr1_canonical_collection"
17545 | "earley_scan" | "earley_predict" | "earley_complete"
17546 | "packrat_parse_step" | "ascent_parser_step"
17547 | "pratt_parse_step" | "shunting_yard_step"
17548 | "regex_compile_thompson" | "regex_match_dfa"
17549 | "lex_keyword_classify"
17550 | "peg_seq" | "peg_choice" | "peg_repeat" | "peg_lookahead"
17551 | "dfa_simulate_step" | "bytecode_disasm_step"
17552 | "ssa_phi_insert" | "dom_tree_idom" | "dominance_frontier"
17553
17554 | "porter_stem_step" | "snowball_stem_english"
17556 | "snowball_stem_french" | "lemmatize_wordnet"
17557 | "lemmatize_lemmy" | "stem_lancaster"
17558 | "soundex_phonetic" | "metaphone_phonetic"
17559 | "caverphone_2" | "nysiis_phonetic"
17560 | "match_rating_codex" | "daitch_mokotoff"
17561 | "viterbi_pos_tag" | "forward_backward_pos"
17562 | "crf_log_likelihood" | "bigram_perplexity"
17563 | "trigram_perplexity" | "ner_bilou_decode"
17564 | "constituency_cyk" | "dependency_parse_eisner"
17565 | "transition_arc_eager" | "transition_arc_standard"
17566 | "word_alignment_ibm1" | "word_alignment_ibm2"
17567 | "lexicalized_parse" | "coreference_singleton"
17568 | "anaphora_distance" | "head_finding_collins"
17569 | "tree_kernel_collins"
17570
17571 | "btrim" | "translate" | "ascii"
17573 | "regexp_split" | "regexp_matches" | "regexp_replace"
17574 | "json_build_object" | "jsonb_set"
17575 | "json_array_length" | "json_extract_path"
17576 | "json_strip_nulls" | "jsonb_pretty"
17577 | "jsonb_path_query" | "json_each"
17578 | "jsonb_array_length" | "jsonb_object_keys"
17579 | "jsonb_typeof" | "array_to_jsonb"
17580 | "ts_match" | "ts_rank" | "ts_headline"
17581 | "substring_similarity" | "levenshtein_dist"
17582 | "word_similarity" | "strict_word_similarity"
17583 | "hstore_to_array" | "array_to_hstore"
17584 | "string_agg" | "array_agg"
17585 | "corr_agg" | "covar_pop" | "covar_samp"
17586 | "regr_slope" | "regr_intercept" | "regr_r2"
17587 | "percentile_cont" | "percentile_disc" | "mode_agg"
17588 | "array_to_string" | "array_position" | "array_positions"
17589 | "array_remove" | "array_replace"
17590 | "xmlforest" | "xmlagg"
17591
17592 | "zadd" | "zrem" | "zrangebyscore"
17594 | "zrank" | "zrevrank" | "zincrby"
17595 | "zcard" | "zcount" | "zlexcount"
17596 | "lpush" | "rpush" | "lrange" | "lrem"
17597 | "hset" | "hget" | "hgetall" | "hlen"
17598 | "hkeys" | "hvals" | "hmset" | "hincrby"
17599 | "sadd" | "srem" | "smembers"
17600 | "sinter" | "sunion" | "sdiff"
17601 | "scard" | "sismember" | "spop"
17602 | "setex" | "setnx" | "expire"
17603 | "ttl" | "pttl" | "persist"
17604 | "incr" | "decr" | "incrby" | "decrby"
17605 | "getset" | "mset" | "mget" | "renamenx"
17606 | "dbsize" | "type_redis" | "exists_key"
17607 | "strlen" | "getrange" | "setrange" | "append_redis"
17608 | "bitcount" | "bitop" | "bitpos"
17609 | "pfadd" | "pfcount"
17610 | "geoadd" | "geodist" | "geohash"
17611 | "xadd" | "xlen" | "xrange"
17612 | "object_encoding" | "debug_object" | "cluster_slots"
17613
17614 | "argpartition" | "bincount" | "nonzero_count"
17616 | "flatnonzero" | "searchsorted" | "digitize"
17617 | "histogram_bin_edges" | "unique_count"
17618 | "polyfit_rmse"
17619 | "ellipk" | "ellipe"
17620 | "hyp1f1" | "hyp2f1" | "mathieu_b"
17621 | "spherical_jn" | "spherical_yn"
17622 | "jv" | "yn" | "iv" | "kv"
17623 | "airyai" | "airybi"
17624 | "polygamma" | "trigamma" | "loggamma"
17625 | "factorial2" | "factorialk"
17626 | "owens_t" | "marcum_q" | "voigt_profile"
17627 | "chebyt" | "chebyu" | "sph_harm"
17628 | "wofz" | "erfcx" | "erfi" | "dawsn"
17629 | "interp1d"
17630 | "convolve_full" | "convolve_valid" | "correlate_full"
17631 | "kron_product"
17632 | "simpson_rule" | "romberg_quad" | "fixed_quad"
17633 | "ode45_step" | "ode_lsoda" | "solve_ivp_step"
17634 | "root_brentq" | "root_newton" | "root_secant"
17635 | "fmin_powell" | "fmin_cobyla"
17636
17637 | "cobb_douglas" | "ces_production"
17639 | "leontief_input" | "leontief_output"
17640 | "slutsky_decompose"
17641 | "marshallian_demand" | "hicksian_demand"
17642 | "expenditure_function" | "indirect_utility"
17643 | "gale_shapley_step" | "deferred_acceptance"
17644 | "top_trading_cycle" | "vcg_payment" | "myerson_optimal"
17645 | "gini_market" | "hhi_concentration"
17646 | "cournot_eq" | "stackelberg_eq" | "bertrand_eq"
17647 | "monopoly_lerner"
17648 | "consumer_surplus" | "producer_surplus"
17649 | "deadweight_loss" | "tax_incidence"
17650 | "pareto_efficiency" | "edgeworth_box_alloc"
17651 | "social_welfare_utilitarian"
17652 | "social_welfare_rawls" | "social_welfare_nash"
17653 | "arrow_independence"
17654 | "vickrey_auction" | "first_price_seal"
17655 | "english_auction" | "dutch_auction"
17656 | "core_coalition" | "stable_matching_count"
17657 | "gale_optimal" | "pareto_dominance"
17658 | "lerner_index"
17659 | "price_elasticity" | "supply_elasticity"
17660 | "income_elasticity" | "engel_curve" | "cross_elasticity"
17661 | "diff_in_diff" | "did_estimator" | "rdd_estimate"
17662 | "hann_w" | "hamming_w" | "blackman_w" | "barthann_w"
17664 | "nuttall_w" | "flattop_w" | "parzen_window" | "tukey_w"
17665 | "taylor_window" | "dpss_window" | "kaiserord_step"
17666 | "butter_lp_re" | "butter_hp_mag"
17667 | "cheby1_lp" | "cheby2_lp" | "ellip_lp" | "bessel_lp"
17668 | "notch_filter"
17669 | "sosfilt_step" | "lfilter_zi_init" | "filtfilt_pad"
17670 | "freqz_eval" | "freqs_eval" | "group_delay_eval"
17671 | "impulse_response_n"
17672 | "tf2zpk_step" | "zpk2tf_step" | "tf2sos_step"
17673 | "zpk2sos_step" | "sos2tf_step"
17674 | "bilinear_xform" | "bilinear_zpk_xform"
17675 | "firwin_lowpass" | "firwin_highpass"
17676 | "firwin_bandpass" | "firwin_bandstop"
17677 | "firwin2_freq" | "remez_design"
17678 | "stft_step" | "istft_step"
17679 | "cwt_morlet" | "ricker_wavelet" | "mexican_hat_wavelet"
17680 | "coherence_xy" | "csd_xy" | "welch_psd_avg"
17681 | "periodogram_basic" | "lombscargle_freq"
17682 | "hilbert_signal" | "envelope_amplitude"
17683 | "deconvolve_step" | "fftconvolve_step" | "oaconvolve_step"
17684 | "upfirdn_step" | "resample_poly_step" | "decimate_step"
17685 | "savgol_coef" | "detrend_linear"
17686 | "wiener_filter" | "medfilt_1d" | "peak_widths_at"
17687 | "dijkstra_relax" | "bellman_ford_relax"
17689 | "floyd_warshall_step" | "johnson_reweight"
17690 | "astar_search" | "bidirectional_dijkstra"
17691 | "yen_k_shortest" | "ida_star"
17692 | "bfs_count" | "dfs_postorder_done" | "topo_kahn_step"
17693 | "tarjan_scc_step" | "kosaraju_step"
17694 | "kruskal_step" | "prim_step" | "boruvka_step"
17695 | "reverse_delete_step"
17696 | "ford_fulkerson_step" | "edmonds_karp_bfs"
17697 | "dinic_step" | "push_relabel_relabel"
17698 | "stoer_wagner_step" | "karger_step"
17699 | "pagerank_iter" | "hits_authority" | "hits_hub"
17700 | "personalized_pagerank"
17701 | "centrality_degree" | "centrality_closeness"
17702 | "centrality_betweenness" | "centrality_eigenvector"
17703 | "centrality_katz" | "harmonic_centrality" | "load_centrality"
17704 | "clustering_coefficient" | "triangles_count" | "transitivity"
17705 | "modularity_score" | "louvain_gain"
17706 | "label_propagation" | "girvan_newman"
17707 | "articulation_point" | "bridge_edge"
17708 | "edge_connectivity" | "vertex_connectivity"
17709 | "biconnected_components"
17710 | "gx_diameter" | "gx_radius" | "gx_eccentricity"
17711 | "warshall_step"
17712 | "tsp_held_karp" | "tsp_nn_step" | "tsp_christofides"
17713 | "graph_coloring_greedy" | "welsh_powell"
17714 | "vf2_consistent" | "subgraph_isomorphism"
17715 | "hungarian_step" | "hopcroft_karp_step"
17716 | "bron_kerbosch"
17717 | "min_vertex_cover" | "max_independent_set"
17718 | "dominating_set_greedy" | "hamiltonian_path"
17719 | "min_steiner_tree" | "k_shortest_spanning"
17720 | "random_walk_hitting" | "simrank"
17721 | "df_groupby" | "df_aggregate" | "df_apply"
17723 | "df_transform" | "df_pivot" | "df_pivot_table"
17724 | "df_melt" | "df_stack" | "df_unstack"
17725 | "df_explode" | "df_get_dummies" | "df_crosstab"
17726 | "df_merge" | "df_join" | "df_concat"
17727 | "df_resample" | "df_rolling" | "df_expanding"
17728 | "df_ewm" | "df_shift" | "df_diff"
17729 | "df_pct_change" | "df_corr" | "df_cov"
17730 | "df_corrwith" | "df_describe" | "df_kurtosis"
17731 | "df_skew" | "df_sem" | "df_mad"
17732 | "df_dropna" | "df_fillna" | "df_interpolate"
17733 | "df_replace" | "df_isnull" | "df_notnull"
17734 | "df_sort_values" | "df_rank" | "df_quantile"
17735 | "df_value_counts" | "df_sample" | "df_nlargest"
17736 | "df_nsmallest" | "df_idxmax" | "df_idxmin"
17737 | "df_clip" | "df_round" | "df_to_datetime"
17738 | "df_to_timedelta" | "df_to_numeric" | "df_eval"
17739 | "df_query" | "df_filter" | "df_drop_duplicates"
17740 | "df_duplicated" | "df_set_index" | "df_reset_index"
17741 | "image_resize" | "image_grayscale" | "image_threshold"
17743 | "image_blur_gaussian" | "image_blur_box" | "image_sharpen"
17744 | "image_edge_canny" | "image_edge_sobel" | "image_edge_laplacian"
17745 | "image_dilate" | "image_erode" | "image_morphology_open"
17746 | "image_morphology_close" | "image_histogram" | "image_equalize"
17747 | "image_clahe" | "image_contrast" | "image_brightness"
17748 | "image_gamma" | "image_invert" | "image_sepia"
17749 | "image_posterize" | "image_solarize" | "convolve_2d"
17750 | "filter_median" | "filter_bilateral" | "filter_nlmeans"
17751 | "gabor_filter" | "hog_features" | "harris_corners"
17752 | "shi_tomasi_corners" | "sift_keypoints" | "orb_keypoints"
17753 | "surf_keypoints" | "template_match" | "face_detect_haar"
17754 | "watershed_segment" | "slic_superpixels" | "felzenszwalb_segment"
17755 | "graph_cut_segment" | "hough_lines" | "hough_circles"
17756 | "ransac_homography" | "optical_flow_lk" | "optical_flow_farneback"
17757 | "corner_subpix" | "image_rotate" | "image_flip_h"
17758 | "image_flip_v" | "image_emboss" | "image_motion_blur"
17759 | "arima_fit" | "arima_forecast" | "arma_order_select"
17761 | "sarimax_fit" | "garch_fit" | "ewma_smooth"
17762 | "holt_winters_additive" | "holt_winters_multiplicative" | "kalman_filter_step"
17763 | "kalman_smoother_step" | "var_fit" | "vecm_fit"
17764 | "johansen_test" | "phillips_perron" | "adfuller"
17765 | "kpss_test" | "breusch_godfrey" | "ljung_box_q"
17766 | "durbin_watson_d" | "granger_causality" | "cointegration_eg"
17767 | "seasonal_decompose" | "stl_decompose" | "acf_basis"
17768 | "pacf_basis" | "moving_average_filter" | "exp_smooth_simple"
17769 | "exp_smooth_double" | "markov_switching_ar" | "markov_switching_mr"
17770 | "arch_lm" | "state_space_kalman" | "ucm_unobserved_components"
17771 | "spectral_density_estimate" | "bayesian_step" | "pivoted_cholesky_var"
17772 | "sk_logistic_predict" | "sk_logistic_fit" | "sk_random_forest_fit"
17774 | "sk_gbt_fit" | "sk_xgb_fit" | "sk_lightgbm_fit"
17775 | "sk_svm_fit" | "sk_kmeans_fit" | "sk_dbscan_fit"
17776 | "sk_agglomerative_fit" | "sk_pca_fit" | "sk_tsne_fit"
17777 | "sk_umap_fit" | "sk_isolation_forest_fit" | "sk_lof_fit"
17778 | "sk_kfold_split" | "sk_stratified_kfold" | "sk_cross_val_score"
17779 | "sk_grid_search" | "sk_random_search" | "sk_bayes_search"
17780 | "sk_pipeline_fit" | "sk_standard_scaler" | "sk_min_max_scaler"
17781 | "sk_robust_scaler" | "sk_quantile_transform" | "sk_power_transform"
17782 | "sk_one_hot" | "sk_ordinal_encode" | "sk_label_encode"
17783 | "sk_tfidf" | "sk_count_vectorize" | "sk_silhouette"
17784 | "sk_calinski_harabasz" | "sk_davies_bouldin" | "sk_adjusted_rand"
17785 | "sk_mutual_info" | "sk_lda_topic" | "sk_nmf_topic"
17786 | "sk_word2vec_train" | "sk_doc2vec_train" | "sk_naive_bayes_predict"
17787 | "sk_knn_predict" | "sk_decision_tree_split"
17788 | "qubit_x" | "qubit_y" | "qubit_z"
17790 | "qubit_h" | "qubit_s" | "qubit_t"
17791 | "qubit_rx" | "qubit_ry" | "qubit_rz"
17792 | "qubit_u3" | "qubit_u2" | "qubit_u1"
17793 | "qubit_phase" | "qubit_cnot" | "qubit_cz"
17794 | "qubit_swap" | "qubit_ccx" | "qubit_measure"
17795 | "qubit_reset" | "bell_state" | "ghz_state"
17796 | "w_state" | "qft" | "inverse_qft"
17797 | "grover_iter" | "shor_period" | "vqe_step"
17798 | "qaoa_step" | "qpe_iteration" | "pauli_string_expect"
17799 | "circuit_depth" | "circuit_width" | "gate_decompose"
17800 | "ancilla_alloc" | "bloch_sphere_x" | "bloch_sphere_z"
17801 | "density_matrix_purity_q" | "entanglement_entropy" | "quantum_teleportation"
17802 | "superdense_coding" | "noise_model_depolarize"
17803 | "mirr_excel" | "accrint" | "cumipmt"
17805 | "cumprinc" | "dollarde" | "dollarfr"
17806 | "received" | "yieldmat" | "yielddisc"
17807 | "duration_macaulay" | "mduration" | "odddyield"
17808 | "disc_excel" | "effect" | "nominal"
17809 | "intrate" | "price_disc" | "cityhash64"
17810 | "farmhash_64" | "metro_hash_64" | "spookyhash_128"
17811 | "t1ha" | "highway_hash" | "fnv0_32"
17812 | "lose_lose"
17813 | "oat_hash" | "lz4_encode_block" | "snappy_encode"
17814 | "zstd_encode_step" | "brotli_encode_meta" | "lzma_encode_step"
17815 | "bz2_encode_step" | "lzo_encode_step" | "deflate_encode_huffman"
17816 | "lzw_encode" | "gzip_encode_step" | "uri_template_expand"
17817 | "uri_resolve" | "uri_normalize" | "percent_decode_url"
17818 | "url_encode_form" | "url_decode_form" | "punycode_decode_step"
17819 | "idn_normalize" | "url_origin" | "etag_validate"
17820 | "cache_control_parse" | "vary_match" | "content_negotiate"
17821 | "accept_lang_pick" | "range_header_parse" | "if_match_check"
17822 | "if_none_match_check" | "digest_auth_quote" | "www_auth_parse"
17823 | "iso8601_duration_parse" | "iso8601_duration_to_seconds" | "rrule_next_occurrence"
17825 | "cron_next_fire" | "date_round_iso" | "week_number_iso"
17826 | "fiscal_year_us" | "age_at_date" | "easter_western"
17827 | "easter_orthodox_year_2" | "chinese_new_year" | "solstice_winter"
17828 | "equinox_spring" | "rgb_to_oklab" | "oklab_to_rgb"
17829 | "rgb_to_cmyk" | "cmyk_to_rgb" | "rgb_to_xyz"
17830 | "xyz_to_rgb" | "rgb_to_yuv" | "yuv_to_rgb"
17831 | "luminance_relative" | "contrast_ratio" | "wcag_pass"
17832 | "color_temperature_kelvin" | "delta_e76" | "delta_e94"
17833 | "delta_e2000" | "color_blend_alpha" | "isbn10_check"
17834 | "isbn13_check" | "ean13_check" | "upc_check"
17835 | "eth_addr_check" | "btc_addr_check" | "ssn_check"
17836 | "vin_check" | "imei_check" | "iban_check"
17837 | "cusip_check" | "kde_silverman_bw" | "kde_scott_bw"
17838 | "kde_bandwidth_lscv" | "kde_epanechnikov" | "kde_gaussian_2d"
17839 | "kde_uniform" | "kde_triangular" | "kde_biweight"
17840 | "kde_triweight" | "kde_cosine" | "kde_logistic_kernel"
17841 | "mod_exp" | "modexp" | "powmod"
17843 | "mod_inv" | "modinv" | "chinese_remainder" | "crt"
17844 | "miller_rabin" | "millerrabin" | "is_probable_prime"
17845 | "derangements" | "stirling2" | "stirling_second"
17847 | "bernoulli_number" | "bernoulli" | "harmonic_number" | "harmonic"
17848 | "drag_force" | "fdrag" | "ideal_gas" | "pv_nrt"
17850 | "bs_delta" | "bsdelta" | "option_delta"
17852 | "bs_gamma" | "bsgamma" | "option_gamma"
17853 | "bs_vega" | "bsvega" | "option_vega"
17854 | "bs_theta" | "bstheta" | "option_theta"
17855 | "bs_rho" | "bsrho" | "option_rho"
17856 | "bond_duration" | "mac_duration"
17857 | "dct" | "idct" | "goertzel" | "chirp" | "chirp_signal"
17859 | "base85_encode" | "b85e" | "ascii85_encode" | "a85e"
17861 | "base85_decode" | "b85d" | "ascii85_decode" | "a85d"
17862 | "pnorm" | "pbinom" | "dbinom" | "ppois"
17864 | "punif" | "pexp" | "pweibull" | "plnorm" | "pcauchy"
17865 | "rbind" | "cbind"
17867 | "row_sums" | "rowSums" | "col_sums" | "colSums"
17868 | "row_means" | "rowMeans" | "col_means" | "colMeans"
17869 | "outer" | "crossprod" | "tcrossprod"
17870 | "nrow" | "ncol" | "prop_table" | "proptable"
17871 | "cummax" | "cummin" | "scale_vec" | "scale"
17873 | "which_fn" | "tabulate"
17874 | "duplicated" | "duped" | "rev_vec"
17875 | "seq_fn" | "rep_fn" | "rep"
17876 | "cut_bins" | "cut" | "find_interval" | "findInterval"
17877 | "ecdf_fn" | "ecdf" | "density_est" | "density"
17878 | "embed_ts" | "embed"
17879 | "shapiro_test" | "shapiro" | "ks_test" | "ks"
17881 | "wilcox_test" | "wilcox" | "mann_whitney"
17882 | "prop_test" | "proptest" | "binom_test" | "binomtest"
17883 | "sapply" | "tapply" | "do_call" | "docall"
17885 | "kmeans" | "prcomp" | "pca"
17887 | "pgamma" | "pbeta" | "pchisq" | "pt_cdf" | "pt" | "pf_cdf" | "pf"
17895 | "dgeom" | "dunif" | "dnbinom" | "dhyper"
17897 | "lowess" | "loess" | "approx_fn" | "approx"
17899 | "lm_fit" | "lm"
17901 | "qt_fn" | "qf_fn"
17903
17904 | "acf_fn" | "acf" | "pacf_fn" | "pacf"
17906 | "diff_lag" | "diff_ts" | "ts_filter" | "filter_ts"
17907 | "predict_lm" | "predict" | "confint_lm" | "confint"
17909 | "cor_matrix" | "cor_mat" | "cov_matrix" | "cov_mat"
17911 | "mahalanobis" | "mahal" | "dist_matrix" | "dist_mat"
17912 | "hclust" | "cutree" | "weighted_var" | "wvar" | "cov2cor"
17913 | "scatter_svg" | "scatter_plot" | "line_svg" | "line_plot"
17915 | "plot_svg" | "hist_svg" | "histogram_svg"
17916 | "boxplot_svg" | "box_plot" | "bar_svg" | "barchart_svg"
17917 | "pie_svg" | "pie_chart" | "heatmap_svg" | "heatmap"
17918 | "donut_svg" | "donut" | "area_svg" | "area_chart"
17919 | "hbar_svg" | "hbar" | "radar_svg" | "radar" | "spider"
17920 | "candlestick_svg" | "candlestick" | "ohlc"
17921 | "violin_svg" | "violin" | "cor_heatmap" | "cor_matrix_svg"
17922 | "stacked_bar_svg" | "stacked_bar"
17923 | "wordcloud_svg" | "wordcloud" | "wcloud"
17924 | "treemap_svg" | "treemap"
17925 | "pvw"
17926 | "cyber_city" | "cyber_grid" | "cyber_rain" | "matrix_rain"
17928 | "cyber_glitch" | "glitch_text" | "cyber_banner" | "neon_banner"
17929 | "cyber_circuit" | "cyber_skull" | "cyber_eye"
17930 | "ai" | "ai_agent" | "prompt" | "stream_prompt" | "stream_prompt_cb"
17932 | "tokens_of"
17933 | "ai_estimate" | "ai_cost" | "ai_history" | "ai_history_clear"
17934 | "ai_cache_clear" | "ai_cache_size"
17935 | "ai_mock_install" | "ai_mock_clear"
17936 | "ai_config_get" | "ai_config_set" | "ai_routing_get" | "ai_routing_set"
17937 | "ai_register_tool" | "ai_unregister_tool" | "ai_clear_tools" | "ai_tools_list"
17938 | "ai_filter" | "ai_map" | "ai_classify" | "ai_match" | "ai_sort" | "ai_dedupe"
17939 | "ai_extract" | "ai_summarize" | "ai_translate" | "ai_template"
17940 | "ai_session_new" | "ai_session_send" | "ai_session_history"
17941 | "ai_session_close" | "ai_session_reset"
17942 | "ai_session_export" | "ai_session_import"
17943 | "ai_memory_save" | "ai_memory_recall" | "ai_memory_forget"
17944 | "ai_memory_count" | "ai_memory_clear"
17945 | "ai_vision" | "ai_pdf" | "ai_grounded" | "ai_citations"
17946 | "ai_transcribe" | "ai_speak" | "ai_image" | "ai_image_edit" | "ai_image_variation"
17947 | "ai_models" | "ai_describe" | "ai_pricing" | "ai_dashboard"
17948 | "ai_moderate" | "ai_chunk" | "ai_warm" | "ai_compare"
17949 | "ai_last_thinking" | "ai_budget" | "ai_batch" | "ai_pmap"
17950 | "ai_file_upload" | "ai_file_list" | "ai_file_get" | "ai_file_delete"
17951 | "ai_file_anthropic_upload" | "ai_file_anthropic_list" | "ai_file_anthropic_delete"
17952 | "vec_cosine" | "vec_search" | "vec_topk"
17953 | "web_search_tool" | "fetch_url_tool" | "read_file_tool" | "run_code_tool"
17955 | "mcp_connect" | "mcp_close" | "mcp_tools" | "mcp_call"
17957 | "mcp_resource" | "mcp_resources" | "mcp_prompt" | "mcp_prompts"
17958 | "mcp_attach_to_ai" | "mcp_detach_from_ai" | "mcp_attached"
17959 | "mcp_server_start" | "mcp_serve_registered_tools"
17960 | "pty_spawn" | "pty_send" | "pty_read" | "pty_expect" | "pty_expect_table"
17962 | "pty_buffer" | "pty_alive" | "pty_eof" | "pty_close" | "pty_interact"
17963 | "pty_strip_ansi" | "pty_after_eof" | "pty_pending_events"
17964 | "stress_fp" | "stress_int" | "stress_cache" | "stress_branch"
17966 | "stress_sort" | "stress_alloc" | "stress_mmap" | "stress_disk"
17967 | "stress_iops" | "stress_net" | "stress_http" | "stress_dns"
17968 | "stress_fork" | "stress_thread" | "stress_aes" | "stress_compress"
17969 | "stress_regex" | "stress_json" | "stress_burst" | "stress_ramp"
17970 | "stress_oscillate" | "stress_all" | "stress_temp" | "stress_thermal_zones"
17971 | "stress_freq" | "stress_throttled" | "stress_load" | "stress_meminfo"
17972 | "stress_cores" | "stress_arm_kill_switch" | "stress_killed"
17973 | "stress_disarm_kill_switch"
17974 | "stress_metrics_record" | "stress_metrics_clear" | "stress_metrics_count"
17975 | "stress_metrics_export" | "stress_metrics_prometheus"
17976 | "stress_metrics_json" | "stress_metrics_csv" | "stress_metrics_watch"
17977 | "audit_log" | "audit_log_path"
17979 | "secrets_encrypt" | "secrets_decrypt" | "secrets_random_key" | "secrets_kdf"
17980 | "web_route" | "web_resources" | "web_root" | "web_routes_table"
17982 | "web_application_config" | "web_boot_application"
17983 | "web_render" | "web_render_partial" | "web_redirect"
17984 | "web_json" | "web_text" | "web_csv" | "web_markdown"
17985 | "web_params" | "web_request" | "web_set_header" | "web_status"
17986 | "web_before_action" | "web_after_action"
17987 | "web_session" | "web_session_set" | "web_session_get" | "web_session_clear"
17988 | "web_signed" | "web_unsigned"
17989 | "web_cookies" | "web_set_cookie"
17990 | "web_flash" | "web_flash_set" | "web_flash_get"
17991 | "web_validate" | "web_permit"
17992 | "web_password_hash" | "web_password_verify"
17993 | "web_token_for" | "web_token_consume" | "web_csrf_meta_tag"
17994 | "web_security_headers" | "web_can"
17995 | "web_h" | "web_truncate" | "web_pluralize" | "web_time_ago_in_words"
17996 | "web_image_tag" | "web_link_to" | "web_button_to"
17997 | "web_form_with" | "web_form_close"
17998 | "web_text_field" | "web_text_area" | "web_check_box"
17999 | "web_stylesheet_link_tag" | "web_javascript_link_tag"
18000 | "web_yield_content" | "web_content_for"
18001 | "web_etag" | "web_cache_get" | "web_cache_set"
18002 | "web_cache_delete" | "web_cache_clear"
18003 | "web_db_connect" | "web_db_execute" | "web_db_query"
18004 | "web_db_begin" | "web_db_commit" | "web_db_rollback"
18005 | "web_create_table" | "web_drop_table"
18006 | "web_add_column" | "web_remove_column"
18007 | "web_migrate" | "web_rollback"
18008 | "web_model_all" | "web_model_find" | "web_model_first" | "web_model_last"
18009 | "web_model_where" | "web_model_create" | "web_model_update"
18010 | "web_model_destroy" | "web_model_count" | "web_model_increment"
18011 | "web_model_paginate" | "web_model_search" | "web_model_soft_destroy"
18012 | "web_model_with"
18013 | "web_jobs_init" | "web_job_enqueue" | "web_job_dequeue"
18014 | "web_job_complete" | "web_job_fail"
18015 | "web_jobs_list" | "web_jobs_stats" | "web_job_purge"
18016 | "web_jsonapi_resource" | "web_jsonapi_collection" | "web_jsonapi_error"
18017 | "web_bearer_token" | "web_jwt_encode" | "web_jwt_decode"
18018 | "web_otp_secret" | "web_otp_generate" | "web_otp_verify"
18019 | "web_uuid" | "web_now" | "web_log" | "web_rate_limit"
18020 | "web_t" | "web_load_locale" | "web_openapi"
18021 | "web_faker_int" | "web_faker_email" | "web_faker_name"
18022 | "web_faker_sentence" | "web_faker_paragraph"
18023 | "check" | "check_no_interop" | "check_ni"
18030 | "test" | "test_no_interop" | "test_ni"
18031 | "andrew_monotone_hull" | "aperture_stop_to_fnumber" | "arpad_predict" | "bilateral_filter_2d"
18040 | "black_hat_transform" | "canny_edges_full" | "case_alternating" | "case_constant"
18041 | "case_dot" | "case_pascal" | "case_path" | "case_sentence"
18042 | "case_swap" | "case_title_proper" | "case_train" | "closing_2d"
18043 | "cohen_sutherland_clip" | "constants_au_meters" | "constants_avogadro_n" | "constants_bohr_radius"
18044 | "constants_boltzmann_k" | "constants_earth_mass" | "constants_earth_radius" | "constants_electron_charge"
18045 | "constants_electron_mass" | "constants_faraday" | "constants_gas_r" | "constants_gravitational_g"
18046 | "constants_lightyear_meters" | "constants_neutron_mass" | "constants_parsec_meters" | "constants_planck_h"
18047 | "constants_planck_hbar" | "constants_planck" | "constants_proton_mass" | "constants_rydberg"
18048 | "constants_solar_mass" | "constants_solar_radius" | "constants_speed_of_light" | "constants_stefan_boltzmann"
18049 | "contour_area" | "contour_centroid" | "contour_find" | "contour_perimeter"
18050 | "convex_hull_3d" | "convex_hull_3d_simple" | "crop_factor" | "delaunay_triangulate_2d" | "depth_of_field_far"
18051 | "depth_of_field_near" | "dh_compute_shared" | "dilation_2d" | "ec_point_add"
18052 | "ec_point_double" | "ed25519_keypair_simple" | "ed25519_sign_simple" | "ed25519_verify_simple"
18053 | "erosion_2d" | "exposure_value" | "field_of_view" | "focal_length_35mm_equiv"
18054 | "glicko_rd_update" | "glicko_volatility" | "graham_scan_hull" | "hu_moments"
18055 | "hyperfocal_distance" | "liang_barsky_clip" | "minkowski_sum_2d" | "moment_image"
18056 | "morphological_gradient" | "opening_2d" | "pagerank_tournament" | "polygon_inflate"
18057 | "polygon_offset" | "polygon_self_intersects" | "polygon_shrink" | "polygon_simple_check"
18058 | "polygon_winding" | "prewitt_x_kernel" | "prewitt_y_kernel" | "ranking_average"
18059 | "ranking_kendall_tau" | "ranking_spearman_rho" | "roberts_cross_kernel" | "rsa_keypair_simple"
18060 | "rsa_modular_exp" | "scharr_x_kernel" | "scharr_y_kernel" | "schnorr_sign_simple"
18061 | "schnorr_verify_simple" | "shutter_speed_reciprocal" | "sobel_magnitude" | "sunny_16_rule"
18062 | "swiss_pairing" | "top_hat_transform" | "tournament_score" | "trueskill_simple"
18063 | "unit_energy" | "unit_pressure" | "unit_temperature" | "unit_volume_metric_to_us"
18064 | "unit_volume_us_to_metric" | "voronoi_cell_2d" | "weiler_atherton_clip" | "zernike_radial"
18065
18066 | "a_star_grid" | "all_pass_filter" | "am_synth" | "bidirectional_bfs"
18067 | "buoyancy_force" | "center_of_mass_2d" | "center_of_mass_3d" | "centered_polygonal"
18068 | "chorus_simple" | "collision_response_2d" | "comb_filter" | "compositions_count"
18069 | "critical_damping" | "cube_number" | "damping_factor" | "decagonal_number"
18070 | "derangement_count" | "dodecahedral" | "elastic_collision_1d" | "exponential_search"
18071 | "fbm_noise_2d" | "fibonacci_matrix" | "fibonacci_nth_fast" | "fir_filter"
18072 | "flanger_simple" | "floyd_cycle_detect" | "fm_synth_2op" | "freeverb_lite"
18073 | "gnomonic_number" | "greedy_best_first" | "hash_2d_int" | "heptagonal_number"
18074 | "hexagonal_number" | "hyperfactorial" | "icosahedral" | "ida_star_search"
18075 | "inelastic_collision_1d" | "interpolation_search" | "lattice_paths" | "lift_force"
18076 | "lucas_nth" | "moment_of_inertia_cylinder" | "moment_of_inertia_disc" | "moment_of_inertia_rod"
18077 | "moment_of_inertia_sphere" | "mulberry32_next" | "multinomial_coefficient" | "narayana_cow"
18078 | "nonagonal_number" | "octagonal_number" | "partitions_count" | "pcg32_next"
18079 | "pell_nth" | "perlin_2d" | "perlin_3d" | "phaser_simple"
18080 | "plate_reverb_simple" | "poisson_brackets" | "primorial" | "projectile_position"
18081 | "projectile_velocity" | "ridge_noise_2d" | "ring_modulate" | "schroeder_reverb"
18082 | "simplex_2d" | "splitmix64_next" | "spring_oscillator_pos" | "square_pyramidal"
18083 | "super_factorial" | "ternary_search" | "tetrahedral" | "tetranacci"
18084 | "torque_arm" | "turbulence_noise_2d" | "value_noise_2d" | "wavetable_synth"
18085 | "worley_2d" | "xorshift32_next"
18086
18087 | "adaptive_threshold" | "bayes_factor" | "bayesian_beta_update" | "bayesian_normal_update"
18088 | "bm25_score" | "boltzmann_choose" | "braycurtis_dist" | "canberra_dist"
18089 | "canny_edges_simple" | "chebyshev_norm" | "cidr_to_range" | "ciede2000_color_distance"
18090 | "ciede76_color_distance" | "ciede94_color_distance" | "conv1d_apply" | "conv2d_apply"
18091 | "correlate2d" | "cosine_sim_sparse" | "credible_interval_beta" | "credible_interval_normal"
18092 | "dice_coeff" | "earth_mover_1d" | "epsilon_greedy_choose" | "fenwick_new"
18093 | "fenwick_query_prefix" | "fenwick_query_range" | "fenwick_update" | "gaussian_kernel"
18094 | "gradient_magnitude_2d" | "harris_response" | "integral_image" | "ip_subnet_split"
18095 | "ipv6_global_unicast" | "jaccard_sim" | "laplacian_kernel" | "lch_to_rgb"
18096 | "mahalanobis_sq" | "manhattan_norm" | "maximum_a_posteriori" | "minkowski_norm"
18097 | "non_max_suppression" | "oklch_to_rgb" | "otsu_threshold" | "overlap_coeff"
18098 | "posterior_predictive_beta" | "posterior_predictive_normal" | "prior_jeffreys_uniform" | "qlearning_step"
18099 | "range_to_cidr" | "rgb_to_lch" | "rgb_to_oklch" | "rl_discount_returns"
18100 | "rl_n_step_return" | "rl_td_error" | "sarsa_step" | "sliding_dot_product"
18101 | "sobel_x_kernel" | "sobel_y_kernel" | "softmax_choose" | "tanimoto_coeff"
18102 | "tfidf_compute" | "thompson_beta_choose" | "trie_count" | "trie_insert"
18103 | "trie_keys" | "trie_lookup" | "trie_new" | "trie_prefix_search"
18104 | "trie_remove" | "tversky_index" | "ucb1_choose" | "union_find_components"
18105 | "union_find_find" | "union_find_new" | "union_find_union" | "window_bartlett"
18106 | "window_blackman_harris" | "window_flat_top" | "window_gaussian" | "window_welch"
18107
18108 | "alphabeta_value" | "chem_arrhenius_k" | "chem_avogadro" | "chem_balance_check"
18109 | "chem_boiling_point_elevation" | "chem_buffer_capacity" | "chem_celsius_to_fahrenheit" | "chem_celsius_to_kelvin"
18110 | "chem_concentration_to_molarity" | "chem_dilution" | "chem_fahrenheit_to_celsius" | "chem_fahrenheit_to_kelvin"
18111 | "chem_formula_parse" | "chem_freezing_point_depression" | "chem_h_from_ph" | "chem_henderson_hasselbalch"
18112 | "chem_ideal_gas_volume" | "chem_isoelectric_estimate" | "chem_kelvin_to_celsius" | "chem_kelvin_to_fahrenheit"
18113 | "chem_kelvin_to_rankine" | "chem_molality" | "chem_molar_mass" | "chem_molarity_to_normality"
18114 | "chem_partial_pressure" | "chem_ph_from_h" | "chem_pka_lookup" | "chem_rankine_to_kelvin"
18115 | "conditional_entropy" | "edmonds_karp_max_flow" | "expectiminimax_value" | "ford_fulkerson_max_flow"
18116 | "job_schedule_ljf" | "job_schedule_spt" | "joint_entropy" | "js_divergence_distributions"
18117 | "kl_divergence_distributions" | "knapsack_fractional" | "knapsack_unbounded" | "lp_simplex_max"
18118 | "lp_simplex_min" | "matching_bipartite_greedy" | "matching_bipartite_hungarian" | "minimax_value"
18119 | "mixed_strategy_2x2" | "ml_attention_score" | "ml_batch_norm" | "ml_dot_product_attention"
18120 | "ml_dropout_mask" | "ml_elu_layer" | "ml_gelu_layer" | "ml_hinge_loss"
18121 | "ml_huber_loss" | "ml_kl_div_loss" | "ml_label_smooth" | "ml_layer_norm"
18122 | "ml_leaky_relu_layer" | "ml_mae_loss" | "ml_mish_layer" | "ml_mse_loss"
18123 | "ml_one_hot_encode" | "ml_position_encoding" | "ml_relu_layer" | "ml_self_attention"
18124 | "ml_sigmoid_layer" | "ml_softmax_layer" | "ml_softplus_layer" | "ml_swish_layer"
18125 | "ml_tanh_layer" | "ngram_perplexity" | "ngram_prob" | "ngram_top_k_next"
18126 | "ngram_train" | "payoff_matrix" | "relative_entropy" | "tsp_2opt"
18127 | "zero_sum_value"
18128
18129 | "aabb_contains_point" | "aabb_intersects" | "aabb_new" | "aabb_union"
18130 | "aabb_volume" | "backward_algorithm" | "blast_kmer_index" | "bmp_header_read"
18131 | "bootstrap_resample" | "codon_optimize" | "codon_to_amino_acid" | "codon_usage_table"
18132 | "dna_at_content" | "dna_complement" | "dna_gc_content" | "dna_kmer_count"
18133 | "dna_kmer_index" | "dna_melting_temp" | "dna_reverse_complement" | "dna_transcribe"
18134 | "dna_translate" | "elf_header_read" | "forward_algorithm" | "gif_header_read"
18135 | "ico_header_read"
18136 | "jpeg_markers" | "levenshtein_edit_path" | "mach_o_header_read" | "markov_stationary"
18137 | "markov_transition_matrix" | "mat4_determinant" | "mat4_identity" | "mat4_inverse"
18138 | "mat4_look_at" | "mat4_multiply" | "mat4_orthographic" | "mat4_perspective"
18139 | "mat4_rotate_axis" | "mat4_rotate_x" | "mat4_rotate_y" | "mat4_rotate_z"
18140 | "mat4_scale" | "mat4_translate" | "mat4_transpose" | "nw_score"
18141 | "permutation_test" | "plane_distance_to_point" | "plane_normalize" | "png_header_read"
18142 | "profile_hmm_score" | "protein_charge_at_ph" | "protein_hydrophobicity" | "protein_molecular_weight"
18143 | "protein_pI" | "quat_conjugate" | "quat_dot" | "quat_from_euler"
18144 | "quat_identity" | "quat_inverse" | "quat_multiply" | "quat_normalize"
18145 | "quat_to_euler" | "quat_to_mat4" | "ray_aabb_intersect" | "ray_plane_intersect_2"
18146 | "ray_plane_intersect" | "rna_gc_content" | "rna_hamming" | "rna_reverse_complement"
18147 | "rna_to_dna" | "sequence_identity_pct" | "sequence_similarity_pct" | "shuffle_resample"
18148 | "sphere_aabb_intersect" | "sphere_sphere_intersect" | "sw_score" | "tar_header_read"
18149 | "triangle_area_3d" | "triangle_normal" | "vec3_add" | "vec3_cross"
18150 | "vec3_distance" | "vec3_dot" | "vec3_length" | "vec3_lerp"
18151 | "vec3_normalize" | "vec3_project" | "vec3_reflect" | "vec3_refract"
18152 | "vec3_scale" | "vec3_sub" | "vec4_add" | "vec4_dot"
18153 | "vec4_length" | "vec4_scale" | "vec4_sub" | "viterbi_decode"
18154 | "wav_header_read" | "zip_central_directory" | "zip_local_file_header"
18155
18156 | "adler32_combine" | "ase_palette_extract" | "base58check_decode" | "base58check_encode"
18157 | "base91_decode" | "basE91_decode" | "base91_encode" | "basE91_encode"
18158 | "bwt_invert" | "bwt_transform" | "caverphone" | "caverphone2"
18159 | "crc10_atm" | "crc12_dect" | "crc24" | "crc32_bzip2"
18160 | "crc32_jamcrc" | "crc32_mpeg2" | "crc32_xfer" | "crc6_itu"
18161 | "crc64_ecma" | "crc64_xz" | "delta_decode" | "delta_encode"
18162 | "destination_lat_lon" | "double_metaphone_primary" | "double_metaphone_secondary" | "fletcher16"
18163 | "fletcher32" | "fletcher64" | "full_moon_julian" | "fuzzy_substring_match"
18164 | "gamma_correct" | "gamma_uncorrect" | "geomag_declination" | "huffman_decode"
18165 | "huffman_encode" | "julian_to_unix" | "lambert_project" | "lat_lon_to_utm"
18166 | "match_rating_compare" | "mercator_project_x" | "mercator_project_y" | "mercator_unproject_lat"
18167 | "mercator_unproject_lon" | "modified_julian_date" | "moon_age_days" | "moon_distance_km"
18168 | "new_moon_julian" | "nysiis" | "phonex" | "rgb_blend_color_burn"
18169 | "rgb_blend_color_dodge" | "rgb_blend_darken" | "rgb_blend_lighten" | "rgb_blend_multiply"
18170 | "rgb_blend_normal" | "rgb_blend_overlay" | "rgb_blend_screen" | "rle_compress"
18171 | "rle_decompress" | "season_of_year" | "sidereal_time_greenwich" | "sidereal_time_local"
18172 | "solar_noon_unix" | "soundex_v1" | "soundex_v2" | "unix_to_julian"
18173 | "utm_to_lat_lon" | "utm_zone" | "varint_decode" | "varint_encode"
18174 | "vincenty_bearing" | "z85_decode" | "z85_encode" | "zigzag_decode"
18175 | "zigzag_encode"
18176
18177 | "anova_one_way" | "binomial_test" | "bit_clz"
18178 | "bit_count_ones" | "bit_count_zeros" | "bit_ctz" | "bit_extract"
18179 | "bit_first_clear" | "bit_first_set" | "bit_insert" | "bit_last_clear"
18180 | "bit_last_set" | "bit_log2_int" | "bit_parity" | "bit_reverse_u16"
18181 | "bit_reverse_u32" | "bit_reverse_u64" | "bit_reverse_u8" | "bit_rotate_left"
18182 | "bit_rotate_right" | "bit_swap_bytes" | "chi_square_goodness_fit" | "chi_square_independence"
18183 | "chord_augmented" | "chord_diminished" | "chord_diminished7" | "chord_dominant7"
18184 | "chord_major" | "chord_major7" | "chord_minor" | "chord_minor7"
18185 | "crc16_xmodem" | "crc16" | "crc32_zlib" | "crc32c"
18186 | "crc8" | "detab" | "entab" | "fisher_exact_2x2"
18187 | "gray_code_decode" | "gray_code_encode" | "hmac_md5_hex" | "hmac_sha1_hex"
18188 | "hmac_sha256_hex" | "hmac_sha384_hex" | "hmac_sha512_hex" | "indent_block"
18189 | "interval_name" | "jenkins_hash" | "justify_center" | "justify_left"
18190 | "justify_right" | "kruskal_wallis" | "ks_test_one_sample" | "ks_test_two_sample"
18191 | "loose_hash" | "mann_whitney_u" | "midi_to_note_name" | "popcount_u32"
18192 | "popcount_u64" | "proportion_test" | "rank_data" | "scale_blues"
18193 | "scale_chromatic" | "scale_dorian" | "scale_harmonic_minor" | "scale_locrian"
18194 | "scale_lydian" | "scale_major" | "scale_melodic_minor" | "scale_minor"
18195 | "scale_mixolydian" | "scale_pentatonic" | "scale_phrygian" | "seconds_per_beat"
18196 | "strip_indent" | "t_test_paired" | "tempo_to_ms_per_beat" | "truncate_middle"
18197 | "unicode_codepoints" | "wilcoxon_signed_rank" | "word_wrap"
18198
18199 | "beta_function" | "beta_incomplete" | "date_add_days" | "date_add_months"
18200 | "date_add_years" | "date_business_days_between" | "date_day" | "date_dayofweek"
18201 | "date_dayofyear" | "date_days_in_month" | "date_diff_days" | "date_diff_hours"
18202 | "date_diff_minutes" | "date_diff_seconds" | "date_easter" | "date_first_of_month"
18203 | "date_hour" | "date_is_leap" | "date_is_weekend" | "date_iso_format"
18204 | "date_iso_week" | "date_last_of_month" | "date_minute" | "date_month"
18205 | "date_quarter" | "date_second" | "date_str_to_unix" | "date_unix_to_str"
18206 | "date_weekofyear" | "date_year" | "ei" | "expint"
18207 | "gamma_regularized_p" | "gamma_regularized_q" | "graph_articulation_points" | "graph_bellman_ford"
18208 | "graph_betweenness" | "graph_bfs" | "graph_bridges" | "graph_closeness"
18209 | "graph_clustering_coefficient" | "graph_color_greedy" | "graph_connected_components" | "graph_cycle_detect"
18210 | "graph_degree" | "graph_dfs" | "graph_dijkstra" | "graph_eccentricity"
18211 | "graph_eigenvector_centrality" | "graph_floyd_warshall" | "graph_from_edges" | "graph_has_path"
18212 | "graph_in_degree" | "graph_is_bipartite" | "graph_is_connected" | "graph_kosaraju"
18213 | "graph_kruskal_mst" | "graph_out_degree" | "graph_pagerank" | "graph_prim_mst"
18214 | "graph_shortest_path" | "graph_strongly_connected_components" | "graph_tarjan" | "graph_to_adj_list"
18215 | "graph_to_adj_matrix" | "graph_topological_sort" | "hypergeom_1f1" | "hypergeom_2f1"
18216 | "li" | "matrix_adjugate" | "matrix_cholesky_decompose" | "matrix_cofactor"
18217 | "matrix_cols" | "matrix_concat_h" | "matrix_concat_v" | "matrix_determinant"
18218 | "matrix_from_cols" | "matrix_get" | "matrix_kronecker" | "matrix_lu_decompose"
18219 | "matrix_minor" | "matrix_new" | "matrix_norm_frobenius" | "matrix_norm_l1"
18220 | "matrix_norm_linf" | "matrix_outer_product" | "matrix_qr_decompose" | "matrix_reshape"
18221 | "matrix_rows" | "matrix_set" | "matrix_submatrix" | "matrix_swap_cols"
18222 | "matrix_swap_rows" | "matrix_to_string" | "matrix_vec_mul" | "si"
18223 | "sun_rise_unix" | "sun_set_unix" | "zeta_riemann" | "zodiac_sign"
18224 | "add_seasonality" | "trapezoidal_integrate" | "simpson_integrate"
18226 | "ode_euler" | "fit_curve_least_squares"
18227 | "adf_test" | "adx" | "atr" | "bollinger_lower" | "bollinger_middle"
18228 | "bollinger_upper" | "break_even_price" | "break_even_qty" | "candlestick_pattern_doji"
18229 | "candlestick_pattern_engulfing" | "candlestick_pattern_evening_star" | "candlestick_pattern_hammer" | "candlestick_pattern_morning_star"
18230 | "candlestick_pattern_three_black_crows" | "candlestick_pattern_three_white_soldiers" | "cci"
18231 | "dema" | "diff_pct" | "diff_series"
18232 | "discount_pct" | "donchian_lower" | "donchian_upper" | "double_exponential_smoothing"
18233 | "duration_modified" | "ema" | "expanding_mean" | "expanding_sum"
18234 | "fibonacci_extension" | "fibonacci_retracement" | "finite_difference_central"
18235 | "finite_difference_forward" | "hma"
18236 | "hurst_exponent" | "interp_lagrange"
18237 | "interp_linear" | "kama"
18238 | "keltner_lower" | "keltner_upper" | "lag_series"
18239 | "loan_interest_total" | "loan_payment" | "loan_remaining" | "log_returns"
18240 | "macd_histogram" | "macd_signal" | "macd" | "markup_pct" | "net_present_value" | "obv" | "parabolic_sar" | "pivot_points" | "profit_margin_pct" | "remove_seasonality" | "resistance_level"
18241 | "roc" | "rolling_kurtosis" | "rolling_max"
18242 | "rolling_mean" | "rolling_median" | "rolling_min" | "rolling_skew"
18243 | "rolling_std" | "rolling_sum" | "rolling_var"
18244 | "rsi" | "shift_series" | "simple_returns" | "sma" | "stoch_rsi" | "support_level"
18245 | "tema" | "trend_line" | "treynor"
18246 | "trix" | "true_range" | "twap" | "ulcer_index"
18247 | "volatility_annualized" | "volatility_realized" | "vwap" | "williams_r"
18248 | "wma"
18249 => Some(name),
18250 _ => None,
18251 }
18252 }
18253
18254 fn is_reserved_hash_name(name: &str) -> bool {
18257 matches!(
18258 name,
18259 "b" | "pc"
18260 | "e"
18261 | "a"
18262 | "d"
18263 | "c"
18264 | "p"
18265 | "k"
18266 | "all"
18267 | "stryke::builtins"
18268 | "stryke::perl_compats"
18269 | "stryke::extensions"
18270 | "stryke::aliases"
18271 | "stryke::descriptions"
18272 | "stryke::categories"
18273 | "stryke::primaries"
18274 | "stryke::keywords"
18275 | "stryke::all"
18276 )
18277 }
18278
18279 const RESERVED_FUNCTION_NAMES: &'static [&'static str] = &[
18284 "y",
18285 "tr",
18286 "s",
18287 "m",
18288 "q",
18289 "qq",
18290 "qw",
18291 "qx",
18292 "qr",
18293 "if",
18294 "unless",
18295 "while",
18296 "until",
18297 "for",
18298 "foreach",
18299 "given",
18300 "when",
18301 "else",
18302 "elsif",
18303 "do",
18304 "eval",
18305 "return",
18306 "last",
18307 "next",
18308 "redo",
18309 "goto",
18310 "my",
18311 "our",
18312 "local",
18313 "state",
18314 "sub",
18315 "fn",
18316 "class",
18317 "struct",
18318 "enum",
18319 "trait",
18320 "use",
18321 "no",
18322 "require",
18323 "package",
18324 "BEGIN",
18325 "END",
18326 "CHECK",
18327 "INIT",
18328 "UNITCHECK",
18329 "and",
18330 "or",
18331 "not",
18332 "x",
18333 "eq",
18334 "ne",
18335 "lt",
18336 "gt",
18337 "le",
18338 "ge",
18339 "cmp",
18340 ];
18341
18342 fn check_udf_shadows_builtin(&self, name: &str, line: usize) -> StrykeResult<()> {
18343 if name.contains("::") {
18346 return Ok(());
18347 }
18348 if Self::RESERVED_FUNCTION_NAMES.contains(&name) {
18351 return Err(self.syntax_err(
18352 format!("`{name}` is a reserved word and cannot be used as a function name"),
18353 line,
18354 ));
18355 }
18356 if self.current_package != "main" {
18361 return Ok(());
18362 }
18363 if Self::is_known_bareword(name)
18367 || Self::is_try_builtin_name(name)
18368 || crate::list_builtins::is_list_builtin_name(name)
18369 {
18370 return Err(self.syntax_err(
18371 format!(
18372"`{name}` is a stryke builtin and cannot be redefined in `package main` (declare in a named package and call via `Pkg::{name}(...)`, or pass --compat)"
18373 ),
18374 line,
18375 ));
18376 }
18377 Ok(())
18378 }
18379
18380 fn check_hash_shadows_reserved(&self, name: &str, line: usize) -> StrykeResult<()> {
18383 if Self::is_reserved_hash_name(name) {
18384 return Err(self.syntax_err(
18385 format!(
18386"`%{name}` is a stryke reserved hash and cannot be redefined (this is not Perl 5; pass --compat for Perl 5 mode)"
18387 ),
18388 line,
18389 ));
18390 }
18391 Ok(())
18392 }
18393
18394 fn validate_hash_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18397 match &value.kind {
18398 ExprKind::Integer(_) | ExprKind::Float(_) => {
18399 return Err(self.syntax_err(
18400 "cannot assign scalar to hash — use %h = (key => value) or %h = %{$hashref}",
18401 line,
18402 ));
18403 }
18404 ExprKind::String(_) | ExprKind::InterpolatedString(_) | ExprKind::Bareword(_) => {
18405 return Err(self.syntax_err(
18406 "cannot assign string to hash — use %h = (key => value) or %h = %{$hashref}",
18407 line,
18408 ));
18409 }
18410 ExprKind::ArrayRef(_) => {
18411 return Err(self.syntax_err(
18412 "cannot assign arrayref to hash — use %h = @{$arrayref} for even-length list",
18413 line,
18414 ));
18415 }
18416 ExprKind::ScalarRef(inner) => {
18417 if matches!(inner.kind, ExprKind::ArrayVar(_)) {
18418 return Err(self.syntax_err(
18419 "cannot assign \\@array to hash — use %h = @array for even-length list",
18420 line,
18421 ));
18422 }
18423 if matches!(inner.kind, ExprKind::HashVar(_)) {
18424 return Err(self.syntax_err(
18425 "cannot assign \\%hash to hash — use %h = %other directly",
18426 line,
18427 ));
18428 }
18429 }
18430 ExprKind::HashRef(_) => {
18431 return Err(self.syntax_err(
18432 "cannot assign hashref to hash — use %h = %{$hashref} to dereference",
18433 line,
18434 ));
18435 }
18436 ExprKind::CodeRef { .. } => {
18437 return Err(self.syntax_err("cannot assign coderef to hash", line));
18438 }
18439 ExprKind::Undef => {
18440 return Err(
18441 self.syntax_err("cannot assign undef to hash — use %h = () to empty", line)
18442 );
18443 }
18444 ExprKind::List(items)
18445 if items.len() % 2 != 0
18446 && !items.iter().any(|e| {
18447 matches!(
18448 e.kind,
18449 ExprKind::ArrayVar(_)
18450 | ExprKind::HashVar(_)
18451 | ExprKind::FuncCall { .. }
18452 | ExprKind::Deref { .. }
18453 | ExprKind::ScalarVar(_)
18454 )
18455 }) =>
18456 {
18457 return Err(self.syntax_err(
18458 format!(
18459 "odd-length list ({} elements) in hash assignment — missing value for last key",
18460 items.len()
18461 ),
18462 line,
18463 ));
18464 }
18465 _ => {}
18466 }
18467 Ok(())
18468 }
18469
18470 fn validate_array_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18475 if let ExprKind::Undef = &value.kind {
18476 return Err(
18477 self.syntax_err("cannot assign undef to array — use @a = () to empty", line)
18478 );
18479 }
18480 Ok(())
18481 }
18482
18483 fn validate_scalar_assignment(&self, value: &Expr, line: usize) -> StrykeResult<()> {
18486 if let ExprKind::List(items) = &value.kind {
18487 if items.len() > 1 {
18488 return Err(self.syntax_err(
18489 format!(
18490 "cannot assign {}-element list to scalar — Perl 5 silently takes last element; use ($x) = (list) or $x = $list[-1]",
18491 items.len()
18492 ),
18493 line,
18494 ));
18495 }
18496 }
18497 Ok(())
18498 }
18499
18500 fn validate_assignment(&self, target: &Expr, value: &Expr, line: usize) -> StrykeResult<()> {
18502 if crate::compat_mode() {
18503 return Ok(());
18504 }
18505 match &target.kind {
18506 ExprKind::HashVar(_) => self.validate_hash_assignment(value, line),
18507 ExprKind::ArrayVar(_) => self.validate_array_assignment(value, line),
18508 ExprKind::ScalarVar(_) => self.validate_scalar_assignment(value, line),
18509 _ => Ok(()),
18510 }
18511 }
18512
18513 fn parse_block_or_bareword_cmp_block(&mut self) -> StrykeResult<Block> {
18517 if matches!(self.peek(), Token::LBrace) {
18518 return self.parse_block();
18519 }
18520 let line = self.peek_line();
18521 if let Token::Ident(ref name) = self.peek().clone() {
18523 if matches!(
18524 self.peek_at(1),
18525 Token::Comma | Token::Semicolon | Token::RBrace | Token::Eof | Token::PipeForward
18526 ) {
18527 let name = name.clone();
18528 self.advance();
18529 let body = Expr {
18530 kind: ExprKind::FuncCall {
18531 name,
18532 args: vec![
18533 Expr {
18534 kind: ExprKind::ScalarVar("a".to_string()),
18535 line,
18536 },
18537 Expr {
18538 kind: ExprKind::ScalarVar("b".to_string()),
18539 line,
18540 },
18541 ],
18542 },
18543 line,
18544 };
18545 return Ok(vec![Statement::new(StmtKind::Expression(body), line)]);
18546 }
18547 }
18548 let expr = self.parse_assign_expr_stop_at_pipe()?;
18550 Ok(vec![Statement::new(StmtKind::Expression(expr), line)])
18551 }
18552
18553 fn parse_fan_optional_progress(
18555 &mut self,
18556 which: &'static str,
18557 ) -> StrykeResult<Option<Box<Expr>>> {
18558 let line = self.peek_line();
18559 if self.eat(&Token::Comma) {
18560 match self.peek() {
18561 Token::Ident(ref kw)
18562 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) =>
18563 {
18564 self.advance();
18565 self.expect(&Token::FatArrow)?;
18566 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18567 }
18568 _ => {
18569 return Err(self.syntax_err(
18570 format!("{which}: expected `progress => EXPR` after comma"),
18571 line,
18572 ));
18573 }
18574 }
18575 }
18576 if let Token::Ident(ref kw) = self.peek().clone() {
18577 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
18578 self.advance();
18579 self.expect(&Token::FatArrow)?;
18580 return Ok(Some(Box::new(self.parse_assign_expr()?)));
18581 }
18582 }
18583 Ok(None)
18584 }
18585
18586 fn parse_assign_expr_list_optional_progress(&mut self) -> StrykeResult<(Expr, Option<Expr>)> {
18593 if self.in_pipe_rhs()
18599 && matches!(
18600 self.peek(),
18601 Token::Semicolon
18602 | Token::RBrace
18603 | Token::RParen
18604 | Token::Eof
18605 | Token::PipeForward
18606 | Token::Comma
18607 )
18608 {
18609 return Ok((self.pipe_placeholder_list(self.peek_line()), None));
18610 }
18611 let mut parts = vec![self.parse_assign_expr_stop_at_pipe()?];
18612 loop {
18613 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18614 break;
18615 }
18616 if matches!(
18617 self.peek(),
18618 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
18619 ) {
18620 break;
18621 }
18622 if self.peek_is_postfix_stmt_modifier_keyword() {
18623 break;
18624 }
18625 if let Token::Ident(ref kw) = self.peek().clone() {
18626 if kw == "progress" && matches!(self.peek_at(1), Token::FatArrow) {
18627 self.advance();
18628 self.expect(&Token::FatArrow)?;
18629 let prog = self.parse_assign_expr_stop_at_pipe()?;
18630 return Ok((merge_expr_list(parts), Some(prog)));
18631 }
18632 }
18633 parts.push(self.parse_assign_expr_stop_at_pipe()?);
18634 }
18635 Ok((merge_expr_list(parts), None))
18636 }
18637
18638 fn parse_one_arg(&mut self) -> StrykeResult<Expr> {
18639 if matches!(self.peek(), Token::LParen) {
18640 self.advance();
18641 let expr = self.parse_expression()?;
18642 self.expect(&Token::RParen)?;
18643 Ok(expr)
18644 } else {
18645 self.parse_assign_expr_stop_at_pipe()
18646 }
18647 }
18648
18649 fn parse_named_unary_arg(&mut self) -> StrykeResult<Expr> {
18658 if matches!(self.peek(), Token::LParen) {
18659 self.advance();
18660 let expr = self.parse_expression()?;
18661 self.expect(&Token::RParen)?;
18662 Ok(expr)
18663 } else {
18664 self.parse_shift()
18665 }
18666 }
18667
18668 fn parse_one_arg_or_default(&mut self) -> StrykeResult<Expr> {
18669 let prev = self.prev_line();
18680 if self.peek_line() > prev {
18681 return Ok(Expr {
18682 kind: ExprKind::ScalarVar("_".into()),
18683 line: prev,
18684 });
18685 }
18686 if matches!(
18693 self.peek(),
18694 Token::Semicolon
18696 | Token::RBrace
18697 | Token::RParen
18698 | Token::RBracket
18699 | Token::Eof
18700 | Token::Comma
18701 | Token::FatArrow
18702 | Token::PipeForward
18703 | Token::Question
18705 | Token::Colon
18706 | Token::NumEq | Token::NumNe | Token::NumLt | Token::NumGt
18708 | Token::NumLe | Token::NumGe | Token::Spaceship
18709 | Token::StrEq | Token::StrNe | Token::StrLt | Token::StrGt
18710 | Token::StrLe | Token::StrGe | Token::StrCmp
18711 | Token::LogAnd | Token::LogOr | Token::LogNot
18713 | Token::LogAndWord | Token::LogOrWord | Token::LogNotWord
18714 | Token::DefinedOr
18715 | Token::Range | Token::RangeExclusive
18717 | Token::Assign | Token::PlusAssign | Token::MinusAssign
18719 | Token::MulAssign | Token::DivAssign | Token::ModAssign
18720 | Token::PowAssign | Token::DotAssign | Token::AndAssign
18721 | Token::OrAssign | Token::XorAssign | Token::DefinedOrAssign
18722 | Token::ShiftLeftAssign | Token::ShiftRightAssign
18723 | Token::BitAndAssign | Token::BitOrAssign
18724 ) {
18725 return Ok(Expr {
18726 kind: ExprKind::ScalarVar("_".into()),
18727 line: self.peek_line(),
18728 });
18729 }
18730 if matches!(self.peek(), Token::LParen) && matches!(self.peek_at(1), Token::RParen) {
18734 let line = self.peek_line();
18735 self.advance(); self.advance(); return Ok(Expr {
18738 kind: ExprKind::ScalarVar("_".into()),
18739 line,
18740 });
18741 }
18742 self.parse_named_unary_arg()
18747 }
18748
18749 fn parse_one_arg_or_argv(&mut self) -> StrykeResult<Expr> {
18751 let line = self.prev_line(); if matches!(self.peek(), Token::LParen) {
18753 self.advance();
18754 if matches!(self.peek(), Token::RParen) {
18755 self.advance();
18756 return Ok(Expr {
18757 kind: ExprKind::ArrayVar("_".into()),
18758 line: self.peek_line(),
18759 });
18760 }
18761 let expr = self.parse_expression()?;
18762 self.expect(&Token::RParen)?;
18763 return Ok(expr);
18764 }
18765 if matches!(
18767 self.peek(),
18768 Token::Semicolon
18769 | Token::RBrace
18770 | Token::RParen
18771 | Token::Eof
18772 | Token::Comma
18773 | Token::PipeForward
18774 ) || self.peek_line() > line
18775 {
18776 Ok(Expr {
18777 kind: ExprKind::ArrayVar("_".into()),
18778 line,
18779 })
18780 } else {
18781 self.parse_assign_expr()
18782 }
18783 }
18784
18785 fn parse_builtin_args(&mut self) -> StrykeResult<Vec<Expr>> {
18786 if matches!(self.peek(), Token::LParen) {
18787 self.advance();
18788 let args = self.parse_arg_list()?;
18789 self.expect(&Token::RParen)?;
18790 Ok(args)
18791 } else if self.suppress_parenless_call > 0 && matches!(self.peek(), Token::Ident(_)) {
18792 Ok(vec![])
18795 } else {
18796 self.parse_list_until_terminator()
18797 }
18798 }
18799
18800 #[inline]
18804 fn fat_arrow_autoquote(&self, name: &str, line: usize) -> Option<Expr> {
18805 if matches!(self.peek(), Token::FatArrow) {
18806 Some(Expr {
18807 kind: ExprKind::String(name.to_string()),
18808 line,
18809 })
18810 } else {
18811 None
18812 }
18813 }
18814
18815 fn parse_hash_subscript_key(&mut self) -> StrykeResult<Expr> {
18826 let line = self.peek_line();
18827 if let Token::Ident(ref k) = self.peek().clone() {
18828 if matches!(self.peek_at(1), Token::RBrace) && !Self::is_underscore_topic_slot(k) {
18829 let s = k.clone();
18830 self.advance();
18831 return Ok(Expr {
18832 kind: ExprKind::String(s),
18833 line,
18834 });
18835 }
18836 }
18837 if matches!(self.peek_at(1), Token::RBrace) {
18838 if let Some(s) = Self::operator_keyword_to_ident_str(self.peek()) {
18839 self.advance();
18840 return Ok(Expr {
18841 kind: ExprKind::String(s.to_string()),
18842 line,
18843 });
18844 }
18845 }
18846 self.parse_expression()
18847 }
18848
18849 #[inline]
18851 fn peek_is_glob_par_progress_kw(&self) -> bool {
18852 matches!(self.peek(), Token::Ident(ref kw) if kw == "progress")
18853 && matches!(self.peek_at(1), Token::FatArrow)
18854 }
18855
18856 fn parse_pattern_list_until_rparen_or_progress(&mut self) -> StrykeResult<Vec<Expr>> {
18858 let mut args = Vec::new();
18859 loop {
18860 if matches!(self.peek(), Token::RParen | Token::Eof) {
18861 break;
18862 }
18863 if self.peek_is_glob_par_progress_kw() {
18864 break;
18865 }
18866 args.push(self.parse_assign_expr()?);
18867 match self.peek() {
18868 Token::RParen => break,
18869 Token::Comma => {
18870 self.advance();
18871 if matches!(self.peek(), Token::RParen) {
18872 break;
18873 }
18874 if self.peek_is_glob_par_progress_kw() {
18875 break;
18876 }
18877 }
18878 _ => {
18879 return Err(self.syntax_err(
18880 "expected `,`, `)`, or `progress =>` after argument in `glob_par` / `par_sed`",
18881 self.peek_line(),
18882 ));
18883 }
18884 }
18885 }
18886 Ok(args)
18887 }
18888
18889 fn parse_pattern_list_glob_par_bare(&mut self) -> StrykeResult<Vec<Expr>> {
18891 let mut args = Vec::new();
18892 loop {
18893 if matches!(
18894 self.peek(),
18895 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
18896 ) {
18897 break;
18898 }
18899 if self.peek_is_postfix_stmt_modifier_keyword() {
18900 break;
18901 }
18902 if self.peek_is_glob_par_progress_kw() {
18903 break;
18904 }
18905 args.push(self.parse_assign_expr()?);
18906 if !self.eat(&Token::Comma) {
18907 break;
18908 }
18909 if self.peek_is_glob_par_progress_kw() {
18910 break;
18911 }
18912 }
18913 Ok(args)
18914 }
18915
18916 fn parse_glob_par_or_par_sed_args(&mut self) -> StrykeResult<(Vec<Expr>, Option<Box<Expr>>)> {
18918 if matches!(self.peek(), Token::LParen) {
18919 self.advance();
18920 let args = self.parse_pattern_list_until_rparen_or_progress()?;
18921 let progress = if self.peek_is_glob_par_progress_kw() {
18922 self.advance();
18923 self.expect(&Token::FatArrow)?;
18924 Some(Box::new(self.parse_assign_expr()?))
18925 } else {
18926 None
18927 };
18928 self.expect(&Token::RParen)?;
18929 Ok((args, progress))
18930 } else {
18931 let args = self.parse_pattern_list_glob_par_bare()?;
18932 let progress = if self.peek_is_glob_par_progress_kw() {
18934 self.advance();
18935 self.expect(&Token::FatArrow)?;
18936 Some(Box::new(self.parse_assign_expr()?))
18937 } else {
18938 None
18939 };
18940 Ok((args, progress))
18941 }
18942 }
18943
18944 pub(crate) fn parse_arg_list(&mut self) -> StrykeResult<Vec<Expr>> {
18945 let mut args = Vec::new();
18946 let saved_no_pf = self.no_pipe_forward_depth;
18950 self.no_pipe_forward_depth = 0;
18951 while !matches!(
18952 self.peek(),
18953 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
18954 ) {
18955 let arg = match self.parse_assign_expr() {
18956 Ok(e) => e,
18957 Err(err) => {
18958 self.no_pipe_forward_depth = saved_no_pf;
18959 return Err(err);
18960 }
18961 };
18962 args.push(arg);
18963 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18964 break;
18965 }
18966 }
18967 self.no_pipe_forward_depth = saved_no_pf;
18968 Ok(args)
18969 }
18970
18971 pub(crate) fn parse_slice_arg_list(&mut self, is_hash: bool) -> StrykeResult<Vec<Expr>> {
18980 let mut args = Vec::new();
18981 let saved_no_pf = self.no_pipe_forward_depth;
18982 self.no_pipe_forward_depth = 0;
18983 while !matches!(
18984 self.peek(),
18985 Token::RParen | Token::RBracket | Token::RBrace | Token::Eof
18986 ) {
18987 let arg = match self.parse_slice_arg(is_hash) {
18988 Ok(e) => e,
18989 Err(err) => {
18990 self.no_pipe_forward_depth = saved_no_pf;
18991 return Err(err);
18992 }
18993 };
18994 args.push(arg);
18995 if !self.eat(&Token::Comma) && !self.eat(&Token::FatArrow) {
18996 break;
18997 }
18998 }
18999 self.no_pipe_forward_depth = saved_no_pf;
19000 Ok(args)
19001 }
19002
19003 fn parse_slice_arg(&mut self, is_hash: bool) -> StrykeResult<Expr> {
19005 let line = self.peek_line();
19006
19007 if matches!(self.peek(), Token::Colon) {
19009 self.advance();
19010 return self.finish_slice_range(None, false, is_hash, line);
19011 }
19012 if matches!(self.peek(), Token::PackageSep) {
19013 self.advance();
19014 return self.finish_slice_range(None, true, is_hash, line);
19015 }
19016
19017 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
19020 let result = self.parse_slice_endpoint(is_hash);
19021 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
19022 let from_expr = result?;
19023
19024 if matches!(self.peek(), Token::Colon) {
19026 self.advance();
19027 return self.finish_slice_range(Some(Box::new(from_expr)), false, is_hash, line);
19028 }
19029 if matches!(self.peek(), Token::PackageSep) {
19030 self.advance();
19031 return self.finish_slice_range(Some(Box::new(from_expr)), true, is_hash, line);
19032 }
19033
19034 Ok(from_expr)
19035 }
19036
19037 fn finish_slice_range(
19044 &mut self,
19045 from: Option<Box<Expr>>,
19046 double: bool,
19047 is_hash: bool,
19048 line: usize,
19049 ) -> StrykeResult<Expr> {
19050 let (to, step) = if double {
19051 let step_v = self.parse_slice_optional_endpoint(is_hash)?;
19053 (None, step_v)
19054 } else {
19055 let to_v = self.parse_slice_optional_endpoint(is_hash)?;
19057 let step_v = if matches!(self.peek(), Token::Colon) {
19058 self.advance();
19059 self.parse_slice_optional_endpoint(is_hash)?
19060 } else if matches!(self.peek(), Token::PackageSep) {
19061 return Err(
19062 self.syntax_err("Unexpected `::` after slice TO endpoint".to_string(), line)
19063 );
19064 } else {
19065 None
19066 };
19067 (to_v, step_v)
19068 };
19069
19070 if let (Some(f), Some(t)) = (from.as_ref(), to.as_ref()) {
19073 return Ok(Expr {
19074 kind: ExprKind::Range {
19075 from: f.clone(),
19076 to: t.clone(),
19077 exclusive: false,
19078 step,
19079 },
19080 line,
19081 });
19082 }
19083
19084 Ok(Expr {
19085 kind: ExprKind::SliceRange { from, to, step },
19086 line,
19087 })
19088 }
19089
19090 fn parse_slice_optional_endpoint(&mut self, is_hash: bool) -> StrykeResult<Option<Box<Expr>>> {
19093 if matches!(
19094 self.peek(),
19095 Token::Colon
19096 | Token::PackageSep
19097 | Token::Comma
19098 | Token::RBracket
19099 | Token::RBrace
19100 | Token::Eof
19101 ) {
19102 return Ok(None);
19103 }
19104 self.suppress_colon_range = self.suppress_colon_range.saturating_add(1);
19105 let r = self.parse_slice_endpoint(is_hash);
19106 self.suppress_colon_range = self.suppress_colon_range.saturating_sub(1);
19107 Ok(Some(Box::new(r?)))
19108 }
19109
19110 fn parse_slice_endpoint(&mut self, is_hash: bool) -> StrykeResult<Expr> {
19114 if is_hash {
19115 if let Token::Ident(name) = self.peek().clone() {
19116 if matches!(
19117 self.peek_at(1),
19118 Token::Colon
19119 | Token::PackageSep
19120 | Token::Comma
19121 | Token::RBracket
19122 | Token::RBrace
19123 ) {
19124 let line = self.peek_line();
19125 self.advance();
19126 return Ok(Expr {
19127 kind: ExprKind::String(name),
19128 line,
19129 });
19130 }
19131 }
19132 }
19133 self.parse_assign_expr()
19134 }
19135
19136 fn parse_method_arg_list_no_paren(&mut self) -> StrykeResult<Vec<Expr>> {
19140 let mut args = Vec::new();
19141 let call_line = self.prev_line();
19142 loop {
19143 if args.is_empty() && matches!(self.peek(), Token::LBrace) {
19146 break;
19147 }
19148 if matches!(
19149 self.peek(),
19150 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof | Token::PipeForward
19151 ) {
19152 break;
19153 }
19154 if let Token::Ident(ref kw) = self.peek().clone() {
19155 if matches!(
19156 kw.as_str(),
19157 "if" | "unless" | "while" | "until" | "for" | "foreach"
19158 ) {
19159 break;
19160 }
19161 }
19162 if args.is_empty()
19165 && (self.peek_method_arg_infix_terminator() || matches!(self.peek(), Token::Comma))
19166 {
19167 break;
19168 }
19169 if args.is_empty() && self.peek_line() > call_line {
19172 break;
19173 }
19174 args.push(self.parse_assign_expr()?);
19175 if !self.eat(&Token::Comma) {
19176 break;
19177 }
19178 }
19179 Ok(args)
19180 }
19181
19182 fn peek_method_arg_infix_terminator(&self) -> bool {
19185 matches!(
19186 self.peek(),
19187 Token::Plus
19188 | Token::Minus
19189 | Token::Star
19190 | Token::Slash
19191 | Token::Percent
19192 | Token::Power
19193 | Token::Dot
19194 | Token::X
19195 | Token::NumEq
19196 | Token::NumNe
19197 | Token::NumLt
19198 | Token::NumGt
19199 | Token::NumLe
19200 | Token::NumGe
19201 | Token::Spaceship
19202 | Token::StrEq
19203 | Token::StrNe
19204 | Token::StrLt
19205 | Token::StrGt
19206 | Token::StrLe
19207 | Token::StrGe
19208 | Token::StrCmp
19209 | Token::LogAnd
19210 | Token::LogOr
19211 | Token::LogAndWord
19212 | Token::LogOrWord
19213 | Token::DefinedOr
19214 | Token::BitAnd
19215 | Token::BitOr
19216 | Token::BitXor
19217 | Token::ShiftLeft
19218 | Token::ShiftRight
19219 | Token::Range
19220 | Token::RangeExclusive
19221 | Token::BindMatch
19222 | Token::BindNotMatch
19223 | Token::Arrow
19224 | Token::Question
19226 | Token::Colon
19227 | Token::Assign
19229 | Token::PlusAssign
19230 | Token::MinusAssign
19231 | Token::MulAssign
19232 | Token::DivAssign
19233 | Token::ModAssign
19234 | Token::PowAssign
19235 | Token::DotAssign
19236 | Token::AndAssign
19237 | Token::OrAssign
19238 | Token::XorAssign
19239 | Token::DefinedOrAssign
19240 | Token::ShiftLeftAssign
19241 | Token::ShiftRightAssign
19242 | Token::BitAndAssign
19243 | Token::BitOrAssign
19244 )
19245 }
19246
19247 fn parse_list_until_terminator(&mut self) -> StrykeResult<Vec<Expr>> {
19248 self.parse_list_until_terminator_inner(false)
19249 }
19250
19251 fn parse_list_until_terminator_allow_pipe(&mut self) -> StrykeResult<Vec<Expr>> {
19256 self.parse_list_until_terminator_inner(true)
19257 }
19258
19259 fn parse_list_until_terminator_inner(&mut self, allow_pipe: bool) -> StrykeResult<Vec<Expr>> {
19260 let mut args = Vec::new();
19261 let call_line = self.prev_line();
19266 loop {
19267 let is_terminator = if allow_pipe {
19271 matches!(
19272 self.peek(),
19273 Token::Semicolon | Token::RBrace | Token::RParen | Token::Eof
19274 )
19275 } else {
19276 matches!(
19277 self.peek(),
19278 Token::Semicolon
19279 | Token::RBrace
19280 | Token::RParen
19281 | Token::Eof
19282 | Token::PipeForward
19283 )
19284 };
19285 if is_terminator {
19286 break;
19287 }
19288 if let Token::Ident(ref kw) = self.peek().clone() {
19290 if matches!(
19291 kw.as_str(),
19292 "if" | "unless" | "while" | "until" | "for" | "foreach"
19293 ) {
19294 break;
19295 }
19296 }
19297 if args.is_empty() && self.peek_line() > call_line {
19304 break;
19305 }
19306 if allow_pipe {
19310 args.push(self.parse_assign_expr()?);
19311 } else {
19312 args.push(self.parse_assign_expr_stop_at_pipe()?);
19313 }
19314 if !self.eat(&Token::Comma) {
19315 break;
19316 }
19317 }
19318 Ok(args)
19319 }
19320
19321 fn parse_forced_hashref_body(&mut self, line: usize) -> StrykeResult<Expr> {
19328 let saved = self.pos;
19329 if let Ok(pairs) = self.try_parse_hash_ref() {
19330 return Ok(Expr {
19331 kind: ExprKind::HashRef(pairs),
19332 line,
19333 });
19334 }
19335 self.pos = saved;
19337 if matches!(self.peek(), Token::RBrace) {
19338 self.advance();
19339 return Ok(Expr {
19340 kind: ExprKind::HashRef(vec![]),
19341 line,
19342 });
19343 }
19344 let inner = self.parse_expression()?;
19348 self.expect(&Token::RBrace)?;
19349 let sentinel_key = Expr {
19350 kind: ExprKind::String("__HASH_SPREAD__".into()),
19351 line,
19352 };
19353 Ok(Expr {
19354 kind: ExprKind::HashRef(vec![(sentinel_key, inner)]),
19355 line,
19356 })
19357 }
19358
19359 fn try_parse_hash_ref(&mut self) -> StrykeResult<Vec<(Expr, Expr)>> {
19360 let mut pairs = Vec::new();
19361 while !matches!(self.peek(), Token::RBrace | Token::Eof) {
19362 let line = self.peek_line();
19367 let key = if let Token::Ident(ref name) = self.peek().clone() {
19368 if matches!(self.peek_at(1), Token::FatArrow)
19369 && !Self::is_underscore_topic_slot(name)
19370 {
19371 self.advance();
19372 Expr {
19373 kind: ExprKind::String(name.clone()),
19374 line,
19375 }
19376 } else {
19377 self.parse_assign_expr()?
19378 }
19379 } else {
19380 self.parse_assign_expr()?
19381 };
19382 if matches!(self.peek(), Token::RBrace | Token::Comma)
19386 && matches!(
19387 key.kind,
19388 ExprKind::HashVar(_)
19389 | ExprKind::Deref {
19390 kind: Sigil::Hash,
19391 ..
19392 }
19393 )
19394 {
19395 let sentinel_key = Expr {
19399 kind: ExprKind::String("__HASH_SPREAD__".into()),
19400 line,
19401 };
19402 pairs.push((sentinel_key, key));
19403 self.eat(&Token::Comma);
19404 continue;
19405 }
19406 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19408 let val = self.parse_assign_expr()?;
19409 pairs.push((key, val));
19410 self.eat(&Token::Comma);
19411 } else {
19412 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19413 }
19414 }
19415 self.expect(&Token::RBrace)?;
19416 Ok(pairs)
19417 }
19418
19419 fn parse_hashref_pairs_until(&mut self, term: &Token) -> StrykeResult<Vec<(Expr, Expr)>> {
19424 let mut pairs = Vec::new();
19425 while !matches!(&self.peek(), t if std::mem::discriminant(*t) == std::mem::discriminant(term))
19426 && !matches!(self.peek(), Token::Eof)
19427 {
19428 let line = self.peek_line();
19429 let key = if let Token::Ident(ref name) = self.peek().clone() {
19430 if matches!(self.peek_at(1), Token::FatArrow)
19431 && !Self::is_underscore_topic_slot(name)
19432 {
19433 self.advance();
19434 Expr {
19435 kind: ExprKind::String(name.clone()),
19436 line,
19437 }
19438 } else {
19439 self.parse_assign_expr()?
19440 }
19441 } else {
19442 self.parse_assign_expr()?
19443 };
19444 if self.eat(&Token::FatArrow) || self.eat(&Token::Comma) {
19445 let val = self.parse_assign_expr()?;
19446 pairs.push((key, val));
19447 self.eat(&Token::Comma);
19448 } else {
19449 return Err(self.syntax_err("Expected => or , in hash ref", key.line));
19450 }
19451 }
19452 Ok(pairs)
19453 }
19454
19455 fn interp_chain_subscripts(
19461 &self,
19462 chars: &[char],
19463 i: &mut usize,
19464 mut base: Expr,
19465 line: usize,
19466 ) -> Expr {
19467 loop {
19468 let (after, requires_subscript) =
19470 if *i + 1 < chars.len() && chars[*i] == '-' && chars[*i + 1] == '>' {
19471 (*i + 2, true)
19472 } else {
19473 (*i, false)
19474 };
19475 if after >= chars.len() {
19476 break;
19477 }
19478 match chars[after] {
19479 '[' => {
19480 *i = after + 1;
19481 let mut idx_str = String::new();
19482 while *i < chars.len() && chars[*i] != ']' {
19483 idx_str.push(chars[*i]);
19484 *i += 1;
19485 }
19486 if *i < chars.len() {
19487 *i += 1;
19488 }
19489 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19490 Expr {
19491 kind: ExprKind::ScalarVar(rest.to_string()),
19492 line,
19493 }
19494 } else if let Ok(n) = idx_str.parse::<i64>() {
19495 Expr {
19496 kind: ExprKind::Integer(n),
19497 line,
19498 }
19499 } else {
19500 Expr {
19501 kind: ExprKind::String(idx_str),
19502 line,
19503 }
19504 };
19505 base = Expr {
19506 kind: ExprKind::ArrowDeref {
19507 expr: Box::new(base),
19508 index: Box::new(idx_expr),
19509 kind: DerefKind::Array,
19510 },
19511 line,
19512 };
19513 }
19514 '{' => {
19515 *i = after + 1;
19516 let mut key = String::new();
19517 let mut depth = 1usize;
19518 while *i < chars.len() && depth > 0 {
19519 if chars[*i] == '{' {
19520 depth += 1;
19521 } else if chars[*i] == '}' {
19522 depth -= 1;
19523 if depth == 0 {
19524 break;
19525 }
19526 }
19527 key.push(chars[*i]);
19528 *i += 1;
19529 }
19530 if *i < chars.len() {
19531 *i += 1;
19532 }
19533 let key_expr = if let Some(rest) = key.strip_prefix('$') {
19534 Expr {
19535 kind: ExprKind::ScalarVar(rest.to_string()),
19536 line,
19537 }
19538 } else {
19539 Expr {
19540 kind: ExprKind::String(key),
19541 line,
19542 }
19543 };
19544 base = Expr {
19545 kind: ExprKind::ArrowDeref {
19546 expr: Box::new(base),
19547 index: Box::new(key_expr),
19548 kind: DerefKind::Hash,
19549 },
19550 line,
19551 };
19552 }
19553 _ => {
19554 if requires_subscript {
19555 }
19557 break;
19558 }
19559 }
19560 }
19561 base
19562 }
19563
19564 fn no_interop_check_scalar_var_name(&self, name: &str, line: usize) -> StrykeResult<()> {
19568 if crate::no_interop_mode() && (name == "a" || name == "b") {
19569 return Err(self.syntax_err(
19570 format!(
19571 "stryke uses `_` / `_1` (bareword in code) or `$_` / `$_1` \
19572 (sigil inside string interpolation / when whitespace would \
19573 change parsing) instead of `${}` (--no-interop is active)",
19574 name
19575 ),
19576 line,
19577 ));
19578 }
19579 Ok(())
19580 }
19581
19582 fn parse_interpolated_string(&self, s: &str, line: usize) -> StrykeResult<Expr> {
19583 let mut parts = Vec::new();
19585 let mut literal = String::new();
19586 let chars: Vec<char> = s.chars().collect();
19587 let mut i = 0;
19588
19589 'istr: while i < chars.len() {
19590 if chars[i] == LITERAL_DOLLAR_IN_DQUOTE {
19591 literal.push('$');
19592 i += 1;
19593 continue;
19594 }
19595 if chars[i] == LITERAL_AT_IN_DQUOTE {
19596 literal.push('@');
19597 i += 1;
19598 continue;
19599 }
19600 if chars[i] == '\\' && i + 1 < chars.len() && chars[i + 1] == '$' {
19602 literal.push('\\');
19603 i += 1;
19604 }
19606 if chars[i] == '$' && i + 1 < chars.len() {
19607 if !literal.is_empty() {
19608 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
19609 }
19610 i += 1; while i < chars.len() && chars[i].is_whitespace() {
19613 i += 1;
19614 }
19615 if i >= chars.len() {
19616 return Err(self.syntax_err("Final $ should be \\$ or $name", line));
19617 }
19618 if chars[i] == '#' {
19620 i += 1;
19621 let mut sname = String::from("#");
19622 while i < chars.len()
19623 && (chars[i].is_alphanumeric() || chars[i] == '_' || chars[i] == ':')
19624 {
19625 sname.push(chars[i]);
19626 i += 1;
19627 }
19628 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19629 sname.push_str("::");
19630 i += 2;
19631 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19632 sname.push(chars[i]);
19633 i += 1;
19634 }
19635 }
19636 self.no_interop_check_scalar_var_name(&sname, line)?;
19637 parts.push(StringPart::ScalarVar(sname));
19638 continue;
19639 }
19640 if chars[i] == '$' {
19644 let next_c = chars.get(i + 1).copied();
19645 let is_pid = match next_c {
19646 None => true,
19647 Some(c)
19648 if !c.is_ascii_digit() && !matches!(c, 'A'..='Z' | 'a'..='z' | '_') =>
19649 {
19650 true
19651 }
19652 _ => false,
19653 };
19654 if is_pid {
19655 parts.push(StringPart::ScalarVar("$$".to_string()));
19656 i += 1; continue;
19658 }
19659 i += 1; }
19661 if chars[i] == '{' {
19662 i += 1;
19668 let mut inner = String::new();
19669 let mut depth = 1usize;
19670 while i < chars.len() && depth > 0 {
19671 match chars[i] {
19672 '{' => depth += 1,
19673 '}' => {
19674 depth -= 1;
19675 if depth == 0 {
19676 break;
19677 }
19678 }
19679 _ => {}
19680 }
19681 inner.push(chars[i]);
19682 i += 1;
19683 }
19684 if i < chars.len() {
19685 i += 1; }
19687
19688 let trimmed = inner.trim();
19692 let is_expr = trimmed.starts_with('$')
19693 || trimmed.starts_with('\\')
19694 || trimmed.starts_with('@') || trimmed.starts_with('%') || trimmed.contains(['(', '+', '-', '*', '/', '.', '?', '&', '|']);
19697 let mut base: Expr = if is_expr {
19698 match parse_expression_from_str(trimmed, "<interp>") {
19702 Ok(e) => Expr {
19703 kind: ExprKind::Deref {
19704 expr: Box::new(e),
19705 kind: Sigil::Scalar,
19706 },
19707 line,
19708 },
19709 Err(_) => Expr {
19710 kind: ExprKind::ScalarVar(inner.clone()),
19711 line,
19712 },
19713 }
19714 } else {
19715 self.no_interop_check_scalar_var_name(&inner, line)?;
19717 Expr {
19718 kind: ExprKind::ScalarVar(inner),
19719 line,
19720 }
19721 };
19722
19723 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19727 parts.push(StringPart::Expr(base));
19728 } else if chars[i] == '^' {
19729 let mut name = String::from("^");
19731 i += 1;
19732 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19733 name.push(chars[i]);
19734 i += 1;
19735 }
19736 if i < chars.len() && chars[i] == '{' {
19737 i += 1; let mut key = String::new();
19739 let mut depth = 1;
19740 while i < chars.len() && depth > 0 {
19741 if chars[i] == '{' {
19742 depth += 1;
19743 } else if chars[i] == '}' {
19744 depth -= 1;
19745 if depth == 0 {
19746 break;
19747 }
19748 }
19749 key.push(chars[i]);
19750 i += 1;
19751 }
19752 if i < chars.len() {
19753 i += 1;
19754 }
19755 let key_expr = if let Some(rest) = key.strip_prefix('$') {
19756 Expr {
19757 kind: ExprKind::ScalarVar(rest.to_string()),
19758 line,
19759 }
19760 } else {
19761 Expr {
19762 kind: ExprKind::String(key),
19763 line,
19764 }
19765 };
19766 parts.push(StringPart::Expr(Expr {
19767 kind: ExprKind::HashElement {
19768 hash: name,
19769 key: Box::new(key_expr),
19770 },
19771 line,
19772 }));
19773 } else if i < chars.len() && chars[i] == '[' {
19774 i += 1;
19775 let mut idx_str = String::new();
19776 while i < chars.len() && chars[i] != ']' {
19777 idx_str.push(chars[i]);
19778 i += 1;
19779 }
19780 if i < chars.len() {
19781 i += 1;
19782 }
19783 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19784 Expr {
19785 kind: ExprKind::ScalarVar(rest.to_string()),
19786 line,
19787 }
19788 } else if let Ok(n) = idx_str.parse::<i64>() {
19789 Expr {
19790 kind: ExprKind::Integer(n),
19791 line,
19792 }
19793 } else {
19794 Expr {
19795 kind: ExprKind::String(idx_str),
19796 line,
19797 }
19798 };
19799 parts.push(StringPart::Expr(Expr {
19800 kind: ExprKind::ArrayElement {
19801 array: name,
19802 index: Box::new(idx_expr),
19803 },
19804 line,
19805 }));
19806 } else {
19807 self.no_interop_check_scalar_var_name(&name, line)?;
19808 parts.push(StringPart::ScalarVar(name));
19809 }
19810 } else if chars[i].is_alphabetic() || chars[i] == '_' {
19811 let mut name = String::new();
19812 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19813 name.push(chars[i]);
19814 i += 1;
19815 }
19816 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
19821 name.push_str("::");
19822 i += 2;
19823 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
19824 name.push(chars[i]);
19825 i += 1;
19826 }
19827 }
19828 if name.ends_with("::") && i < chars.len() && !crate::compat_mode() {
19842 if chars[i] == '^'
19843 && i + 1 < chars.len()
19844 && chars[i + 1].is_ascii_alphabetic()
19845 {
19846 name.push('^');
19847 name.push(chars[i + 1]);
19848 i += 2;
19849 } else if "!@$&*+;',\"\\|?/<>.0123456789~%-=()[]{}".contains(chars[i]) {
19850 name.push(chars[i]);
19851 i += 1;
19852 }
19853 }
19854 let is_topic_slot = name == "_"
19859 || (name.len() > 1
19860 && name.starts_with('_')
19861 && name[1..].bytes().all(|b| b.is_ascii_digit()));
19862 if is_topic_slot {
19863 let try_indexed = chars.get(i) == Some(&'<')
19865 && chars.get(i + 1).is_some_and(|c| c.is_ascii_digit());
19866 let mut handled_indexed = false;
19867 if try_indexed {
19868 let mut j = i + 1;
19869 while j < chars.len() && chars[j].is_ascii_digit() {
19870 j += 1;
19871 }
19872 let digits: String = chars[i + 1..j].iter().collect();
19873 if let Ok(n) = digits.parse::<usize>() {
19874 if n >= 1 {
19875 for _ in 0..n {
19876 name.push('<');
19877 }
19878 i = j;
19879 handled_indexed = true;
19880 }
19881 }
19882 }
19883 if !handled_indexed {
19884 while i < chars.len() && chars[i] == '<' {
19885 name.push('<');
19886 i += 1;
19887 }
19888 }
19889 }
19890 self.no_interop_check_scalar_var_name(&name, line)?;
19895 let mut base = if i < chars.len() && chars[i] == '{' {
19900 i += 1; let mut key = String::new();
19903 let mut depth = 1;
19904 while i < chars.len() && depth > 0 {
19905 if chars[i] == '{' {
19906 depth += 1;
19907 } else if chars[i] == '}' {
19908 depth -= 1;
19909 if depth == 0 {
19910 break;
19911 }
19912 }
19913 key.push(chars[i]);
19914 i += 1;
19915 }
19916 if i < chars.len() {
19917 i += 1;
19918 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
19920 Expr {
19921 kind: ExprKind::ScalarVar(rest.to_string()),
19922 line,
19923 }
19924 } else {
19925 Expr {
19926 kind: ExprKind::String(key),
19927 line,
19928 }
19929 };
19930 Expr {
19931 kind: ExprKind::HashElement {
19932 hash: name,
19933 key: Box::new(key_expr),
19934 },
19935 line,
19936 }
19937 } else if i < chars.len() && chars[i] == '[' {
19938 i += 1;
19940 let mut idx_str = String::new();
19941 while i < chars.len() && chars[i] != ']' {
19942 idx_str.push(chars[i]);
19943 i += 1;
19944 }
19945 if i < chars.len() {
19946 i += 1;
19947 }
19948 let idx_expr = if let Some(rest) = idx_str.strip_prefix('$') {
19949 Expr {
19950 kind: ExprKind::ScalarVar(rest.to_string()),
19951 line,
19952 }
19953 } else if let Ok(n) = idx_str.parse::<i64>() {
19954 Expr {
19955 kind: ExprKind::Integer(n),
19956 line,
19957 }
19958 } else {
19959 Expr {
19960 kind: ExprKind::String(idx_str),
19961 line,
19962 }
19963 };
19964 Expr {
19965 kind: ExprKind::ArrayElement {
19966 array: name,
19967 index: Box::new(idx_expr),
19968 },
19969 line,
19970 }
19971 } else {
19972 Expr {
19974 kind: ExprKind::ScalarVar(name),
19975 line,
19976 }
19977 };
19978
19979 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
19983 parts.push(StringPart::Expr(base));
19984 } else if chars[i].is_ascii_digit() {
19985 if chars[i] == '0' {
19987 i += 1;
19988 if i < chars.len() && chars[i].is_ascii_digit() {
19989 return Err(self.syntax_err(
19990 "Numeric variables with more than one digit may not start with '0'",
19991 line,
19992 ));
19993 }
19994 parts.push(StringPart::ScalarVar("0".into()));
19995 } else {
19996 let start = i;
19997 while i < chars.len() && chars[i].is_ascii_digit() {
19998 i += 1;
19999 }
20000 parts.push(StringPart::ScalarVar(chars[start..i].iter().collect()));
20001 }
20002 } else {
20003 let c = chars[i];
20004 let probe = c.to_string();
20005 if VMHelper::is_special_scalar_name_for_get(&probe)
20011 || matches!(c, '\'' | '`' | '&')
20012 {
20013 i += 1;
20014 if i < chars.len() && chars[i] == '{' {
20016 i += 1; let mut key = String::new();
20018 let mut depth = 1;
20019 while i < chars.len() && depth > 0 {
20020 if chars[i] == '{' {
20021 depth += 1;
20022 } else if chars[i] == '}' {
20023 depth -= 1;
20024 if depth == 0 {
20025 break;
20026 }
20027 }
20028 key.push(chars[i]);
20029 i += 1;
20030 }
20031 if i < chars.len() {
20032 i += 1;
20033 } let key_expr = if let Some(rest) = key.strip_prefix('$') {
20035 Expr {
20036 kind: ExprKind::ScalarVar(rest.to_string()),
20037 line,
20038 }
20039 } else {
20040 Expr {
20041 kind: ExprKind::String(key),
20042 line,
20043 }
20044 };
20045 let mut base = Expr {
20046 kind: ExprKind::HashElement {
20047 hash: probe,
20048 key: Box::new(key_expr),
20049 },
20050 line,
20051 };
20052 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20053 parts.push(StringPart::Expr(base));
20054 } else {
20055 let mut base = Expr {
20057 kind: ExprKind::ScalarVar(probe),
20058 line,
20059 };
20060 base = self.interp_chain_subscripts(&chars, &mut i, base, line);
20061 if matches!(base.kind, ExprKind::ScalarVar(_)) {
20062 if let ExprKind::ScalarVar(name) = base.kind {
20064 self.no_interop_check_scalar_var_name(&name, line)?;
20065 parts.push(StringPart::ScalarVar(name));
20066 }
20067 } else {
20068 parts.push(StringPart::Expr(base));
20069 }
20070 }
20071 } else {
20072 literal.push('$');
20073 literal.push(c);
20074 i += 1;
20075 }
20076 }
20077 } else if chars[i] == '@' && i + 1 < chars.len() {
20078 let next = chars[i + 1];
20079 if next == '$' {
20081 if !literal.is_empty() {
20082 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20083 }
20084 i += 1; debug_assert_eq!(chars[i], '$');
20086 i += 1; while i < chars.len() && chars[i].is_whitespace() {
20088 i += 1;
20089 }
20090 if i >= chars.len() {
20091 return Err(self.syntax_err(
20092 "Expected variable or block after `@$` in double-quoted string",
20093 line,
20094 ));
20095 }
20096 let inner_expr = if chars[i] == '{' {
20097 i += 1;
20098 let start = i;
20099 let mut depth = 1usize;
20100 while i < chars.len() && depth > 0 {
20101 match chars[i] {
20102 '{' => depth += 1,
20103 '}' => {
20104 depth -= 1;
20105 if depth == 0 {
20106 break;
20107 }
20108 }
20109 _ => {}
20110 }
20111 i += 1;
20112 }
20113 if depth != 0 {
20114 return Err(self.syntax_err(
20115 "Unterminated `${ ... }` after `@` in double-quoted string",
20116 line,
20117 ));
20118 }
20119 let inner: String = chars[start..i].iter().collect();
20120 i += 1; parse_expression_from_str(inner.trim(), "-e")?
20122 } else {
20123 let mut name = String::new();
20124 if chars[i] == '^' {
20125 name.push('^');
20126 i += 1;
20127 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
20128 {
20129 name.push(chars[i]);
20130 i += 1;
20131 }
20132 } else {
20133 while i < chars.len()
20134 && (chars[i].is_alphanumeric()
20135 || chars[i] == '_'
20136 || chars[i] == ':')
20137 {
20138 name.push(chars[i]);
20139 i += 1;
20140 }
20141 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20142 name.push_str("::");
20143 i += 2;
20144 while i < chars.len()
20145 && (chars[i].is_alphanumeric() || chars[i] == '_')
20146 {
20147 name.push(chars[i]);
20148 i += 1;
20149 }
20150 }
20151 }
20152 if name.is_empty() {
20153 return Err(self.syntax_err(
20154 "Expected identifier after `@$` in double-quoted string",
20155 line,
20156 ));
20157 }
20158 Expr {
20159 kind: ExprKind::ScalarVar(name),
20160 line,
20161 }
20162 };
20163 parts.push(StringPart::Expr(Expr {
20164 kind: ExprKind::Deref {
20165 expr: Box::new(inner_expr),
20166 kind: Sigil::Array,
20167 },
20168 line,
20169 }));
20170 continue 'istr;
20171 }
20172 if next == '{' {
20173 if !literal.is_empty() {
20174 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20175 }
20176 i += 2; let start = i;
20178 let mut depth = 1usize;
20179 while i < chars.len() && depth > 0 {
20180 match chars[i] {
20181 '{' => depth += 1,
20182 '}' => {
20183 depth -= 1;
20184 if depth == 0 {
20185 break;
20186 }
20187 }
20188 _ => {}
20189 }
20190 i += 1;
20191 }
20192 if depth != 0 {
20193 return Err(
20194 self.syntax_err("Unterminated @{ ... } in double-quoted string", line)
20195 );
20196 }
20197 let inner: String = chars[start..i].iter().collect();
20198 i += 1; let inner_expr = parse_expression_from_str(inner.trim(), "-e")?;
20200 parts.push(StringPart::Expr(Expr {
20201 kind: ExprKind::Deref {
20202 expr: Box::new(inner_expr),
20203 kind: Sigil::Array,
20204 },
20205 line,
20206 }));
20207 continue 'istr;
20208 }
20209 if !(next.is_alphabetic() || next == '_' || next == '+' || next == '-') {
20210 literal.push(chars[i]);
20211 i += 1;
20212 } else {
20213 if !literal.is_empty() {
20214 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20215 }
20216 i += 1;
20217 let mut name = String::new();
20218 if i < chars.len() && (chars[i] == '+' || chars[i] == '-') {
20219 name.push(chars[i]);
20220 i += 1;
20221 } else {
20222 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_') {
20223 name.push(chars[i]);
20224 i += 1;
20225 }
20226 while i + 1 < chars.len() && chars[i] == ':' && chars[i + 1] == ':' {
20227 name.push_str("::");
20228 i += 2;
20229 while i < chars.len() && (chars[i].is_alphanumeric() || chars[i] == '_')
20230 {
20231 name.push(chars[i]);
20232 i += 1;
20233 }
20234 }
20235 }
20236 if i < chars.len() && chars[i] == '[' {
20237 i += 1;
20238 let start_inner = i;
20239 let mut depth = 1usize;
20240 while i < chars.len() && depth > 0 {
20241 match chars[i] {
20242 '[' => depth += 1,
20243 ']' => depth -= 1,
20244 _ => {}
20245 }
20246 if depth == 0 {
20247 let inner: String = chars[start_inner..i].iter().collect();
20248 i += 1; let indices = parse_slice_indices_from_str(inner.trim(), "-e")?;
20250 parts.push(StringPart::Expr(Expr {
20251 kind: ExprKind::ArraySlice {
20252 array: name.clone(),
20253 indices,
20254 },
20255 line,
20256 }));
20257 continue 'istr;
20258 }
20259 i += 1;
20260 }
20261 return Err(self.syntax_err(
20262 "Unterminated [ in array slice inside quoted string",
20263 line,
20264 ));
20265 }
20266 parts.push(StringPart::ArrayVar(name));
20267 }
20268 } else if chars[i] == '#'
20269 && i + 1 < chars.len()
20270 && chars[i + 1] == '{'
20271 && !crate::compat_mode()
20272 {
20273 if !literal.is_empty() {
20275 parts.push(StringPart::Literal(std::mem::take(&mut literal)));
20276 }
20277 i += 2; let mut inner = String::new();
20279 let mut depth = 1usize;
20280 while i < chars.len() && depth > 0 {
20281 match chars[i] {
20282 '{' => depth += 1,
20283 '}' => {
20284 depth -= 1;
20285 if depth == 0 {
20286 break;
20287 }
20288 }
20289 _ => {}
20290 }
20291 inner.push(chars[i]);
20292 i += 1;
20293 }
20294 if i < chars.len() {
20295 i += 1; }
20297 let expr = parse_block_from_str(inner.trim(), "-e", line)?;
20298 parts.push(StringPart::Expr(expr));
20299 } else {
20300 literal.push(chars[i]);
20301 i += 1;
20302 }
20303 }
20304 if !literal.is_empty() {
20305 parts.push(StringPart::Literal(literal));
20306 }
20307
20308 if parts.len() == 1 {
20309 if let StringPart::Literal(s) = &parts[0] {
20310 return Ok(Expr {
20311 kind: ExprKind::String(s.clone()),
20312 line,
20313 });
20314 }
20315 }
20316 if parts.is_empty() {
20317 return Ok(Expr {
20318 kind: ExprKind::String(String::new()),
20319 line,
20320 });
20321 }
20322
20323 Ok(Expr {
20324 kind: ExprKind::InterpolatedString(parts),
20325 line,
20326 })
20327 }
20328
20329 fn expr_to_overload_key(&self, e: &Expr) -> StrykeResult<String> {
20330 match &e.kind {
20331 ExprKind::String(s) => Ok(s.clone()),
20332 _ => Err(self.syntax_err(
20333 "overload key must be a string literal (e.g. '\"\"' or '+')",
20334 e.line,
20335 )),
20336 }
20337 }
20338
20339 fn expr_to_overload_sub(&mut self, e: &Expr) -> StrykeResult<String> {
20340 match &e.kind {
20341 ExprKind::String(s) => Ok(s.clone()),
20342 ExprKind::Integer(n) => Ok(n.to_string()),
20343 ExprKind::SubroutineRef(s) | ExprKind::SubroutineCodeRef(s) => Ok(s.clone()),
20344 ExprKind::CodeRef { params, body } => {
20348 let id = self.next_overload_anon_id;
20349 self.next_overload_anon_id = self.next_overload_anon_id.saturating_add(1);
20350 let name = format!("__overload_anon_{}", id);
20351 self.pending_synthetic_subs.push(Statement {
20352 label: None,
20353 kind: StmtKind::SubDecl {
20354 name: name.clone(),
20355 params: params.clone(),
20356 body: body.clone(),
20357 prototype: None,
20358 },
20359 line: e.line,
20360 });
20361 Ok(name)
20362 }
20363 _ => Err(self.syntax_err(
20364 "overload handler must be a string literal, number (e.g. fallback => 1), or \\&subname (method in current package)",
20365 e.line,
20366 )),
20367 }
20368 }
20369}
20370
20371fn merge_expr_list(parts: Vec<Expr>) -> Expr {
20372 if parts.len() == 1 {
20373 parts.into_iter().next().unwrap()
20374 } else {
20375 let line = parts.first().map(|e| e.line).unwrap_or(0);
20376 Expr {
20377 kind: ExprKind::List(parts),
20378 line,
20379 }
20380 }
20381}
20382
20383pub fn parse_expression_from_str(s: &str, file: &str) -> StrykeResult<Expr> {
20385 let mut lexer = Lexer::new_with_file(s, file);
20386 let tokens = lexer.tokenize()?;
20387 let mut parser = Parser::new_with_file(tokens, file);
20388 let e = parser.parse_expression()?;
20389 if !parser.at_eof() {
20390 return Err(parser.syntax_err(
20391 "Extra tokens in embedded string expression",
20392 parser.peek_line(),
20393 ));
20394 }
20395 Ok(e)
20396}
20397
20398pub fn parse_block_from_str(s: &str, file: &str, line: usize) -> StrykeResult<Expr> {
20400 let mut lexer = Lexer::new_with_file(s, file);
20401 let tokens = lexer.tokenize()?;
20402 let mut parser = Parser::new_with_file(tokens, file);
20403 let stmts = parser.parse_statements()?;
20404 let inner_line = stmts.first().map(|st| st.line).unwrap_or(line);
20405 let inner = Expr {
20406 kind: ExprKind::CodeRef {
20407 params: vec![],
20408 body: stmts,
20409 },
20410 line: inner_line,
20411 };
20412 Ok(Expr {
20413 kind: ExprKind::Do(Box::new(inner)),
20414 line,
20415 })
20416}
20417
20418pub fn parse_slice_indices_from_str(s: &str, file: &str) -> StrykeResult<Vec<Expr>> {
20421 let mut lexer = Lexer::new_with_file(s, file);
20422 let tokens = lexer.tokenize()?;
20423 let mut parser = Parser::new_with_file(tokens, file);
20424 parser.parse_arg_list()
20425}
20426pub fn parse_format_value_line(line: &str) -> StrykeResult<Vec<Expr>> {
20428 let trimmed = line.trim();
20429 if trimmed.is_empty() {
20430 return Ok(vec![]);
20431 }
20432 let mut lexer = Lexer::new(trimmed);
20433 let tokens = lexer.tokenize()?;
20434 let mut parser = Parser::new(tokens);
20435 let mut exprs = Vec::new();
20436 loop {
20437 if parser.at_eof() {
20438 break;
20439 }
20440 exprs.push(parser.parse_assign_expr()?);
20442 if parser.eat(&Token::Comma) {
20443 continue;
20444 }
20445 if !parser.at_eof() {
20446 return Err(parser.syntax_err("Extra tokens in format value line", parser.peek_line()));
20447 }
20448 break;
20449 }
20450 Ok(exprs)
20451}
20452
20453#[cfg(test)]
20454mod tests {
20455 use super::*;
20456
20457 fn parse_ok(code: &str) -> Program {
20458 let mut lexer = Lexer::new(code);
20459 let tokens = lexer.tokenize().expect("tokenize");
20460 let mut parser = Parser::new(tokens);
20461 parser.parse_program().expect("parse")
20462 }
20463
20464 fn parse_err(code: &str) -> String {
20465 let mut lexer = Lexer::new(code);
20466 let tokens = match lexer.tokenize() {
20467 Ok(t) => t,
20468 Err(e) => return e.message,
20469 };
20470 let mut parser = Parser::new(tokens);
20471 parser.parse_program().unwrap_err().message
20472 }
20473
20474 #[test]
20475 fn parse_empty_program() {
20476 let p = parse_ok("");
20477 assert!(p.statements.is_empty());
20478 }
20479
20480 #[test]
20481 fn parse_semicolons_only() {
20482 let p = parse_ok(";;");
20483 assert!(p.statements.len() <= 3);
20484 }
20485
20486 #[test]
20487 fn parse_simple_scalar_assignment() {
20488 let p = parse_ok("$x = 1");
20489 assert_eq!(p.statements.len(), 1);
20490 }
20491
20492 #[test]
20493 fn parse_simple_array_assignment() {
20494 let p = parse_ok("@arr = (1, 2, 3)");
20495 assert_eq!(p.statements.len(), 1);
20496 }
20497
20498 #[test]
20499 fn parse_simple_hash_assignment() {
20500 let p = parse_ok("%h = (a => 1, b => 2)");
20501 assert_eq!(p.statements.len(), 1);
20502 }
20503
20504 #[test]
20505 fn parse_subroutine_decl() {
20506 let p = parse_ok("fn foo { 1 }");
20507 assert_eq!(p.statements.len(), 1);
20508 match &p.statements[0].kind {
20509 StmtKind::SubDecl { name, .. } => assert_eq!(name, "foo"),
20510 _ => panic!("expected SubDecl"),
20511 }
20512 }
20513
20514 #[test]
20515 fn parse_class_method_expr_body_shorthand() {
20516 let p = parse_ok("class X { fn adg = \"\" }");
20517 match &p.statements[0].kind {
20518 StmtKind::ClassDecl { def } => {
20519 let m = def.method("adg").expect("adg method");
20520 let body = m.body.as_ref().expect("body");
20521 assert_eq!(body.len(), 1);
20522 match &body[0].kind {
20523 StmtKind::Expression(e) => match &e.kind {
20524 ExprKind::String(s) => assert!(s.is_empty()),
20525 _ => panic!("expected string expr, got {:?}", e.kind),
20526 },
20527 _ => panic!("expected expression stmt"),
20528 }
20529 }
20530 _ => panic!("expected ClassDecl"),
20531 }
20532 }
20533
20534 #[test]
20535 fn parse_named_fn_eq_shorthand_with_sig() {
20536 let p = parse_ok("fn add_one($x) = $x + 1");
20537 match &p.statements[0].kind {
20538 StmtKind::SubDecl {
20539 name, params, body, ..
20540 } => {
20541 assert_eq!(name, "add_one");
20542 assert_eq!(params.len(), 1);
20543 assert_eq!(body.len(), 1);
20544 }
20545 _ => panic!("expected SubDecl"),
20546 }
20547 }
20548
20549 #[test]
20550 fn parse_anon_fn_eq_shorthand_with_sig() {
20551 let p = parse_ok("my $f = fn($x) = 23");
20552 match &p.statements[0].kind {
20553 StmtKind::My(decls) => {
20554 let init = decls[0].initializer.as_ref().expect("initializer");
20555 match &init.kind {
20556 ExprKind::CodeRef { params, body } => {
20557 assert_eq!(params.len(), 1);
20558 assert_eq!(body.len(), 1);
20559 }
20560 _ => panic!("expected CodeRef"),
20561 }
20562 }
20563 _ => panic!("expected My"),
20564 }
20565 }
20566
20567 #[test]
20568 fn parse_struct_method_eq_shorthand() {
20569 let p = parse_ok("struct S { fn double($a) = $a * 2 }");
20570 match &p.statements[0].kind {
20571 StmtKind::StructDecl { def } => {
20572 assert_eq!(def.methods.len(), 1);
20573 assert_eq!(def.methods[0].name, "double");
20574 assert_eq!(def.methods[0].body.len(), 1);
20575 }
20576 _ => panic!("expected StructDecl"),
20577 }
20578 }
20579
20580 #[test]
20581 fn parse_trait_method_eq_shorthand() {
20582 let p = parse_ok("trait T { fn k = 0 }");
20583 match &p.statements[0].kind {
20584 StmtKind::TraitDecl { def } => {
20585 let m = def.method("k").expect("k");
20586 let body = m.body.as_ref().expect("default body");
20587 assert_eq!(body.len(), 1);
20588 }
20589 _ => panic!("expected TraitDecl"),
20590 }
20591 }
20592
20593 #[test]
20594 fn parse_fn_eq_shorthand_rejects_top_level_comma() {
20595 let msg = parse_err("fn z = 1, 2");
20596 assert!(
20597 msg.contains("single expression") || msg.contains("comma"),
20598 "{}",
20599 msg
20600 );
20601 }
20602
20603 #[test]
20604 fn parse_subroutine_with_prototype() {
20605 let p = parse_ok("fn foo ($$) { 1 }");
20606 assert_eq!(p.statements.len(), 1);
20607 match &p.statements[0].kind {
20608 StmtKind::SubDecl { prototype, .. } => {
20609 assert!(prototype.is_some());
20610 }
20611 _ => panic!("expected SubDecl"),
20612 }
20613 }
20614
20615 #[test]
20616 fn parse_anonymous_fn() {
20617 let p = parse_ok("my $f = fn { 1 }");
20618 assert_eq!(p.statements.len(), 1);
20619 }
20620
20621 #[test]
20622 fn parse_if_statement() {
20623 let p = parse_ok("if (1) { 2 }");
20624 assert_eq!(p.statements.len(), 1);
20625 matches!(&p.statements[0].kind, StmtKind::If { .. });
20626 }
20627
20628 #[test]
20629 fn parse_if_elsif_else() {
20630 let p = parse_ok("if (0) { 1 } elsif (1) { 2 } else { 3 }");
20631 assert_eq!(p.statements.len(), 1);
20632 }
20633
20634 #[test]
20635 fn parse_unless_statement() {
20636 let p = parse_ok("unless (0) { 1 }");
20637 assert_eq!(p.statements.len(), 1);
20638 }
20639
20640 #[test]
20641 fn parse_while_loop() {
20642 let p = parse_ok("while ($x) { $x-- }");
20643 assert_eq!(p.statements.len(), 1);
20644 }
20645
20646 #[test]
20647 fn parse_until_loop() {
20648 let p = parse_ok("until ($x) { $x++ }");
20649 assert_eq!(p.statements.len(), 1);
20650 }
20651
20652 #[test]
20653 fn parse_for_c_style() {
20654 let p = parse_ok("for (my $i=0; $i<10; $i++) { 1 }");
20655 assert_eq!(p.statements.len(), 1);
20656 }
20657
20658 #[test]
20659 fn parse_foreach_loop() {
20660 let p = parse_ok("foreach my $x (@arr) { 1 }");
20661 assert_eq!(p.statements.len(), 1);
20662 }
20663
20664 #[test]
20665 fn parse_loop_with_label() {
20666 let p = parse_ok("OUTER: for my $i (1..10) { last OUTER }");
20667 assert_eq!(p.statements.len(), 1);
20668 assert_eq!(p.statements[0].label.as_deref(), Some("OUTER"));
20669 }
20670
20671 #[test]
20672 fn parse_begin_block() {
20673 let p = parse_ok("BEGIN { 1 }");
20674 assert_eq!(p.statements.len(), 1);
20675 matches!(&p.statements[0].kind, StmtKind::Begin(_));
20676 }
20677
20678 #[test]
20679 fn parse_end_block() {
20680 let p = parse_ok("END { 1 }");
20681 assert_eq!(p.statements.len(), 1);
20682 matches!(&p.statements[0].kind, StmtKind::End(_));
20683 }
20684
20685 #[test]
20686 fn parse_package_statement() {
20687 let p = parse_ok("package Foo::Bar");
20688 assert_eq!(p.statements.len(), 1);
20689 match &p.statements[0].kind {
20690 StmtKind::Package { name } => assert_eq!(name, "Foo::Bar"),
20691 _ => panic!("expected Package"),
20692 }
20693 }
20694
20695 #[test]
20696 fn parse_use_statement() {
20697 let p = parse_ok("use strict");
20698 assert_eq!(p.statements.len(), 1);
20699 }
20700
20701 #[test]
20702 fn parse_no_statement() {
20703 let p = parse_ok("no warnings");
20704 assert_eq!(p.statements.len(), 1);
20705 }
20706
20707 #[test]
20708 fn parse_require_bareword() {
20709 let p = parse_ok("require Foo::Bar");
20710 assert_eq!(p.statements.len(), 1);
20711 }
20712
20713 #[test]
20714 fn parse_require_string() {
20715 let p = parse_ok(r#"require "foo.pl""#);
20716 assert_eq!(p.statements.len(), 1);
20717 }
20718
20719 #[test]
20720 fn parse_eval_block() {
20721 let p = parse_ok("eval { 1 }");
20722 assert_eq!(p.statements.len(), 1);
20723 }
20724
20725 #[test]
20726 fn parse_eval_string() {
20727 let p = parse_ok(r#"eval "1 + 2""#);
20728 assert_eq!(p.statements.len(), 1);
20729 }
20730
20731 #[test]
20732 fn parse_qw_word_list() {
20733 let p = parse_ok("my @a = qw(foo bar baz)");
20734 assert_eq!(p.statements.len(), 1);
20735 }
20736
20737 #[test]
20738 fn parse_q_string() {
20739 let p = parse_ok("my $s = q{hello}");
20740 assert_eq!(p.statements.len(), 1);
20741 }
20742
20743 #[test]
20744 fn parse_qq_string() {
20745 let p = parse_ok(r#"my $s = qq(hello $x)"#);
20746 assert_eq!(p.statements.len(), 1);
20747 }
20748
20749 #[test]
20750 fn parse_regex_match() {
20751 let p = parse_ok(r#"$x =~ /foo/"#);
20752 assert_eq!(p.statements.len(), 1);
20753 }
20754
20755 #[test]
20756 fn parse_regex_substitution() {
20757 let p = parse_ok(r#"$x =~ s/foo/bar/g"#);
20758 assert_eq!(p.statements.len(), 1);
20759 }
20760
20761 #[test]
20762 fn parse_transliterate() {
20763 let p = parse_ok(r#"$x =~ tr/a-z/A-Z/"#);
20764 assert_eq!(p.statements.len(), 1);
20765 }
20766
20767 #[test]
20768 fn parse_ternary_operator() {
20769 let p = parse_ok("my $x = $a ? 1 : 2");
20770 assert_eq!(p.statements.len(), 1);
20771 }
20772
20773 #[test]
20774 fn parse_arrow_method_call() {
20775 let p = parse_ok("$obj->method()");
20776 assert_eq!(p.statements.len(), 1);
20777 }
20778
20779 #[test]
20780 fn parse_arrow_deref_hash() {
20781 let p = parse_ok("$r->{key}");
20782 assert_eq!(p.statements.len(), 1);
20783 }
20784
20785 #[test]
20786 fn parse_arrow_deref_array() {
20787 let p = parse_ok("$r->[0]");
20788 assert_eq!(p.statements.len(), 1);
20789 }
20790
20791 #[test]
20792 fn parse_chained_arrow_deref() {
20793 let p = parse_ok("$r->{a}[0]{b}");
20794 assert_eq!(p.statements.len(), 1);
20795 }
20796
20797 #[test]
20798 fn parse_my_multiple_vars() {
20799 let p = parse_ok("my ($a, $b, $c) = (1, 2, 3)");
20800 assert_eq!(p.statements.len(), 1);
20801 }
20802
20803 #[test]
20804 fn parse_our_scalar() {
20805 let p = parse_ok("our $VERSION = '1.0'");
20806 assert_eq!(p.statements.len(), 1);
20807 }
20808
20809 #[test]
20810 fn parse_local_scalar() {
20811 let p = parse_ok("local $/ = undef");
20812 assert_eq!(p.statements.len(), 1);
20813 }
20814
20815 #[test]
20816 fn parse_state_variable() {
20817 let p = parse_ok("fn Test::counter { state $n = 0; $n++ }");
20818 assert_eq!(p.statements.len(), 1);
20819 }
20820
20821 #[test]
20822 fn parse_postfix_if() {
20823 let p = parse_ok("print 1 if $x");
20824 assert_eq!(p.statements.len(), 1);
20825 }
20826
20827 #[test]
20828 fn parse_postfix_unless() {
20829 let p = parse_ok("die 'error' unless $ok");
20830 assert_eq!(p.statements.len(), 1);
20831 }
20832
20833 #[test]
20834 fn parse_postfix_while() {
20835 let p = parse_ok("$x++ while $x < 10");
20836 assert_eq!(p.statements.len(), 1);
20837 }
20838
20839 #[test]
20840 fn parse_postfix_for() {
20841 let p = parse_ok("print for @arr");
20842 assert_eq!(p.statements.len(), 1);
20843 }
20844
20845 #[test]
20846 fn parse_last_next_redo() {
20847 let p = parse_ok("for (@a) { next if $_ < 0; last if $_ > 10 }");
20848 assert_eq!(p.statements.len(), 1);
20849 }
20850
20851 #[test]
20852 fn parse_return_statement() {
20853 let p = parse_ok("fn foo { return 42 }");
20854 assert_eq!(p.statements.len(), 1);
20855 }
20856
20857 #[test]
20858 fn parse_wantarray() {
20859 let p = parse_ok("fn foo { wantarray ? @a : $a }");
20860 assert_eq!(p.statements.len(), 1);
20861 }
20862
20863 #[test]
20864 fn parse_caller_builtin() {
20865 let p = parse_ok("my @c = caller");
20866 assert_eq!(p.statements.len(), 1);
20867 }
20868
20869 #[test]
20870 fn parse_ref_to_array() {
20871 let p = parse_ok("my $r = \\@arr");
20872 assert_eq!(p.statements.len(), 1);
20873 }
20874
20875 #[test]
20876 fn parse_ref_to_hash() {
20877 let p = parse_ok("my $r = \\%hash");
20878 assert_eq!(p.statements.len(), 1);
20879 }
20880
20881 #[test]
20882 fn parse_ref_to_scalar() {
20883 let p = parse_ok("my $r = \\$x");
20884 assert_eq!(p.statements.len(), 1);
20885 }
20886
20887 #[test]
20888 fn parse_deref_scalar() {
20889 let p = parse_ok("my $v = $$r");
20890 assert_eq!(p.statements.len(), 1);
20891 }
20892
20893 #[test]
20894 fn parse_deref_array() {
20895 let p = parse_ok("my @a = @$r");
20896 assert_eq!(p.statements.len(), 1);
20897 }
20898
20899 #[test]
20900 fn parse_deref_hash() {
20901 let p = parse_ok("my %h = %$r");
20902 assert_eq!(p.statements.len(), 1);
20903 }
20904
20905 #[test]
20906 fn parse_blessed_ref() {
20907 let p = parse_ok("bless $r, 'Foo'");
20908 assert_eq!(p.statements.len(), 1);
20909 }
20910
20911 #[test]
20912 fn parse_heredoc_basic() {
20913 let p = parse_ok("my $s = <<END;\nfoo\nEND");
20914 assert_eq!(p.statements.len(), 1);
20915 }
20916
20917 #[test]
20918 fn parse_heredoc_quoted() {
20919 let p = parse_ok("my $s = <<'END';\nfoo\nEND");
20920 assert_eq!(p.statements.len(), 1);
20921 }
20922
20923 #[test]
20924 fn parse_do_block() {
20925 let p = parse_ok("my $x = do { 1 + 2 }");
20926 assert_eq!(p.statements.len(), 1);
20927 }
20928
20929 #[test]
20930 fn parse_do_file() {
20931 let p = parse_ok(r#"do "foo.pl""#);
20932 assert_eq!(p.statements.len(), 1);
20933 }
20934
20935 #[test]
20936 fn parse_map_expression() {
20937 let p = parse_ok("my @b = map { $_ * 2 } @a");
20938 assert_eq!(p.statements.len(), 1);
20939 }
20940
20941 #[test]
20944 fn parse_dist_thread_on_scalar_empty_list_source() {
20945 let p = parse_ok("~d> on $c () map { _ * 2 }");
20946 assert_eq!(p.statements.len(), 1);
20947 let StmtKind::Expression(root) = &p.statements[0].kind else {
20948 panic!("expected Expression statement");
20949 };
20950 let ExprKind::DistReduceExpr { cluster, list, .. } = &root.kind else {
20951 panic!("expected DistReduceExpr, got {:?}", root.kind);
20952 };
20953 assert!(
20954 matches!(cluster.kind, ExprKind::ScalarVar(ref s) if s == "c"),
20955 "expected cluster $c, got {:?}",
20956 cluster.kind
20957 );
20958 assert!(
20959 matches!(list.kind, ExprKind::List(ref v) if v.is_empty()),
20960 "expected empty list source, got {:?}",
20961 list.kind
20962 );
20963 }
20964
20965 #[test]
20966 fn parse_grep_expression() {
20967 let p = parse_ok("my @b = grep { $_ > 0 } @a");
20968 assert_eq!(p.statements.len(), 1);
20969 }
20970
20971 #[test]
20972 fn parse_sort_expression() {
20973 let p = parse_ok("my @b = sort { $a <=> $b } @a");
20974 assert_eq!(p.statements.len(), 1);
20975 }
20976
20977 #[test]
20978 fn pipe_sort_does_not_swallow_next_my_decl() {
20979 let p = parse_ok("my @s = @data |> sort\nmy $j = join(\",\", @s)");
20983 assert_eq!(
20984 p.statements.len(),
20985 2,
20986 "expected 2 stmts (sort + join decl), got {}: {:?}",
20987 p.statements.len(),
20988 p.statements
20989 .iter()
20990 .map(|s| format!("{:?}", s.kind).chars().take(60).collect::<String>())
20991 .collect::<Vec<_>>(),
20992 );
20993 }
20994
20995 #[test]
20996 fn pipe_sort_multiline_pipeline_preserves_next_decl() {
20997 let p = parse_ok(
21001 "my @bk = @{$inv->by_cat(\"bakery\")} |> maps { _->label() } |> sort\nmy $j = join(\"|\", @bk)",
21002 );
21003 assert_eq!(p.statements.len(), 2);
21004 }
21005
21006 #[test]
21007 fn parse_pipe_forward() {
21008 let p = parse_ok("@a |> map { $_ * 2 }");
21009 assert_eq!(p.statements.len(), 1);
21010 }
21011
21012 #[test]
21013 fn parse_expression_from_str_simple() {
21014 let e = parse_expression_from_str("$x + 1", "-e").unwrap();
21015 assert!(matches!(e.kind, ExprKind::BinOp { .. }));
21016 }
21017
21018 #[test]
21019 fn parse_expression_from_str_extra_tokens_error() {
21020 let err = parse_expression_from_str("$x; $y", "-e").unwrap_err();
21021 assert!(err.message.contains("Extra tokens"));
21022 }
21023
21024 #[test]
21025 fn parse_slice_indices_from_str_basic() {
21026 let indices = parse_slice_indices_from_str("0, 1, 2", "-e").unwrap();
21027 assert_eq!(indices.len(), 3);
21028 }
21029
21030 #[test]
21031 fn parse_format_value_line_empty() {
21032 let exprs = parse_format_value_line("").unwrap();
21033 assert!(exprs.is_empty());
21034 }
21035
21036 #[test]
21037 fn parse_format_value_line_single() {
21038 let exprs = parse_format_value_line("$x").unwrap();
21039 assert_eq!(exprs.len(), 1);
21040 }
21041
21042 #[test]
21043 fn parse_format_value_line_multiple() {
21044 let exprs = parse_format_value_line("$a, $b, $c").unwrap();
21045 assert_eq!(exprs.len(), 3);
21046 }
21047
21048 #[test]
21049 fn parse_unclosed_brace_error() {
21050 let err = parse_err("fn foo {");
21051 assert!(!err.is_empty());
21052 }
21053
21054 #[test]
21055 fn parse_unclosed_paren_error() {
21056 let err = parse_err("print (1, 2");
21057 assert!(!err.is_empty());
21058 }
21059
21060 #[test]
21061 fn parse_invalid_statement_error() {
21062 let err = parse_err("???");
21063 assert!(!err.is_empty());
21064 }
21065
21066 #[test]
21067 fn merge_expr_list_single() {
21068 let e = Expr {
21069 kind: ExprKind::Integer(1),
21070 line: 1,
21071 };
21072 let merged = merge_expr_list(vec![e.clone()]);
21073 matches!(merged.kind, ExprKind::Integer(1));
21074 }
21075
21076 #[test]
21077 fn merge_expr_list_multiple() {
21078 let e1 = Expr {
21079 kind: ExprKind::Integer(1),
21080 line: 1,
21081 };
21082 let e2 = Expr {
21083 kind: ExprKind::Integer(2),
21084 line: 1,
21085 };
21086 let merged = merge_expr_list(vec![e1, e2]);
21087 matches!(merged.kind, ExprKind::List(_));
21088 }
21089
21090 struct NoInteropGuard {
21099 saved: Option<bool>,
21100 }
21101 impl NoInteropGuard {
21102 fn on() -> Self {
21103 let saved = crate::no_interop_mode_tls();
21104 crate::set_no_interop_mode_tls(Some(true));
21105 Self { saved }
21106 }
21107 }
21108 impl Drop for NoInteropGuard {
21109 fn drop(&mut self) {
21110 crate::set_no_interop_mode_tls(self.saved);
21111 }
21112 }
21113
21114 #[test]
21115 fn no_interop_rejects_sub_keyword() {
21116 let _g = NoInteropGuard::on();
21117 let err = parse_err("sub foo { 1 }");
21118 assert!(
21119 err.contains("--no-interop") && err.contains("fn"),
21120 "sub rejected with fn hint: got {err:?}"
21121 );
21122 }
21123
21124 #[test]
21125 fn no_interop_rejects_say() {
21126 let _g = NoInteropGuard::on();
21127 let err = parse_err("say 1");
21128 assert!(
21129 err.contains("--no-interop") && err.contains("`p`"),
21130 "say rejected with p hint: got {err:?}"
21131 );
21132 }
21133
21134 #[test]
21135 fn no_interop_rejects_scalar_keyword() {
21136 let _g = NoInteropGuard::on();
21137 let err = parse_err("my $n = scalar @x");
21138 assert!(
21139 err.contains("--no-interop") && (err.contains("len") || err.contains("cnt")),
21140 "scalar rejected with len/cnt hint: got {err:?}"
21141 );
21142 }
21143
21144 #[test]
21145 fn no_interop_rejects_reverse() {
21146 let _g = NoInteropGuard::on();
21147 let err = parse_err("my @y = reverse @x");
21148 assert!(
21149 err.contains("--no-interop") && err.contains("rev"),
21150 "reverse rejected with rev hint: got {err:?}"
21151 );
21152 }
21153
21154 #[test]
21159 fn no_interop_accepts_stryke_idioms() {
21160 let _g = NoInteropGuard::on();
21161 parse_ok("fn foo { 1 }");
21164 parse_ok("p 1");
21165 parse_ok("my @x = (1, 2, 3); my $n = len(@x)");
21166 parse_ok("my @x = (1, 2, 3); my @y = rev(@x)");
21167 }
21168
21169 #[test]
21174 fn default_mode_still_accepts_perl5_forms() {
21175 parse_ok("sub foo { 1 }");
21177 parse_ok("say 1");
21178 }
21179
21180 #[test]
21185 fn no_interop_rejects_bare_dollar_a_dollar_b() {
21186 let _g = NoInteropGuard::on();
21187 let err = parse_err("my $x = $a + $b");
21189 assert!(
21190 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
21191 "$a/$b rejected with positional hint: got {err:?}"
21192 );
21193 }
21194
21195 #[test]
21199 fn no_interop_rejects_dollar_a_inside_sort_block() {
21200 let _g = NoInteropGuard::on();
21201 let err = parse_err("my @s = sort { $a <=> $b } (3, 1, 2)");
21202 assert!(
21203 err.contains("--no-interop") || err.contains("$_0") || err.contains("$_1"),
21204 "$a in sort block rejected: got {err:?}"
21205 );
21206 }
21207
21208 #[test]
21211 fn no_interop_accepts_positional_underscore_in_sort_block() {
21212 let _g = NoInteropGuard::on();
21213 parse_ok("my @s = sort { $_0 <=> $_1 } (3, 1, 2)");
21214 }
21215
21216 #[test]
21221 fn colon_range_parses_in_for_loop() {
21222 let _g = NoInteropGuard::on();
21223 parse_ok("for my $i (1:10) { p $i }");
21224 parse_ok("my @r = 0:99");
21225 parse_ok("my @r = -5:5");
21226 }
21227
21228 #[test]
21230 fn postfix_statement_modifiers_parse() {
21231 let _g = NoInteropGuard::on();
21232 parse_ok("p _ for (1, 2, 3)");
21233 parse_ok("p 1 if 1");
21234 parse_ok("p 0 unless 0");
21235 }
21236
21237 #[test]
21240 fn pipe_forward_accepts_both_function_and_block_rhs() {
21241 let _g = NoInteropGuard::on();
21242 parse_ok("my $r = 1:10 |> sum");
21243 parse_ok("my @r = 1:10 |> maps { _ * 2 }");
21244 parse_ok("my @r = 1:10 |> grep { _ % 2 == 0 } |> maps { _ + 1 }");
21245 }
21246
21247 #[test]
21249 fn bareword_positional_underscore_n_parses_in_blocks() {
21250 let _g = NoInteropGuard::on();
21251 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
21253 parse_ok("my @r = maps { _0 * 2 } (1, 2, 3)");
21255 }
21256
21257 #[test]
21261 fn no_interop_accepts_struct_decl() {
21262 let _g = NoInteropGuard::on();
21263 parse_ok("struct Point { x => Int, y => Int }");
21264 }
21265
21266 #[test]
21267 fn no_interop_accepts_enum_decl() {
21268 let _g = NoInteropGuard::on();
21269 parse_ok("enum Color { Red, Green, Blue }");
21270 parse_ok("enum Maybe { Just => Int, Nothing }");
21274 }
21275
21276 #[test]
21277 fn no_interop_accepts_class_decl_with_methods() {
21278 let _g = NoInteropGuard::on();
21279 parse_ok(
21280 "class Rect {\n width: Float\n height: Float\n\n fn area { $self->width * $self->height }\n}",
21281 );
21282 }
21283
21284 #[test]
21285 fn no_interop_accepts_trait_decl() {
21286 let _g = NoInteropGuard::on();
21287 parse_ok("trait Greeter { fn greet; fn loudly { p \"GREET\" } }");
21288 }
21289
21290 #[test]
21295 fn defined_or_assign_compound_operators_parse() {
21296 let _g = NoInteropGuard::on();
21297 parse_ok("my $h = {}; $h->{x} ||= []");
21298 parse_ok("my $v; $v //= 0");
21299 }
21300
21301 #[test]
21304 fn explicit_hashref_literal_parses() {
21305 let _g = NoInteropGuard::on();
21306 parse_ok("my $row = +{ region => \"north\", qty => 10 }");
21307 parse_ok("my @rows = (+{ a => 1 }, +{ a => 2 })");
21308 }
21309
21310 #[test]
21313 fn eval_block_and_dollar_at_parse() {
21314 let _g = NoInteropGuard::on();
21315 parse_ok("my $r = eval { 1 + 2 }; p $@ if $@");
21316 parse_ok("eval { die \"boom\" }; p $@");
21317 }
21318
21319 #[test]
21322 fn try_catch_parses() {
21323 let _g = NoInteropGuard::on();
21324 parse_ok("try { die \"boom\" } catch ($e) { p $e }");
21325 }
21326
21327 #[test]
21330 fn file_test_operators_parse() {
21331 let _g = NoInteropGuard::on();
21332 parse_ok("p 1 if -d \"/tmp\"");
21333 parse_ok("p 2 if -f \"/etc/hosts\"");
21334 parse_ok("p 3 if -e $0");
21335 parse_ok("my $sz = -s \"/etc/hosts\"");
21336 }
21337
21338 #[test]
21342 fn thread_macros_parse() {
21343 let _g = NoInteropGuard::on();
21344 parse_ok("my $r = ~> 5 +1 *2");
21345 parse_ok("my @r = ~> (1,2,3) maps { _ * 2 }");
21346 }
21347
21348 #[test]
21352 fn hash_destructure_sub_signature_parses() {
21353 let _g = NoInteropGuard::on();
21354 parse_ok("fn handle({ name => $name, qty => $qty }) { p \"$name x $qty\" }");
21355 }
21356
21357 #[test]
21360 fn anonymous_fn_parses() {
21361 let _g = NoInteropGuard::on();
21362 parse_ok("my $f = fn { _0 * 2 }");
21363 parse_ok("my @doubled = maps { _0 * 2 } (1, 2, 3)");
21364 }
21365
21366 #[test]
21368 fn ternary_and_chained_ternary_parse() {
21369 let _g = NoInteropGuard::on();
21370 parse_ok("my $r = $x > 0 ? \"pos\" : \"neg\"");
21371 parse_ok("my $r = $x > 0 ? \"pos\" : $x < 0 ? \"neg\" : \"zero\"");
21372 }
21373
21374 #[test]
21377 fn array_slice_with_negative_indices_parses() {
21378 let _g = NoInteropGuard::on();
21379 parse_ok("my @arr = (1,2,3,4,5); my @tail = @arr[-3:-1]");
21380 parse_ok("my @arr = (1,2,3); my @last_two = @arr[-2:]");
21381 }
21382
21383 #[test]
21386 fn state_variable_declaration_parses() {
21387 let _g = NoInteropGuard::on();
21388 parse_ok("fn my_counter { state $n = 0; $n++; $n }");
21391 parse_ok("fn my_memo($k) { state %cache; $cache{$k} //= compute($k) }");
21392 }
21393
21394 #[test]
21397 fn our_declaration_parses() {
21398 let _g = NoInteropGuard::on();
21399 parse_ok("our $VERSION = 1.0");
21400 parse_ok("our @EXPORT = (1, 2, 3)");
21401 }
21402
21403 #[test]
21406 fn regex_binding_operators_parse() {
21407 let _g = NoInteropGuard::on();
21408 parse_ok("p 1 if $s =~ /^\\d+$/");
21409 parse_ok("p 0 unless $s !~ /[A-Z]/");
21410 parse_ok("my @m = $s =~ /(\\w+)/g");
21411 }
21412
21413 #[test]
21417 fn nested_data_structure_literals_parse() {
21418 let _g = NoInteropGuard::on();
21419 parse_ok("my %h = (a => [1, 2, 3], b => [4, 5])");
21420 parse_ok("my @rows = (+{ x => 1 }, +{ x => 2 })");
21421 parse_ok("my %grid = (cells => [+{ row => 1 }, +{ row => 2 }])");
21422 }
21423
21424 #[test]
21427 fn anonymous_fn_with_explicit_params_parses() {
21428 let _g = NoInteropGuard::on();
21429 parse_ok("my $add = fn ($x, $y) { $x + $y }");
21430 parse_ok("my $h = fn ($v, %opts) { p $v; p %opts }");
21431 }
21432
21433 #[test]
21436 fn package_declaration_parses() {
21437 let _g = NoInteropGuard::on();
21438 parse_ok("package Foo; my $x = 1");
21439 parse_ok("package Foo::Bar::Baz; our $VERSION = 0.01");
21440 }
21441
21442 #[test]
21446 fn loop_control_keywords_parse() {
21447 let _g = NoInteropGuard::on();
21448 parse_ok("for my $i (1:10) { next if $i % 2; p $i }");
21449 parse_ok("while (1) { last if $done }");
21450 parse_ok("my $rerun = 0; for (1:5) { if ($rerun) { redo } }");
21451 }
21452
21453 #[test]
21456 fn labelled_loops_parse() {
21457 let _g = NoInteropGuard::on();
21458 parse_ok("OUTER: for my $i (1:10) { last OUTER if $i > 5 }");
21459 parse_ok("OUTER: for my $i (1:3) { INNER: for my $j (1:3) { next OUTER if $j > $i } }");
21460 }
21461
21462 #[test]
21466 fn string_repeat_x_operator_parses() {
21467 let _g = NoInteropGuard::on();
21468 parse_ok("my $sep = \"-\" x 40");
21469 parse_ok("my @zeros = (0) x 100");
21470 parse_ok("my $bar = \"#\" x $count");
21471 }
21472
21473 #[test]
21476 fn chomp_chop_parse() {
21477 let _g = NoInteropGuard::on();
21478 parse_ok("chomp(my $line = <STDIN>)");
21479 parse_ok("my $s = \"hi\\n\"; chomp $s");
21480 parse_ok("my $t = \"hi\"; chop $t");
21481 }
21482
21483 #[test]
21486 fn substitution_operator_parses() {
21487 let _g = NoInteropGuard::on();
21488 parse_ok("my $s = \"abc\"; $s =~ s/b/X/");
21489 parse_ok("my $s = \"AaBb\"; $s =~ s/[a-z]//g");
21490 parse_ok("my $s = \"Hello\"; $s =~ s/(.)/\\1\\1/g");
21491 }
21492
21493 #[test]
21496 fn sprintf_parses_with_format_specs() {
21497 let _g = NoInteropGuard::on();
21498 parse_ok("my $row = sprintf(\"%-10s %5d\", \"foo\", 42)");
21499 parse_ok("p sprintf(\"%.3f ms\", 1.234)");
21500 parse_ok("p sprintf(\"%04x\", 255)");
21501 }
21502
21503 #[test]
21506 fn diamond_stdin_reads_parse() {
21507 let _g = NoInteropGuard::on();
21508 parse_ok("my $line = <STDIN>");
21509 parse_ok("my @lines = <STDIN>");
21510 parse_ok("while (my $line = <STDIN>) { p $line }");
21511 }
21512
21513 #[test]
21516 fn chained_method_calls_parse() {
21517 let _g = NoInteropGuard::on();
21518 parse_ok("$obj->foo->bar");
21519 parse_ok("my $r = $row->{region}");
21520 parse_ok("$obj->set(1)->get");
21521 }
21522
21523 #[test]
21526 fn hash_deref_forms_parse() {
21527 let _g = NoInteropGuard::on();
21528 parse_ok("my $h = +{a=>1}; my %copy = %$h");
21529 parse_ok("my $h = +{a=>1}; my @k = keys %$h");
21530 parse_ok("my $h = +{a=>1}; p $h->{a}");
21531 parse_ok("my $h = +{a=>1, b=>2}; p len(keys %{$h})");
21532 }
21533
21534 #[test]
21537 fn die_unless_assertion_parses() {
21538 let _g = NoInteropGuard::on();
21539 parse_ok("die \"x must be 1\" unless 1 == 1");
21540 parse_ok("die \"empty list\" if len(@x) == 0");
21541 parse_ok("my $x = 1; die \"hi\" unless $x");
21542 }
21543
21544 #[test]
21547 fn qw_literal_parses() {
21548 let _g = NoInteropGuard::on();
21549 parse_ok("my @w = qw(red green blue)");
21550 parse_ok("for my $name (qw(Alice Bob Carol)) { p $name }");
21551 }
21552
21553 #[test]
21556 fn array_slice_with_explicit_indices_parses() {
21557 let _g = NoInteropGuard::on();
21558 parse_ok("my @a = (10, 20, 30, 40); my @s = @a[0, 2]");
21559 parse_ok("my @a = (10, 20, 30); my @s = @a[2, 0, 1]");
21560 }
21561
21562 #[test]
21565 fn hash_slice_with_keys_parses() {
21566 let _g = NoInteropGuard::on();
21567 parse_ok("my %h = (a=>1, b=>2, c=>3); my @v = @h{'a','c'}");
21568 }
21569
21570 #[test]
21574 fn bitwise_operators_parse() {
21575 let _g = NoInteropGuard::on();
21576 parse_ok("my $x = 0xAA; my $y = $x & 0x0F");
21577 parse_ok("my $x = 1; my $y = $x | 2 | 4");
21578 parse_ok("my $x = 0xFF; my $y = $x ^ 0x80");
21579 parse_ok("my $x = 0xAA; my $y = ~$x & 0xff");
21580 parse_ok("my $x = 1; my $y = $x << 4");
21581 parse_ok("my $x = 0xF0; my $y = $x >> 2");
21582 }
21583
21584 #[test]
21587 fn numeric_literal_prefixes_parse() {
21588 let _g = NoInteropGuard::on();
21589 parse_ok("my $hex = 0xCAFEBABE");
21590 parse_ok("my $bin = 0b1101");
21591 parse_ok("my $hex8 = 0xff & 0x0f");
21592 }
21593
21594 #[test]
21597 fn negative_array_index_parses() {
21598 let _g = NoInteropGuard::on();
21599 parse_ok("my @arr = (1, 2, 3); p $arr[-1]");
21602 parse_ok("my @stack = (10, 20, 30); p $stack[-1] if len(@stack) > 0");
21603 }
21604
21605 #[test]
21608 fn loop_control_standalone_parses() {
21609 let _g = NoInteropGuard::on();
21610 parse_ok("while (1) { last }");
21611 parse_ok("for my $i (1:10) { next if $i == 3 }");
21612 parse_ok("fn ret { return 42 }");
21613 }
21614
21615 #[test]
21619 fn shift_pop_on_array_parses() {
21620 let _g = NoInteropGuard::on();
21621 parse_ok("my @a = (1, 2, 3); my $first = shift @a");
21622 parse_ok("my @a = (1, 2, 3); my $last = pop @a");
21623 parse_ok("fn drop_first(@xs) { shift @xs; @xs }");
21624 }
21625
21626 #[test]
21629 fn special_internal_names_parse() {
21630 let _g = NoInteropGuard::on();
21631 parse_ok("p __FILE__");
21632 parse_ok("p __LINE__");
21633 parse_ok("p __PACKAGE__");
21634 parse_ok("p __FILE__ . \":\" . __LINE__");
21635 }
21636
21637 #[test]
21640 fn deeply_nested_ternary_parses() {
21641 let _g = NoInteropGuard::on();
21642 parse_ok("my $g = ($p eq \"a\") ? 1 : ($p eq \"b\") ? 2 : ($p eq \"c\") ? 3 : 4");
21643 }
21644
21645 #[test]
21649 fn array_of_hashref_chained_subscript_parses() {
21650 let _g = NoInteropGuard::on();
21651 parse_ok("my @rows = (+{name=>'a',sc=>1}); p $rows[0]->{name}");
21652 parse_ok("my @rows = (+{n=>10},+{n=>20}); p $rows[-1]->{n}");
21653 }
21654
21655 #[test]
21658 fn nested_for_loops_parse() {
21659 let _g = NoInteropGuard::on();
21660 parse_ok("for my $r (0:5) { for my $c (0:5) { p \"$r,$c\" } }");
21661 }
21662
21663 #[test]
21667 fn dot_assign_string_append_parses() {
21668 let _g = NoInteropGuard::on();
21669 parse_ok("my $s = \"a\"; $s .= \"b\"");
21670 parse_ok("my $out = \"\"; $out .= \"x\" for (1:3)");
21671 }
21672
21673 #[test]
21676 fn unshift_parses() {
21677 let _g = NoInteropGuard::on();
21678 parse_ok("my @path = (3, 4); unshift @path, 2; unshift @path, 1");
21679 parse_ok("my @q; unshift @q, $_ for (1:5)");
21680 }
21681
21682 #[test]
21685 fn defined_or_fallback_parses() {
21686 let _g = NoInteropGuard::on();
21687 parse_ok("my $x; my $y = $x // 0");
21688 parse_ok("my %h = (a=>1); my $v = $h{missing} // -1");
21689 parse_ok("p 1 if defined $foo");
21690 }
21691
21692 #[test]
21695 fn rev_over_range_parses() {
21696 let _g = NoInteropGuard::on();
21697 parse_ok("for my $i (rev 0:9) { p $i }");
21698 parse_ok("my @r = rev (1, 2, 3, 4)");
21699 }
21700
21701 #[test]
21704 fn array_deref_forms_parse() {
21705 let _g = NoInteropGuard::on();
21706 parse_ok("my $r = [1, 2, 3]; my @copy = @$r");
21707 parse_ok("my $r = [1, 2, 3]; my @copy = @{$r}");
21708 parse_ok("my @rs = ([1], [2, 3]); my @flat; push @flat, @$_ for @rs");
21709 }
21710
21711 #[test]
21714 fn hashref_subscript_alt_forms_parse() {
21715 let _g = NoInteropGuard::on();
21716 parse_ok("my $h = +{a=>1}; p $h->{a}");
21717 parse_ok("my $h = +{a=>1}; p ${$h}{a}");
21718 }
21719
21720 #[test]
21723 fn numeric_builtins_parse() {
21724 let _g = NoInteropGuard::on();
21725 parse_ok("my $x = abs(-5)");
21726 parse_ok("my $f = int(3.7)");
21727 parse_ok("my $s = sqrt(2)");
21728 parse_ok("my $n = int($x * 100 + 0.5) / 100");
21729 }
21730
21731 #[test]
21735 fn local_declaration_parses() {
21736 let _g = NoInteropGuard::on();
21737 parse_ok("our $g = 1; (fn { local $g = 99; p $g })->()");
21739 }
21740
21741 #[test]
21745 fn srand_rand_parse() {
21746 let _g = NoInteropGuard::on();
21747 parse_ok("srand(42); my $r = rand(6)");
21748 parse_ok("srand(); my $r = int(rand(100))");
21749 }
21750
21751 #[test]
21754 fn substr_parses() {
21755 let _g = NoInteropGuard::on();
21756 parse_ok("my $s = \"hello\"; p substr($s, 0, 1)");
21757 parse_ok("my $s = \"hello\"; p substr($s, 1)");
21758 parse_ok("my $s = \"hello\"; p substr($s, -2)");
21759 }
21760
21761 #[test]
21764 fn underscore_separators_in_numbers_parse() {
21765 let _g = NoInteropGuard::on();
21766 parse_ok("my $n = 1_000_000");
21767 parse_ok("my $r = 1_000_000 / 365");
21768 parse_ok("my $hex = 0xff_ff");
21769 }
21770
21771 #[test]
21774 fn arrayref_of_arrayref_access_parses() {
21775 let _g = NoInteropGuard::on();
21776 parse_ok("my $grid = [[1, 2], [3, 4]]");
21777 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[0]->[1]");
21778 parse_ok("my $grid = [[1, 2], [3, 4]]; p $grid->[1][0]");
21779 }
21780
21781 #[test]
21784 fn arrow_chain_assignment_parses() {
21785 let _g = NoInteropGuard::on();
21786 parse_ok("my $g = [[0, 0]]; $g->[0]->[1] = 99");
21787 parse_ok("my $g = [[0, 0]]; $g->[0][1] = 99");
21788 }
21789
21790 #[test]
21793 fn tuple_destructure_from_arrayref_parses() {
21794 let _g = NoInteropGuard::on();
21795 parse_ok("my $e = [10, 20]; my ($lhs, $rhs) = @$e");
21796 parse_ok("my $e = [\"x\", 1]; my ($name, $weight) = ($e->[0], $e->[1])");
21797 }
21798
21799 #[test]
21802 fn next_last_unless_postfix_parses() {
21803 let _g = NoInteropGuard::on();
21804 parse_ok("for my $i (1:10) { next unless $i % 2 == 0; p $i }");
21805 parse_ok("while (1) { last unless $live }");
21806 }
21807
21808 #[test]
21811 fn keys_on_braced_hash_deref_parses() {
21812 let _g = NoInteropGuard::on();
21813 parse_ok("my $h = +{a=>1}; my @k = keys %{$h}");
21814 parse_ok("my $h = +{a=>+{b=>1}}; my @k = keys %{$h->{a}}");
21815 }
21816
21817 #[test]
21821 fn namespaced_fn_decl_parses() {
21822 let _g = NoInteropGuard::on();
21823 parse_ok("fn Module::method($x) { $x * 2 }");
21824 parse_ok("fn Foo::Bar::helper { 42 }");
21825 parse_ok("fn Demo::run { p \"running\" }");
21826 }
21827
21828 #[test]
21831 fn namespaced_fn_call_parses() {
21832 let _g = NoInteropGuard::on();
21833 parse_ok("fn Module::add($x, $y) { $x + $y } p Module::add(2, 3)");
21834 parse_ok("fn Foo::Bar::baz { 1 } fn Demo::main { Foo::Bar::baz() + Foo::Bar::baz() }");
21837 }
21838
21839 #[test]
21842 fn index_builtin_parses() {
21843 let _g = NoInteropGuard::on();
21844 parse_ok("my $i = index(\"hello world\", \"world\")");
21845 parse_ok("my $i = index($s, $pat, 0)");
21846 }
21847
21848 #[test]
21852 fn rev_on_array_literal_parses() {
21853 let _g = NoInteropGuard::on();
21854 parse_ok("my @r = rev (1, 2, 3, 4)");
21855 parse_ok("for my $x (rev (\"a\", \"b\", \"c\")) { p $x }");
21856 }
21857
21858 #[test]
21863 fn numeric_and_string_comparison_in_one_expr_parses() {
21864 let _g = NoInteropGuard::on();
21865 parse_ok("my $r = $_0 <=> $_1 || $name cmp $other");
21866 parse_ok("p 1 if $x == $y && $name eq \"foo\"");
21867 }
21868
21869 #[test]
21875 fn bareword_positional_in_sort_reduce_blocks_parses() {
21876 let _g = NoInteropGuard::on();
21877 parse_ok("my @s = sort { _0 <=> _1 } (3, 1, 2)");
21879 parse_ok("my $r = (1, 2, 3) |> reduce { _0 + _1 }");
21881 parse_ok("my $s = (\"a\", \"b\") |> reduce { _0 . _1 }");
21883 }
21884
21885 #[test]
21888 fn bareword_topic_in_maps_grep_parses() {
21889 let _g = NoInteropGuard::on();
21890 parse_ok("my @r = (1, 2, 3) |> maps { _ * 2 }");
21891 parse_ok("my @r = (1, 2, 3, 4) |> grep { _ % 2 == 0 }");
21892 parse_ok("my @r = 1:100 |> pmap { _ ** 3 }");
21893 parse_ok("my @r = 1:100 |> pgrep { _ % 7 == 0 }");
21894 }
21895
21896 #[test]
21900 fn bareword_vs_sigil_in_string_interp_parses() {
21901 let _g = NoInteropGuard::on();
21902 parse_ok("my @r = (5, 10) |> maps { _ + 1 }");
21904 parse_ok("p \"first=$_0 second=$_1\"");
21908 parse_ok("p \"got: $_\"");
21909 }
21910
21911 #[test]
21914 fn bareword_topic_in_postfix_for_parses() {
21915 let _g = NoInteropGuard::on();
21916 parse_ok("my $n = 0; $n += _ for (1, 2, 3, 4)");
21917 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
21918 }
21919
21920 #[test]
21923 fn bareword_topic_as_hash_key_parses() {
21924 let _g = NoInteropGuard::on();
21925 parse_ok("my %h; $h{_}++ for (\"a\", \"b\", \"a\")");
21926 parse_ok("my @arr = (1, 2, 3); my %seen; $seen{$arr[_]}++ for (0, 1, 2)");
21927 }
21928
21929 #[test]
21932 fn bareword_topic_inside_subscript_chain_parses() {
21933 let _g = NoInteropGuard::on();
21934 parse_ok(
21935 "my @c = (\"a\", \"b\", \"c\"); my %seen = (a => 1, b => 2, c => 1); \
21936 my @hits = grep { $seen{$c[_]} == 1 } (0, 1, 2)",
21937 );
21938 }
21939
21940 #[test]
21943 fn ref_builtin_parses() {
21944 let _g = NoInteropGuard::on();
21945 parse_ok("my $x = [1, 2]; p ref($x)");
21946 parse_ok("my $r = +{a => 1}; p 1 if ref($r) eq \"HASH\"");
21947 }
21948
21949 #[test]
21953 fn expression_bodied_recursive_fn_parses() {
21954 let _g = NoInteropGuard::on();
21955 parse_ok("fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1)");
21956 parse_ok("fn N::lcm = _0 * _1 / N::gcd(_0, _1)");
21957 }
21958
21959 #[test]
21962 fn expression_bodied_pipe_reduce_parses() {
21963 let _g = NoInteropGuard::on();
21964 parse_ok(
21965 "fn N::gcd = _1 == 0 ? _0 : N::gcd(_1, _0 % _1); \
21966 fn N::gcd_list = @{_} |> reduce { N::gcd(_0, _1) }",
21967 );
21968 }
21969
21970 #[test]
21973 fn reduce_fold_with_hashref_accumulator_parses() {
21974 let _g = NoInteropGuard::on();
21975 parse_ok(
21976 "my @xs = (3, 2, 3); \
21977 my $st = (+{cur => 0, best => -100}, @xs) |> reduce { \
21978 +{ cur => _1, best => _0->{best} } \
21979 }",
21980 );
21981 }
21982
21983 #[test]
21986 fn array_deref_slice_with_variable_bounds_parses() {
21987 let _g = NoInteropGuard::on();
21988 parse_ok(
21989 "my @a = (10, 20, 30, 40, 50); my $r = \\@a; \
21990 my $lo = 1; my $hi = 3; \
21991 my @s = @$r[$lo:$hi]",
21992 );
21993 }
21994
21995 #[test]
21998 fn min_max_over_array_deref_slice_parses() {
21999 let _g = NoInteropGuard::on();
22000 parse_ok(
22001 "my @a = (1, 3, 2, 5); my $r = \\@a; \
22002 my $lo = 0; my $hi = 2; \
22003 my $m1 = min(@$r[$lo:$hi]); \
22004 my $m2 = max(@$r[$lo:$hi])",
22005 );
22006 }
22007
22008 #[test]
22012 fn flat_maps_with_recursive_call_parses() {
22013 let _g = NoInteropGuard::on();
22014 parse_ok(
22015 "fn Flat::flatten($r) = @$r |> flat_maps { \
22016 ref(_) eq \"ARRAY\" ? Flat::flatten(_) : (_) \
22017 }",
22018 );
22019 }
22020
22021 #[test]
22024 fn nested_map_with_outer_lexical_capture_parses() {
22025 let _g = NoInteropGuard::on();
22026 parse_ok(
22027 "my @lists = ([1,2,3], [10,20,30]); \
22028 my @r = 0:2 |> maps { my $i = _; [map { _->[$i] } @lists] }",
22029 );
22030 }
22031
22032 #[test]
22035 fn array_deref_of_bareword_topic_parses() {
22036 let _g = NoInteropGuard::on();
22037 parse_ok("my @lists = ([1,2,3], [10,20,30]); p min(map { len(@{_}) } @lists)");
22038 }
22039
22040 #[test]
22045 fn thread_macro_accepts_glob_rand_srand_stages() {
22046 parse_ok("my @r = ~> \"/tmp/*\" glob sort");
22047 parse_ok("my $i = ~> 100 rand int");
22048 parse_ok("~> 42 srand");
22049 }
22050
22051 #[test]
22055 fn recursive_fn_with_arrayref_assignment_parses() {
22056 let _g = NoInteropGuard::on();
22057 parse_ok(
22058 "fn UF::find($uf, $x) { \
22059 return $x if $uf->{parent}[$x] == $x; \
22060 $uf->{parent}[$x] = UF::find($uf, $uf->{parent}[$x]); \
22061 $uf->{parent}[$x] \
22062 }",
22063 );
22064 }
22065
22066 #[test]
22069 fn hashref_init_with_range_and_repeat_parses() {
22070 let _g = NoInteropGuard::on();
22071 parse_ok("fn UF::new($n) = +{ parent => [0:$n - 1], rank => [(0) x $n], count => $n }");
22072 }
22073
22074 #[test]
22077 fn postfix_for_arrayref_deref_parses() {
22078 let _g = NoInteropGuard::on();
22079 parse_ok("my @words = (\"a\", \"b\"); my $r = \\@words; my @out; push @out, $_ for @$r");
22080 }
22081
22082 #[test]
22085 fn tuple_swap_destructure_parses() {
22086 let _g = NoInteropGuard::on();
22087 parse_ok("my $ra = 1; my $rb = 2; ($ra, $rb) = ($rb, $ra)");
22088 }
22089
22090 #[test]
22095 fn explicit_2d_array_row_init_parses() {
22096 let _g = NoInteropGuard::on();
22097 parse_ok(
22098 "my @d; my $m = 3; my $n = 4; \
22099 for my $i (0:$m) { $d[$i] = [(0) x ($n + 1)] }",
22100 );
22101 }
22102
22103 #[test]
22106 fn min_with_three_args_parses() {
22107 let _g = NoInteropGuard::on();
22108 parse_ok("my $x = min(1, 2, 3)");
22109 parse_ok("my @d; $d[0][0] = 5; my $r = min($d[0][0] + 1, $d[0][0] + 1, $d[0][0] + 0)");
22110 }
22111
22112 #[test]
22115 fn string_slice_with_len_bound_parses() {
22116 let _g = NoInteropGuard::on();
22117 parse_ok(
22118 "my $w = \"apple\"; my $pre = \"app\"; \
22119 my $ok = len($w) >= len($pre) && $w[0:len($pre) - 1] eq $pre",
22120 );
22121 }
22122
22123 #[test]
22126 fn sort_block_with_arrow_deref_topic_parses() {
22127 let _g = NoInteropGuard::on();
22128 parse_ok(
22129 "my @edges = ([0,1,4], [2,3,1], [1,2,2]); \
22130 my @sorted = sort { _0->[2] <=> _1->[2] } @edges",
22131 );
22132 }
22133
22134 #[test]
22137 fn cstyle_for_with_postdecrement_parses() {
22138 let _g = NoInteropGuard::on();
22139 parse_ok(
22140 "my @arr = (1, 2, 3, 4, 5); \
22141 my $n = len(@arr); \
22142 for (my $i = $n - 1; $i > 0; $i--) { p $arr[$i] }",
22143 );
22144 }
22145
22146 #[test]
22149 fn tuple_swap_arrayref_index_parses() {
22150 let _g = NoInteropGuard::on();
22151 parse_ok(
22152 "my @arr = (1, 2, 3); my $r = \\@arr; my $i = 0; my $j = 2; \
22153 ($r->[$i], $r->[$j]) = ($r->[$j], $r->[$i])",
22154 );
22155 }
22156
22157 #[test]
22160 fn recursive_backtracking_arrayref_mutation_parses() {
22161 let _g = NoInteropGuard::on();
22162 parse_ok(
22163 "fn Q::go($n, $cols, $count_ref) { \
22164 my $r = len(@$cols); \
22165 if ($r == $n) { $$count_ref++; return } \
22166 for my $c (0:$n - 1) { \
22167 push @$cols, $c; \
22168 Q::go($n, $cols, $count_ref); \
22169 pop @$cols \
22170 } \
22171 }",
22172 );
22173 }
22174
22175 #[test]
22178 fn nested_hashref_chain_parses() {
22179 let _g = NoInteropGuard::on();
22180 parse_ok(
22181 "my $c = +{ nodes => +{ a => +{ val => 1, prev => undef, next => \"b\" } } }; \
22182 my $p = $c->{nodes}{a}{prev}; \
22183 my $n = $c->{nodes}{a}{next}",
22184 );
22185 }
22186
22187 #[test]
22190 fn scalar_ref_postincrement_parses() {
22191 let _g = NoInteropGuard::on();
22192 parse_ok("my $n = 0; my $ref = \\$n; $$ref++; p $n");
22193 }
22194
22195 #[test]
22198 fn hash_of_hash_autoviv_increment_parses() {
22199 let _g = NoInteropGuard::on();
22200 parse_ok(
22201 "my %table; \
22202 my $prev = \"the\"; my $next = \"quick\"; \
22203 $table{$prev} //= +{}; \
22204 $table{$prev}{$next}++",
22205 );
22206 }
22207
22208 #[test]
22211 fn cstyle_for_with_literal_bounds_parses() {
22212 let _g = NoInteropGuard::on();
22213 parse_ok("my $sum = 0; for (my $i = 0; $i < 10; $i++) { $sum += $i }");
22214 }
22215
22216 #[test]
22222 fn recursive_expression_body_with_ternary_parses() {
22223 let _g = NoInteropGuard::on();
22224 parse_ok("fn J::s($n, $k) = $n == 1 ? 0 : (J::s($n - 1, $k) + $k) % $n");
22225 }
22226
22227 #[test]
22231 fn namespaced_quote_like_tail_segments_parse() {
22232 let _g = NoInteropGuard::on();
22233 parse_ok("fn Foo::s($x) = $x + 1");
22234 parse_ok("fn Foo::m($x) = $x * 2");
22235 parse_ok("fn Foo::q($x) = $x");
22236 parse_ok("fn Foo::qq($x) = $x");
22237 parse_ok("fn Foo::qx($x) = $x");
22238 parse_ok("fn Foo::qr($x) = $x");
22239 parse_ok("fn Foo::tr($x) = $x");
22240 parse_ok("fn Foo::y($x) = $x");
22241 }
22242
22243 #[test]
22246 fn splice_single_remove_parses() {
22247 let _g = NoInteropGuard::on();
22248 parse_ok("my @circle = 0:5; splice @circle, 2, 1");
22249 }
22250
22251 #[test]
22253 fn atan2_call_parses() {
22254 let _g = NoInteropGuard::on();
22255 parse_ok("fn MC::true_pi = atan2(0, -1)");
22256 parse_ok("my $pi = atan2(0, -1)");
22257 }
22258
22259 #[test]
22262 fn flat_2d_array_indexing_parses() {
22263 let _g = NoInteropGuard::on();
22264 parse_ok(
22265 "my @board = (0) x 81; \
22266 my $r = 3; my $c = 5; \
22267 $board[$r * 9 + $c] = 7; \
22268 my $v = $board[$r * 9 + $c]",
22269 );
22270 }
22271
22272 #[test]
22278 fn par_top_level_prefix_form_parses() {
22279 let _g = NoInteropGuard::on();
22280 parse_ok("my @r = par { _ * 2 } (1, 2, 3, 4)");
22281 parse_ok("par { p _ } @big");
22282 }
22283
22284 #[test]
22288 fn dp_max_step_chained_subscript_parses() {
22289 let _g = NoInteropGuard::on();
22290 parse_ok(
22291 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22292 $d[1][1] = max($d[0][1], $d[1][0]); \
22293 my $r = $d[1][1]",
22294 );
22295 }
22296
22297 #[test]
22299 fn rolling_hash_arithmetic_parses() {
22300 let _g = NoInteropGuard::on();
22301 parse_ok(
22302 "my $h = 0; my $base = 257; my $mod = 1000000007; my $high = 256; \
22303 my $drop = 65; my $add = 90; \
22304 $h = (($h - $drop * $high) * $base + $add) % $mod; \
22305 $h = ($h + $mod) % $mod",
22306 );
22307 }
22308
22309 #[test]
22312 fn triple_nested_2d_via_k_parses() {
22313 let _g = NoInteropGuard::on();
22314 parse_ok(
22315 "my @d; for my $i (0:3) { $d[$i] = [(0) x 4] } \
22316 for my $k (0:3) { for my $i (0:3) { for my $j (0:3) { \
22317 $d[$i][$j] = $d[$i][$k] + $d[$k][$j] \
22318 if $d[$i][$k] + $d[$k][$j] < $d[$i][$j] \
22319 } } }",
22320 );
22321 }
22322
22323 #[test]
22326 fn dp_array_repeat_init_parses() {
22327 let _g = NoInteropGuard::on();
22328 parse_ok("my $amount = 11; my $INF = 1e18; my @dp = ($INF) x ($amount + 1); $dp[0] = 0");
22329 }
22330
22331 #[test]
22333 fn rev_split_join_chain_parses() {
22334 let _g = NoInteropGuard::on();
22335 parse_ok("my $s = \"abc\"; my $r = join(\"\", rev split //, $s)");
22336 }
22337
22338 #[test]
22341 fn cstyle_for_decrement_with_arrayref_swap_parses() {
22342 let _g = NoInteropGuard::on();
22343 parse_ok(
22344 "my @arr = (1, 2, 3); my $r = \\@arr; \
22345 for (my $end = len(@arr) - 1; $end > 0; $end--) { \
22346 ($r->[0], $r->[$end]) = ($r->[$end], $r->[0]) \
22347 }",
22348 );
22349 }
22350
22351 #[test]
22354 fn shift_in_while_loop_parses() {
22355 let _g = NoInteropGuard::on();
22356 parse_ok(
22357 "my @q = (0, 1, 2); my @out; \
22358 while (len(@q) > 0) { my $u = shift @q; push @out, $u }",
22359 );
22360 }
22361
22362 #[test]
22365 fn mod_pow_squaring_loop_parses() {
22366 let _g = NoInteropGuard::on();
22367 parse_ok(
22368 "fn MR::mod_pow($base, $exp, $m) { \
22369 my $result = 1; my $bs = $base % $m; my $e = $exp; \
22370 while ($e > 0) { \
22371 $result = ($result * $bs) % $m if $e % 2 == 1; \
22372 $e = int($e / 2); \
22373 $bs = ($bs * $bs) % $m \
22374 } \
22375 $result \
22376 }",
22377 );
22378 }
22379
22380 #[test]
22383 fn dp_traceback_walk_parses() {
22384 let _g = NoInteropGuard::on();
22385 parse_ok(
22386 "my @xs = (1, 2, 3); my $n = 3; my $target = 4; \
22387 my @dp; for my $i (0:$n) { $dp[$i] = [(0) x ($target + 1)] } \
22388 my @out; my $j = $target; \
22389 for (my $i = $n; $i > 0; $i--) { \
22390 my $v = $xs[$i - 1]; \
22391 if ($j >= $v && $dp[$i - 1][$j - $v] == 1) { \
22392 unshift @out, $v; $j -= $v \
22393 } \
22394 }",
22395 );
22396 }
22397
22398 #[test]
22401 fn binary_search_in_for_loop_parses() {
22402 let _g = NoInteropGuard::on();
22403 parse_ok(
22404 "my @xs = (3, 1, 4, 1, 5); my @tails; \
22405 for my $x (@xs) { \
22406 my $lo = 0; my $hi = len @tails; \
22407 while ($lo < $hi) { \
22408 my $mid = int(($lo + $hi) / 2); \
22409 if ($tails[$mid] < $x) { $lo = $mid + 1 } else { $hi = $mid } \
22410 } \
22411 if ($lo == len @tails) { push @tails, $x } else { $tails[$lo] = $x } \
22412 }",
22413 );
22414 }
22415
22416 #[test]
22419 fn edge_relaxation_destructure_parses() {
22420 let _g = NoInteropGuard::on();
22421 parse_ok(
22422 "my @edges = ([0, 1, 5], [1, 2, -3]); \
22423 my @dist = (0, 1e18, 1e18); my $INF = 1e18; \
22424 for my $e (@edges) { \
22425 my ($u, $w, $cost) = @$e; \
22426 next if $dist[$u] >= $INF; \
22427 $dist[$w] = $dist[$u] + $cost if $dist[$u] + $cost < $dist[$w] \
22428 }",
22429 );
22430 }
22431
22432 #[test]
22435 fn bitwise_and_with_negation_parses() {
22436 let _g = NoInteropGuard::on();
22437 parse_ok("fn Fenwick::lsb($x) = $x & -$x");
22438 parse_ok("my $k = 12; my $lo_bit = $k & -$k");
22439 }
22440
22441 #[test]
22444 fn counting_sort_cumulative_loop_parses() {
22445 let _g = NoInteropGuard::on();
22446 parse_ok(
22447 "my @count = (3, 1, 2, 4); my $running = 0; \
22448 for my $v (0:3) { \
22449 my $c = $count[$v]; \
22450 $count[$v] = $running; \
22451 $running += $c \
22452 }",
22453 );
22454 }
22455
22456 #[test]
22460 fn recursive_tree_walk_with_hashref_parses() {
22461 let _g = NoInteropGuard::on();
22462 parse_ok(
22463 "fn Huff::walk($node, $prefix, $codes) { \
22464 return unless defined $node; \
22465 if (exists $node->{sym}) { \
22466 $codes->{$node->{sym}} = $prefix eq \"\" ? \"0\" : $prefix; \
22467 return \
22468 } \
22469 Huff::walk($node->{left}, $prefix . \"0\", $codes); \
22470 Huff::walk($node->{right}, $prefix . \"1\", $codes) \
22471 }",
22472 );
22473 }
22474
22475 #[test]
22478 fn z_array_window_arithmetic_parses() {
22479 let _g = NoInteropGuard::on();
22480 parse_ok(
22481 "my @c = (\"a\", \"b\", \"a\"); my $n = 3; \
22482 my @z = (0) x $n; $z[0] = $n; \
22483 my $l = 0; my $r = 0; my $i = 1; \
22484 if ($i < $r) { \
22485 my $inside = $r - $i < $z[$i - $l] ? $r - $i : $z[$i - $l]; \
22486 $z[$i] = $inside \
22487 } \
22488 while ($i + $z[$i] < $n && $c[$z[$i]] eq $c[$i + $z[$i]]) { $z[$i]++ }",
22489 );
22490 }
22491
22492 #[test]
22495 fn bfs_with_parent_link_node_parses() {
22496 let _g = NoInteropGuard::on();
22497 parse_ok(
22498 "my @open = (+{ r => 0, c => 0, g => 0, parent => undef }); \
22499 while (len(@open) > 0) { \
22500 my $cur = shift @open; \
22501 for my $d ([-1, 0], [1, 0], [0, -1], [0, 1]) { \
22502 my ($dr, $dc) = @$d; \
22503 push @open, +{ \
22504 r => $cur->{r} + $dr, c => $cur->{c} + $dc, \
22505 g => $cur->{g} + 1, parent => $cur \
22506 } \
22507 } \
22508 last \
22509 }",
22510 );
22511 }
22512
22513 #[test]
22516 fn cross_product_sort_comparator_parses() {
22517 let _g = NoInteropGuard::on();
22518 parse_ok(
22519 "fn Hull::cross($p1, $p2, $p3) = \
22520 ($p2->[0] - $p1->[0]) * ($p3->[1] - $p1->[1]) - \
22521 ($p2->[1] - $p1->[1]) * ($p3->[0] - $p1->[0]); \
22522 my $pivot = [0, 0]; my @pts = ([1, 1], [2, 0]); \
22523 my @sorted = sort { \
22524 my $c = Hull::cross($pivot, _0, _1); \
22525 $c == 0 ? 0 : ($c < 0 ? 1 : -1) \
22526 } @pts",
22527 );
22528 }
22529
22530 #[test]
22533 fn lomuto_partition_loop_parses() {
22534 let _g = NoInteropGuard::on();
22535 parse_ok(
22536 "fn QS::partition($arr, $lo, $hi) { \
22537 my $pivot = $arr->[$hi]; \
22538 my $i = $lo - 1; \
22539 for my $j ($lo:$hi - 1) { \
22540 if ($arr->[$j] <= $pivot) { \
22541 $i++; \
22542 ($arr->[$i], $arr->[$j]) = ($arr->[$j], $arr->[$i]) \
22543 } \
22544 } \
22545 ($arr->[$i + 1], $arr->[$hi]) = ($arr->[$hi], $arr->[$i + 1]); \
22546 $i + 1 \
22547 }",
22548 );
22549 }
22550
22551 #[test]
22554 fn tortoise_hare_diff_loop_parses() {
22555 let _g = NoInteropGuard::on();
22556 parse_ok(
22557 "my $x = 2; my $y = 2; my $n = 35; my $d = 1; \
22558 while ($d == 1) { \
22559 $x = ($x * $x + 1) % $n; \
22560 $y = ($y * $y + 1) % $n; \
22561 $y = ($y * $y + 1) % $n; \
22562 my $diff = $x > $y ? $x - $y : $y - $x; \
22563 $d = $diff \
22564 }",
22565 );
22566 }
22567
22568 #[test]
22571 fn ext_gcd_recursive_destructure_parses() {
22572 let _g = NoInteropGuard::on();
22573 parse_ok(
22574 "fn Mod::ext_gcd($va, $vb) { \
22575 return [$va, 1, 0] if $vb == 0; \
22576 my $r = Mod::ext_gcd($vb, $va % $vb); \
22577 my ($g, $x1, $y1) = @$r; \
22578 [$g, $y1, $x1 - int($va / $vb) * $y1] \
22579 }",
22580 );
22581 }
22582
22583 #[test]
22586 fn convolution_recurrence_dp_parses() {
22587 let _g = NoInteropGuard::on();
22588 parse_ok(
22589 "my @c = (1); my $n = 5; \
22590 for my $i (1:$n) { \
22591 my $sum = 0; \
22592 for my $j (0:$i - 1) { $sum += $c[$j] * $c[$i - 1 - $j] } \
22593 push @c, $sum \
22594 }",
22595 );
22596 }
22597
22598 #[test]
22603 fn tarjan_scc_shared_state_parses() {
22604 let _g = NoInteropGuard::on();
22605 parse_ok(
22606 "fn SCC::strong_connect($s, $v) { \
22607 $s->{idx_of}{$v} = $s->{index}; \
22608 $s->{low_of}{$v} = $s->{index}; \
22609 $s->{index}++; \
22610 push @{$s->{stack}}, $v; \
22611 $s->{on_stack}{$v} = 1; \
22612 for my $w (@{$s->{adj}{$v}}) { \
22613 if (!exists $s->{idx_of}{$w}) { \
22614 SCC::strong_connect($s, $w); \
22615 $s->{low_of}{$v} = $s->{low_of}{$w} if $s->{low_of}{$w} < $s->{low_of}{$v} \
22616 } \
22617 } \
22618 }",
22619 );
22620 }
22621
22622 #[test]
22625 fn heaps_algorithm_recursive_parses() {
22626 let _g = NoInteropGuard::on();
22627 parse_ok(
22628 "fn Perm::heaps_inner($arr, $n, $out) { \
22629 if ($n == 1) { my @snap = @$arr; push @$out, \\@snap; return } \
22630 for my $i (0:$n - 1) { \
22631 Perm::heaps_inner($arr, $n - 1, $out); \
22632 if ($n % 2 == 0) { \
22633 ($arr->[$i], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[$i]) \
22634 } else { \
22635 ($arr->[0], $arr->[$n - 1]) = ($arr->[$n - 1], $arr->[0]) \
22636 } \
22637 } \
22638 }",
22639 );
22640 }
22641
22642 #[test]
22650 fn format_as_hash_key_parses() {
22651 parse_ok("my %opts; $opts{format} = \"csv\"");
22652 parse_ok("my %opts = (format => \"csv\", level => 9)");
22653 parse_ok("my $h = +{ format => \"csv\" }");
22654 parse_ok("my @keys = ($h->{format}, $h->{level})");
22655 }
22656
22657 #[test]
22659 fn format_as_method_call_parses() {
22660 parse_ok("class Foo { val: Str; fn format($self) { \"x\" } } my $f = Foo(val => \"y\"); my $s = $f->format()");
22661 }
22662
22663 #[test]
22665 fn format_as_namespaced_tail_parses() {
22666 parse_ok("fn Foo::format($x) = $x . \"!\"");
22667 parse_ok("fn Foo::format($x) = $x . \"!\"; my $r = Foo::format(\"hi\")");
22668 }
22669
22670 #[test]
22677 fn arrow_hash_compound_assign_parses_all_ops() {
22678 let _g = NoInteropGuard::on();
22679 parse_ok("my $h = +{n=>10}; $h->{n} -= 1");
22680 parse_ok("my $h = +{n=>10}; $h->{n} += 1");
22681 parse_ok("my $h = +{n=>10}; $h->{n} *= 2");
22682 parse_ok("my $h = +{n=>10}; $h->{n} /= 2");
22683 parse_ok("my $h = +{n=>10}; $h->{n} %= 3");
22684 parse_ok("my $h = +{n=>\"x\"}; $h->{n} .= \"y\"");
22685 }
22686
22687 #[test]
22691 fn arrow_hash_compound_assign_value_chains_parses() {
22692 let _g = NoInteropGuard::on();
22693 parse_ok("my $h = +{n=>10}; my $v = $h->{n} -= 1");
22694 parse_ok("my $h = +{n=>10}; my @list = ($h->{n} += 5, $h->{n} += 5)");
22695 parse_ok("my $h = +{n=>10}; my $double = ($h->{n} -= 1) * 2");
22696 }
22697}